Codebase list dillo / 4c2b77c
Imported Upstream version 0.8.6 Axel Beckert 12 years ago
203 changed file(s) with 77250 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0
1 _________________________________________________________________
2
3 Dillo project's team
4 _________________________________________________________________
5
6 Project maintainer:
7 * Jorge Arellano Cid
8
9 Core Developers:
10 * Jorge Arellano Cid
11 * Sebastian Geerken
12 * Luca Rota
13
14 Steady developers:
15 * Livio Baldini
16 * Eric Gaudet
17 * Jörgen Viksell
18
19 Contributors:
20 * Lars Clausen
21 * Geoff Lane
22 * Sammy Mannaert
23 * James McCollough
24
25 Patches:
26 * Philip Blundell
27 * Francis Daly
28 * Sam Dennis
29 * Melvin Hadasht
30 * Madis Janson
31 * Andrew McPherson
32 * Sean 'Shaleh' Perry
33 * Marcos Ramírez
34 * Adam Sampson
35 * Andreas Schweitzer
36 * Dominic Wong
37 _________________________________________________________________
38
39 Web site logo:
40 * Eric Gaudet
41 * Jarrod Henry
42
43 Gzilla author:
44 * Raph Levien
45 _________________________________________________________________
46
47
48
49
50 -------------------
51 Up to gzilla-0.1.7:
52 -------------------
53
54 Raph Levien <raph@acm.org> is the author of gzilla.
55
56 Christoph Thompson <obituary@freshmeat.net> and Tomas O"gren <stric@ing.umu.se>
57 added pixmaps for the buttons and the code encessary to make them work.
58 They also reorganized the source tree and gave general advice and tips.
59 (Tomas will be pleased to find that my personal bookmarks file is no longer
60 hard-coded into Gzilla. :))
61
62 Ian Main <slow@intergate.bc.ca> did the bookmarks.
63
64 Thanks to Otto Hammersmith, David Mosberger-Tang, and Peter Mattis for
65 patches.
66
67 Contributions are always welcome!
68
0
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12 freedom to share and change it. By contrast, the GNU General Public
13 License is intended to guarantee your freedom to share and change free
14 software--to make sure the software is free for all its users. This
15 General Public License applies to most of the Free Software
16 Foundation's software and to any other program whose authors commit to
17 using it. (Some other Free Software Foundation software is covered by
18 the GNU Library General Public License instead.) You can apply it to
19 your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22 price. Our General Public Licenses are designed to make sure that you
23 have the freedom to distribute copies of free software (and charge for
24 this service if you wish), that you receive source code or can get it
25 if you want it, that you can change the software or use pieces of it
26 in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29 anyone to deny you these rights or to ask you to surrender the rights.
30 These restrictions translate to certain responsibilities for you if you
31 distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34 gratis or for a fee, you must give the recipients all the rights that
35 you have. You must make sure that they, too, receive or can get the
36 source code. And you must show them these terms so they know their
37 rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40 (2) offer you this license which gives you legal permission to copy,
41 distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44 that everyone understands that there is no warranty for this free
45 software. If the software is modified by someone else and passed on, we
46 want its recipients to know that what they have is not the original, so
47 that any problems introduced by others will not reflect on the original
48 authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51 patents. We wish to avoid the danger that redistributors of a free
52 program will individually obtain patent licenses, in effect making the
53 program proprietary. To prevent this, we have made it clear that any
54 patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57 modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63 a notice placed by the copyright holder saying it may be distributed
64 under the terms of this General Public License. The "Program", below,
65 refers to any such program or work, and a "work based on the Program"
66 means either the Program or any derivative work under copyright law:
67 that is to say, a work containing the Program or a portion of it,
68 either verbatim or with modifications and/or translated into another
69 language. (Hereinafter, translation is included without limitation in
70 the term "modification".) Each licensee is addressed as "you".
71
72 Activities other than copying, distribution and modification are not
73 covered by this License; they are outside its scope. The act of
74 running the Program is not restricted, and the output from the Program
75 is covered only if its contents constitute a work based on the
76 Program (independent of having been made by running the Program).
77 Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80 source code as you receive it, in any medium, provided that you
81 conspicuously and appropriately publish on each copy an appropriate
82 copyright notice and disclaimer of warranty; keep intact all the
83 notices that refer to this License and to the absence of any warranty;
84 and give any other recipients of the Program a copy of this License
85 along with the Program.
86
87 You may charge a fee for the physical act of transferring a copy, and
88 you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91 of it, thus forming a work based on the Program, and copy and
92 distribute such modifications or work under the terms of Section 1
93 above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114 These requirements apply to the modified work as a whole. If
115 identifiable sections of that work are not derived from the Program,
116 and can be reasonably considered independent and separate works in
117 themselves, then this License, and its terms, do not apply to those
118 sections when you distribute them as separate works. But when you
119 distribute the same sections as part of a whole which is a work based
120 on the Program, the distribution of the whole must be on the terms of
121 this License, whose permissions for other licensees extend to the
122 entire whole, and thus to each and every part regardless of who wrote it.
123
124 Thus, it is not the intent of this section to claim rights or contest
125 your rights to work written entirely by you; rather, the intent is to
126 exercise the right to control the distribution of derivative or
127 collective works based on the Program.
128
129 In addition, mere aggregation of another work not based on the Program
130 with the Program (or with a work based on the Program) on a volume of
131 a storage or distribution medium does not bring the other work under
132 the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135 under Section 2) in object code or executable form under the terms of
136 Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155 The source code for a work means the preferred form of the work for
156 making modifications to it. For an executable work, complete source
157 code means all the source code for all modules it contains, plus any
158 associated interface definition files, plus the scripts used to
159 control compilation and installation of the executable. However, as a
160 special exception, the source code distributed need not include
161 anything that is normally distributed (in either source or binary
162 form) with the major components (compiler, kernel, and so on) of the
163 operating system on which the executable runs, unless that component
164 itself accompanies the executable.
165
166 If distribution of executable or object code is made by offering
167 access to copy from a designated place, then offering equivalent
168 access to copy the source code from the same place counts as
169 distribution of the source code, even though third parties are not
170 compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173 except as expressly provided under this License. Any attempt
174 otherwise to copy, modify, sublicense or distribute the Program is
175 void, and will automatically terminate your rights under this License.
176 However, parties who have received copies, or rights, from you under
177 this License will not have their licenses terminated so long as such
178 parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181 signed it. However, nothing else grants you permission to modify or
182 distribute the Program or its derivative works. These actions are
183 prohibited by law if you do not accept this License. Therefore, by
184 modifying or distributing the Program (or any work based on the
185 Program), you indicate your acceptance of this License to do so, and
186 all its terms and conditions for copying, distributing or modifying
187 the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190 Program), the recipient automatically receives a license from the
191 original licensor to copy, distribute or modify the Program subject to
192 these terms and conditions. You may not impose any further
193 restrictions on the recipients' exercise of the rights granted herein.
194 You are not responsible for enforcing compliance by third parties to
195 this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198 infringement or for any other reason (not limited to patent issues),
199 conditions are imposed on you (whether by court order, agreement or
200 otherwise) that contradict the conditions of this License, they do not
201 excuse you from the conditions of this License. If you cannot
202 distribute so as to satisfy simultaneously your obligations under this
203 License and any other pertinent obligations, then as a consequence you
204 may not distribute the Program at all. For example, if a patent
205 license would not permit royalty-free redistribution of the Program by
206 all those who receive copies directly or indirectly through you, then
207 the only way you could satisfy both it and this License would be to
208 refrain entirely from distribution of the Program.
209
210 If any portion of this section is held invalid or unenforceable under
211 any particular circumstance, the balance of the section is intended to
212 apply and the section as a whole is intended to apply in other
213 circumstances.
214
215 It is not the purpose of this section to induce you to infringe any
216 patents or other property right claims or to contest validity of any
217 such claims; this section has the sole purpose of protecting the
218 integrity of the free software distribution system, which is
219 implemented by public license practices. Many people have made
220 generous contributions to the wide range of software distributed
221 through that system in reliance on consistent application of that
222 system; it is up to the author/donor to decide if he or she is willing
223 to distribute software through any other system and a licensee cannot
224 impose that choice.
225
226 This section is intended to make thoroughly clear what is believed to
227 be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230 certain countries either by patents or by copyrighted interfaces, the
231 original copyright holder who places the Program under this License
232 may add an explicit geographical distribution limitation excluding
233 those countries, so that distribution is permitted only in or among
234 countries not thus excluded. In such case, this License incorporates
235 the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238 of the General Public License from time to time. Such new versions will
239 be similar in spirit to the present version, but may differ in detail to
240 address new problems or concerns.
241
242 Each version is given a distinguishing version number. If the Program
243 specifies a version number of this License which applies to it and "any
244 later version", you have the option of following the terms and conditions
245 either of that version or of any later version published by the Free
246 Software Foundation. If the Program does not specify a version number of
247 this License, you may choose any version ever published by the Free Software
248 Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251 programs whose distribution conditions are different, write to the author
252 to ask for permission. For software which is copyrighted by the Free
253 Software Foundation, write to the Free Software Foundation; we sometimes
254 make exceptions for this. Our decision will be guided by the two goals
255 of preserving the free status of all derivatives of our free software and
256 of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285 possible use to the public, the best way to achieve this is to make it
286 free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289 to attach them to the start of each source file to most effectively
290 convey the exclusion of warranty; and each file should have at least
291 the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) 19yy <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
310
311 Also add information on how to contact you by electronic and paper mail.
312
313 If the program is interactive, make it output a short notice like this
314 when it starts in an interactive mode:
315
316 Gnomovision version 69, Copyright (C) 19yy name of author
317 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 This is free software, and you are welcome to redistribute it
319 under certain conditions; type `show c' for details.
320
321 The hypothetical commands `show w' and `show c' should show the appropriate
322 parts of the General Public License. Of course, the commands you use may
323 be called something other than `show w' and `show c'; they could even be
324 mouse-clicks or menu items--whatever suits your program.
325
326 You should also get your employer (if you work as a programmer) or your
327 school, if any, to sign a "copyright disclaimer" for the program, if
328 necessary. Here is a sample; alter the names:
329
330 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
333 <signature of Ty Coon>, 1 April 1989
334 Ty Coon, President of Vice
335
336 This General Public License does not permit incorporating your program into
337 proprietary programs. If your program is a subroutine library, you may
338 consider it more useful to permit linking proprietary applications with the
339 library. If this is what you want to do, use the GNU Library General
340 Public License instead of this License.
341
0 =============================================================================
1 Dillo project
2 =============================================================================
3
4
5 dillo-0.8.6 [Apr 26, 2006]
6
7 - * Designed and implemented a dpi protocol library (libDpip.a in /dpip).
8 * Added a couple of new dpip commands.
9 * Fixed and uniformed the escaping of values inside dpip tags.
10 * Ported the bookmarks, download, file, https, ftp and hello plugins,
11 plus the dpid daemon and the rest of the source tree to use it.
12 * Improved the dpi buffer reception to handle split buffers (This was
13 required for handling arbitrary data streams with dpip).
14 * Fixed a bug in Cache_entry_remove_raw.
15 * Added a couple of "const" and C++ wrappers to dpiutil's API.
16 * Fixed a serious bug with the FTP plugin that led to two downloads of the
17 same file when left-clicking a non-viewable file.
18 * Added MIME/type detection to the FTP plugin, and removed popen().
19 * Set the dpi daemon (dpid) not to exit when the downloads dpi is running.
20 * Improved the accuracy of the illegal-character error reporting for URLs.
21 * Now save dialog replaces %20 and ' ' with '_' in the Filename suggestion.
22 * Made URL ADT automatically count and strip illegal characters from URLs.
23 * Added dpi/downloads.cc (Default FLTK2-based GUI for downloads dpi).
24 * Added "./configure --disable-dlgui" to build without FLTK2-GUI downloads.
25 * Fixed dpip's tag syntax and its parsing to accept any value string.
26 * Added DOCTYPE parsing (for better bug-meter error messages).
27 * Added a DOCTYPE type declaration tag to dpi-generated HTML.
28 * Fixed bookmarks dpi to escape ' in URLs and &<>"' in titles (BUG#655).
29 * Added a check for malicious image sizes in IMG tags.
30 * Made the parser aware of buggy pages with multiple BODY and HTML elements.
31 * Fixed a bug in MIME content/type detection.
32 * Check HTTP Content-Type against real data (a security procedure).
33 Patches: Jorge Arellano
34 - * Added a datauri dpi to handle "data:" URIs (RFC-2397).
35 Patch: Jorge Arellano, Ben Wiley Sittler
36 - * Moved the cookies management into a dpi server: cookies.dpi.
37 * Removed the restriction of only one dillo with cookies enabled!
38 * Fixed a bug with cookies for sites with self-signed certificate.
39 * Updated the cookies documentation.
40 * Made the downloads plugin dillo-cookie aware.
41 * Ported the cookies dpi to libDpip.a.
42 * Merged the new dpip code into the source tree.
43 Patches: Diego Sáenz, Jorge Arellano
44 - * Added "./configure --disable-threaded-dns" (for some non reentrant BSDs).
45 Patch: Jorge Arellano, Francis Daly
46 - * Fixed a bug with roman literals divisible by 10 (BUG#700).
47 * Fixed a bug with long alphabetically ordered lists (BUG#704).
48 Patch: Glyn Kennington
49 - * Fixed a file descriptor leak in the dpi protocol library.
50 * Fixed a subtle segfault bug with malformed URLs in cookies.c.
51 Patch: Francis Daly
52
53
54 dillo-0.8.5 [Jun 15, 2005]
55
56 - * Set "file:" to work as URI for current directory.
57 Patch: Diego Sáenz
58 - * Added a "small" dillorc option for panel size (medium without labels).
59 Patch: Eugeniy, Jorge Arellano
60 - * Fixed the shell escaping code in the ftp plugin.
61 * Added some checks for sane values in html.c.
62 * Added URL filtering to the ftp and downloads dpis to avoid SMTP hacks.
63 * Fixed the file dpi to react to the DpiBye command.
64 Patches: Jorge Arellano
65
66
67 dillo-0.8.4 [Jan 11, 2005]
68
69 - * Fixed a possible attack (program abortion) by malicious web pages, which
70 contain huge values for <table> attributes "colspan" and "rowspan".
71 * Changed anchors, they are now tested to be unique, and removed properly,
72 when a widget tree is changed (e.g. another page is visited). Also added
73 HTML warnings.
74 Patches: Sebastian Geerken
75 - * Fixed two minor memory leaks (IO's Buf1Start & html's SPCBuf).
76 * Fixed handling of XML's "/>" tag-closing (e.g. <script ... />). BUG#514
77 * Removed obsolete code from IO/file.c.
78 * Added a few missing EINTR handlers in dpi.c.
79 * Orthogonalized the generic parser:
80 - Fixes memory leaks and widget state when recovering from bad HTML.
81 - Improves error detection and validation (needed by XHTML).
82 - Makes DOC tree generation possible (needed by CSS).
83 - Cleaner design of handling routines for bad HTML.
84 - Orthodox treatment of double optional elements (HTML, HEAD, BODY).
85 - Lots of minor code cleanups.
86 * Switched the dpi file server's design to pthreads (fixes a critical race).
87 * Avoided a crash when indexed GIF images lack a color map (BUG#647).
88 * Fixed a bug when the remote HTTP server sends no Content-Type and
89 the TCP packetizing splits the header from data (BUG#650).
90 * Returned the parser to the old whitespace "collapsing" mode
91 (this can be changed with the SGML_SPCDEL define in html.c).
92 * Fixed a memory leak for DwStyle (there was one leak per page).
93 Patches: Jorge Arellano
94 - * Fixed a large memory leak of thread specific resources. --Very important
95 Patch: Jorge Arellano, Livio Baldini
96 - * Removed warnings for pointer arithmetic and strict prototypes all
97 around the code (now it works under LP64 architectures).
98 * Made miscelaneous cleanups for LP64 architectures.
99 Patches: Jorge Arellano, Dieter Baron
100 - * Changed dpid's umask to 0077.
101 Patch: Jorge Arellano, Richard Zidlicky
102 - * Switched to g_vsnprintf (instead of vsnprintf).
103 Patch: Frank Wille
104 - * Updated a bit the README file.
105 Patch: Dieter Baron
106 - * Made a grammatical and typographical review of the whole documentation
107 in doc/. Also added some clarifications.
108 * Fixed a libpng detection problem (e.g., on CYGWIN). BUG#651
109 Patches: Roberto Sanchez
110 - * Fixed "id" and "name" attributes parsing logic.
111 * Improved the parsing algorithm for character entities. BUG#605
112 Patches: Matthias Franz
113 - * Fixed a security bug with uncertain data and a_Interface_msg().
114 CAN-2005-0012.
115 Patch: Tavis Ormandy
116
117
118 dillo-0.8.3 [Oct 27, 2004]
119
120 - * Added a missing error handler for unreachable host in http.c.
121 Patch: Dennis Schneider, Jorge Arellano
122 - * Added fragment (aka anchor) decoding before it is set by Dw.
123 Patch: Matthias Franz, Jorge Arellano
124 - * Fixed dpid to work even when a dpi directory is empty.
125 Patch: George Georgiev, Jorge Arellano
126 - * Made the search dialog's URL go encoded in the query.
127 Patch: Matthias Franz
128 - * Fixed the width of sized text entry widgets within FORMS.
129 Patch: Thorben Thuermer
130 -(*)Made a library-based https dpi prototype, with certificate authentication!
131 * Separated the code in dpi/ so the common base now lies in dpiutil.c.
132 Patches: Garrett Kajmowicz
133 - * Added SSL library detection code to configure.in.
134 Patch: Garrett Kajmowicz, Thomas-Martin Seck
135 - * Fixed the wrong image-URL after cancelling a link-image popup (BUG#580).
136 * Improved the transfer speed for the bookmarks dpi when using 2.6.x linux.
137 * Fixed the downloads dpi to work when there're "'" characters in the URL.
138 * Fixed " and ' characters stuffing in capi and interface for dpip commands.
139 (*)Added a "dialog" command to the dpi protocol (dpip). It allows any dpi to
140 make GUI choice-questions trough Dillo by using simple dpi tags.
141 * Merged some dialog-generating code in interface.c and fixed a bug with
142 the FORM repost dialog.
143 * Designed and implemented a unified API for handling data streams inside
144 dillo plugins. Servers and filters can use it.
145 * Converted the bookmarks, ftp, file, hello and the https prototype dpis
146 to the new dpiutil API.
147 * Replaced the old 'force_visited_color' dillorc option with the new
148 'contrast_visited_color' (using a renewed color-choosing algorithm).
149 * Added some 'undefined ASCII' to latin1 character conversions.
150 * Added the "w3c_plus_heuristics" option to dillorc.
151 * Removed a segfault bug on oversized images (rare case).
152 * Removed a CPU-hog on 302 redirections with cookies.
153 * Made HTTP's 302 redirections non-cacheable (incomplete).
154 * Implemented a new scheme for detecting redirection loops.
155 * Fixed cookies to accept four legacy old-date formats for "Expires".
156 Patches: Jorge Arellano
157 - * Introduced a light-weight heuristic algorithm over the W3C parsing
158 scheme (allows for slightly better rendering: w3c_plus_heuristics=YES).
159 Patch: Rubén Fernández
160 - * Moved the internal support for "file:" URIs into a dpi (server plugin).
161 * Added TABLE-based rendering of directory listings to the new file dpi.
162 Patches: Jorge Arellano, Jörgen Viksell
163 - * Removed DwWidget::realize and DwWidget::unrealize.
164 * Made all signals expect events to abstract methods.
165 * Renamed a_Dw_widget_{size_request|get_extremes|size_allocate|set_*} to
166 p_*, they should not be used outside of Dw.
167 Patches: Sebastian Geerken
168 - * Fixed the meta refresh warning to not switch from IN_HEAD to IN_BODY.
169 Patch: Björn Brill
170
171
172 dillo-0.8.2 [Jul 06, 2004]
173
174 - * Made PgUp/PgDn scroll by a full page, instead of a half (BUG#418).
175 * Added new Gtk+ widgets GtkExtButton, GtkExtMenu, and GtkExtMenuItem.
176 - Used GtkExtButton to enhance the button 3 menu of the forward button,
177 backward button and bug meter buutton.
178 - GtkExtMenu and GtkExtMenuItem are used to make handling button 2
179 in the history menus cleaner.
180 * Made bug meter button react on high-level "clicked" signal, instead of
181 "button-press-event".
182 * New widget GtkMenuTitle, used for nicer titles in menus.
183 Patches: Sebastian Geerken
184 - * Added a small handler for javascript links (BUG#546).
185 * Made the parser ignore white space immediately after an open tag, and
186 immediately before a close tag.
187 * Fixed handling of redirection answers with unviewable MIME type (BUG#563).
188 * Fixed the history-stack handling after redirection chains.
189 * Fixed the character escaping logic in directory listings (BUG#564).
190 * Added a small hack to view UTF-8 encoded quotation marks.
191 * Added a "start_page" option to dillorc (to override the splash screen).
192 * Tuned the buffering scheme for local directory listings (more speed).
193 * Set some initial socket-buffering for dpis (in dpid).
194 * Disallowed the "about:" method on non-root URLs (BUG#573).
195 * Made the rendered area keep its focus after a form submition.
196 * Fixed some include files in src/IO/.
197 Patches: Jorge Arellano
198 - * Added hints (icons and tooltip text) to buttons featuring a right-click
199 context menu.
200 * Now you can copy & paste an URL into the "Clear URL" button.
201 Patch: Jorge Arellano, Sebastian Geerken
202 - * Made the save and open file dialogs remember the last directory (BUG#211).
203 Patch: Diego Sáenz
204
205
206 dillo-0.8.1 [May 14, 2004]
207
208 - * Fixed dirent.h includes inside dpid.
209 Patch: Joseph Myers
210 - * Fixed a slippery bug with certain interlaced gif images (BUG#500).
211 Patch: Andreas Mueller
212 - * Fixed libpng-1.2.4 detection in configure.in.
213 Patch: Rubén Helguera
214 - * Added proxy authentication support through the "http_proxyuser" option
215 in dillorc (the password is asked at run time).
216 Patch: Ivan Daniluk, Jorge Arellano, Francis Daly
217 - * Moved tooltips to DwStyle, tooltip event handling to DwPage, and applied
218 this also to the TITLE attribute of <a> and <abbr>.
219 Patch: Jörgen Viksell, Sebastian Geerken
220 - * Fixed a bug related to spaces after anchors and breaks.
221 Patch: Sebastian Geerken
222 - * Removed two "type punning" gcc warnings (dw_gtk_viewport.c).
223 * Added some missing "static" qualifiers.
224 * Improved a_Strbuf_chars() so no list reversion is required.
225 * Removed an unused data list (dns.c), and redundant code (selection.c).
226 * Switch one realloc() call to g_realloc(), to match g_free() (dpi.c).
227 * Removed unnecessary NULL-checks and NULL initializations.
228 * Added Html_get_attr_wdef(), it lets providing a default return value.
229 Patches: Jörgen Viksell
230 - * Fixed configure.in so pthreads are only linked where needed.
231 Patch: Jörgen Viksell, Jorge Arellano
232 - * Modified a_Misc_stuff_chars for simplicity and removed a memory leak.
233 * Made the dpi framework send the HTTP query to the https dpi
234 (this allows for an SSL-lib dpi and for easier session caching).
235 * Cleaned up the int2void and void2int casts in CCC parameters.
236 * Added container|inline model information to the HTML element table, and
237 made the bug-meter and the parser aware of it. This both improves bug
238 detection and rendering.
239 * Fixed newly detected HTML bugs in bookmarks dpi and file.c.
240 * Fixed opening files with a ':' character in its name (again).
241 * Added binaryconst.h (allows for binary constants in C).
242 * Fixed The ladder effect with lists (BUG#534).
243 * Made the bug-meter detect tags lacking a closing '>' (BUG#532).
244 * Made the bug-meter detect excluded inline elements from <PRE>.
245 * Eliminated a segfault source with tricky <input> tags (BUG#552).
246 * Fixed <address> to render as a block element (BUG#527).
247 * Added a content test for "name" and "id" attribute values (BUG#536).
248 * Fixed the URL resolver handling of the "//" sequence in <path> (BUG#535).
249 * Added "show_extra_warnings" and removed "use_old_parser" (dillorc).
250 * Added minor support for the deprecated <MENU> element.
251 * Eliminated a race condition that produced segfaults when a dpi transfer
252 was cancelled before the contents were sent (a very rare case).
253 * Added a test for socklen_t in configure.in.
254 * Fixed the downloads dpi to handle both new savenames and target directory.
255 * GdkRgb: fixed handling of not usable system default visual and colormap.
256 * Made dillo recognize unhandled MIME types, and offer a download dialog!
257 Patches: Jorge Arellano
258
259
260 dillo-0.8.0 [Feb 08, 2004]
261
262 - * Added a right-mouse-button popup for images!
263 Patch: Frank de Lange, Eric Gaudet, Jorge Arellano
264 - * Made main document window grab focus on startup, fullwindow,
265 and after open url (BUG#330)
266 * Set Ctrl-U to focus the location entry,
267 Ctrl-R to reload, and Ctrl-H to hide controls.
268 Patches: Johan Hovold, Jorge Arellano, Stephan Goetter
269 - * Added a missing handler for broken-connection condition.
270 Patch: Jorge Arellano, Phil Pennock
271 - * Introduced a new way of handling dillo plugins! Now the
272 communications and managing is done by a daemon: dpid.
273 This comes with a lot of advantages described in Dpid.txt.
274 Patch: Programming: Ferdi Franceschini; Design: Jorge Arellano
275 - * Wrote documentation for dpid (Dpid.txt).
276 * Removed a memory leak in Get_line().
277 Patches: Jorge Arellano, Ferdi Franceschini
278 - * Developed a plugin for downloads. It uses wget and can handle several
279 connections at the same time.
280 * Developed stress tests for both dpid and the downloads dpi.
281 Patches: Ferdi Franceschini
282 - * Adapted dpi.c to manage plugins through dpid.
283 * Improved the incoming dpi-stream processing to accept images from a dpi.
284 * Added/updated lots of dpi-related comments.
285 * Updated the dpi1 spec.
286 * Removed the forced end-to-end reload that was set upon dpis. Now,
287 dpi-generated pages can be cached.
288 * Made dillo able to handle multiple plugins (still lacks a dynamic API)
289 * Wrote bare bones plugins for handling: FTP and HTTPS.
290 * Wrote an example plugin: HELLO --kind of "Hello world".
291 * Made all the bindings to make it work (fully commented).
292 * Added code for automatical and non-blocking dpid start!
293 * Added an extensible secondary URL convenience for popup menus.
294 * Attached the image popup to the link menu (when the link is an image).
295 * Removed a memory leak in the selection code (commands.c).
296 * Cleaned up memory handling when reusing the GioChannel for IPv6.
297 * Removed a race-condition-polling-CPU-hog bug in IO! (hairy... ;)
298 * Adapted the generic parser to make HTML error detection, providing
299 the line number and a hint (expected tag) in the error message!
300 * Added a bug-meter button that shows the count of detected HTML errors
301 (left click shows the errors, right click offers a validation menu).
302 * Added information about optional, required and forbidden end tags.
303 * Modified the parser's handling of closing tags to account for elements
304 with an optional close tag, and for more accurate diagnosis messages.
305 * Added 'use_old_parser' option to dillorc (boolean).
306 * Fixed the handling of HEAD and BODY elements to account for their
307 double optional condition (both open and close tags are optional).
308 * Added the MSG() macro to msg.h and replaced g_print() with it.
309 * Added the "show_msg" dillorc option to disable MSG().
310 * Increased the number of warning messages reported by gcc.
311 * Solved a lot of signed/unsigned problems.
312 * Made the necessary cleanups/bug-removals for the new warning level.
313 * Connected the dpi stream to the cache using the CCC!
314 * Fixed the cache API by introducing the new call a_Capi_get_buf().
315 * Fixed a race condition and a multiple request problem.
316 * Cleaned up the code for the progressbar widgets.
317 * Standarized unix domain sockets with AF_LOCAL for portability.
318 * Minor cleanups for a smooth compile on older systems (libc5).
319 * Fixed the handling of P element for the HTML nesting checks.
320 * Set Ctrl-B for bookmarks shortcut (instead of Alt-B).
321 Patches: Jorge Arellano
322 - * Enhanced the speed of the actual selection of text.
323 * Added command line option --debug-rendering.
324 * Added "button_sensitive" attribute to DwWidget, which is needed to
325 make <BUTTON>'s accessable at all. (They were inaccessable since the
326 introduction of text selection!)
327 * Changed behaviour of DwButton, see NOTE at beginning of dw_button.c.
328 * Added "collapsing margins" to DwPage.
329 * Added CSS "list-style-type" and "display" equivalents to DwStyle, changed
330 definition of "font", replaced "nowrap" by "white-space", and renamed
331 "link" to "x_link".
332 * DwBullet now uses DwStyle for the bullet type, made necessary changes
333 in HTML parser.
334 * Changed DwStyleLength, now only pixel values and percentages are
335 supported. (For CSS, anything else will be done elsewhere.)
336 * Added word backgrounds to DwPage (not yet used.)
337 * Added the possibility to clip widget drawings (new function
338 p_Dw_widget_will_clip).
339 * Made images showing the ALT text as long as no image data has been
340 retrieved.
341 * Cleaned up event handling and related code: "link_*" signals now return
342 gboolean, and DwWidget events are signals.
343 * Moved DwRectangle and related to dw.c.
344 * Rewrote idle drawing, fixed BUG#202.
345 * Removed p_Dw_widget_queue_clear*.
346 * Added --enable-rtfl option to configure.
347 * Fixed a bug in findtext (wrong highlighting).
348 * Many changes in scrolling: added x coordinate (except for anchors), and
349 DW_[VH]POS_INTO_VIEW position. Added x coordinate also to DilloUrl.
350 Patches: Sebastian Geerken
351 - * Fixed bug in DwImage::link_clicked signal.
352 Patch: Stephan Goetter, Frank de Lange (simultaneously and independent :-)
353 - * Fixed memory leak in Html_tag_open_isindex.
354 * Added numerical keypad cursor keys navigation.
355 * Changed return values of Dw event methods from gint to gboolean.
356 * Cleaned up debug message generation by using glib wrappers.
357 * Replaced DwStyle::SubSup by new DwStyleVAlignType values, and
358 DwStyle::uline and DwPage::strike by new DwStyle::text_decorations.
359 * Added new convenience macros DW_WIDGET_HEIGHT, DW_WIDGET_CONTENT_HEIGHT,
360 and DW_WIDGET_CONTENT_WIDTH.
361 * Added configure options to disable either: png, jpeg or gif.
362 * Fixed configure.in for proper library linking for dpis and dpid.
363 * Improved libpng detection.
364 Patches: Jörgen Viksell
365 - * Fixed wrong handling of coordinates in ISMAP and USEMAP images.
366 * Added a hand-shaped cursor to input controls of type image.
367 * Fixed a off-by-one memory leak in Dw(Ext)Iterator.
368 * Fixed NULL result handling of p_Dw_widget_text_iterator() in DwBullet,
369 DwHRuler and DwImage.
370 * Made dpid/Makefile.am aware of $(DESTDIR).
371 * Fixed wrong return value of a_Findtext_search for widget == NULL.
372 Patches: Frank de Lange
373 - * Fixed a bug in Dw cursor code.
374 Patch: Frank de Lange, Sebastian Geerken
375 - * Corrected marshal functions for DwWidget signals.
376 Patch: Anders Gavare, Sebastian Geerken
377 - * Added support for anchors using the "id" attribute (BUG#495).
378 * Defined dillo's version-string in one place only: configure.in.
379 Patch: Francis Daly
380 - * Removed a segfault source with corrupted MIME types in HTTP (BUG#501).
381 * Made SPAM-safe URLs aware of image buttons (BUG#508).
382 Patch: Francis Daly, Jorge Arellano
383 - * Added a web search dialog (with toolbar icon, shortcut: Ctrl-S).
384 The search engine can be set in dillorc (defaults to google).
385 Patch: Johan Hovold, Jorge Arellano
386 - * Fixed a problem with libpng options detection (configure.in).
387 Patch: Rubén Fernández
388 - * Added "pthreads" (with an "s") detection to configure.in.
389 Patch: Andreas Schweitzer
390 - * Added the "-geometry" switch to the CLI.
391 Patch: Jorge Arellano, Jan Dittmer
392
393
394 dillo-0.7.3 [Aug 03, 2003]
395
396 - * Some more selection goodies:
397 - Redesign of the selection state model, now the selection is preserved
398 as long as possible.
399 - Highlighted text is now drawn inverse (new DwWidget::bg_color).
400 - Selection of images, list bullets and hrulers (as text), with a common
401 text iterator for the respective widgets.
402 * Borders may now be drawn inverse (needed for selection).
403 * Improved the speed when selecting large areas. (BUG#450)
404 * Fixed a bug in DwPage extremes.
405 * Fixed a wrong implementation of incremental resizing for DwPage.
406 (Affected functions: Dw_page_rewrap and a_Dw_page_add_widget)
407 * Fixed a bug in a_Dw_widget_size_allocate.
408 * Made jumping to anchors faster (removes CPU hog).
409 * Fixed a bug in Dw_page_get_extremes().
410 * Made (invalid) <li>'s without <ol> or <ul> defined, and independent of
411 each other.
412 * Fixed rendering of <frameset>.
413 Patches: Sebastian Geerken
414 - * Made a new set of toolbar icons!
415 Patch: John Grantham (http://www.grantham.de/)
416 - * Added support for the hspace and vspace attributes of the IMG tag.
417 * Made only left button activate the image input type (BUG#367,#451).
418 * Fixed SELECT with "multiple" but without "size" (BUG#469).
419 Patches: Jörgen Viksell
420 - * Added titles to bookmark server's html pages.
421 Patch: Kelson Vibber
422 - * Made IFRAME to be handled like FRAME (shows link).
423 Patch: Nikita Borodikhin, Jorge Arellano
424 - * Fixed a bug in 'a_Misc_stristr' that permeated findtext. (BUG#447)
425 Patch: Jorge Arellano, "squirrelblue"
426 - * Finished handling of single and double quotes inside dpi tags.
427 * Fixed a bug for named-entities' character codes greater than 255.
428 * Introduced a small UCS to Latin1 converter to help rendering.
429 * Added a check for Unix98's "socklen_t" (BUG#466).
430 * Added the missing EINTR handlers in IO.c and file.c.
431 * Fixed the problem of adding garbage anchors.
432 Patches: Jorge Arellano
433
434
435 dillo-0.7.2 [Apr 30, 2003]
436
437 - * Implemented text selection! (aka: Copy&Paste) (BUG#59)
438 Patch: Sebastian Geerken, Eric Gaudet
439 - * Fixed IPv6 support when the unthreaded server is used.
440 Patch: Damien Couderc, Jorge Arellano
441 - * Fixed the IPv6 socket connection code for *BSD.
442 Patch: Daniel Hartmeier, Jorge Arellano
443 - * Made the URL_SpamSafe flag be inherited by the BASE element.
444 Patch: Melvin Hadasht
445 - * Switched configure.in to use AC_CANONICAL_SYSTEM instead of 'uname'.
446 Patch: Patrice Mandin
447 - * Added "image/x-png" to MIME types (obsolete, but should be recognized).
448 Patch: Paolo P.
449 - * Fixed the code that handled the installation of "dillorc".
450 Patch: Andreas Schweitzer
451 - * Fixed a lot of glitches in configure.in: notably libpng and libjpeg
452 detection, enabling and disabling. (BUG#: 386, 407, 392, 349)!
453 Patches: Andreas Schweitzer
454 - * Fixed two leaks in Dw(Ext)Iterator.
455 Patches: Jörgen Viksell
456 - * Repaired some minor misbehaviours in the cookie-strings parser.
457 Patches: Jörgen Viksell, Jorge Arellano
458 - * Enabled entities parsing in HTML-given hidden and password values.
459 Patch: Jorge Arellano, Francis Daly
460 - * Implemented character stuffing in dpi (Fix bookmarks with quotes) BUG#434.
461 * Added a HTML warning message for META outside HEAD.
462 * Removed a segfault source when the server doesn't send content/type info.
463 * Added file type detection for filenames without extension.
464 * Removed the warnings detected with gcc 3.2.2.
465 * Fixed the VERBATIM parsing mode and replaced the SCRIPT mode with it.
466 * Fixed the problem with CR handling in TEXTAREA (BUG#318).
467 * Fixed initial value parsing within TEXTAREA tags (BUG#400).
468 * Fixed loading files with spaces in the name (command line) BUG#437.
469 Patches: Jorge Arellano
470
471
472 dillo-0.7.1.2 [Mar 11, 2003]
473
474 - * Fixed a bug in the bugfix that used uninitialized memory contents
475 causing all kind of undesirable side effects.
476 Patch: Andreas Schweitzer
477
478
479 dillo-0.7.1 [Mar 10, 2003] -- bugfix release
480
481 - * Fixed the setting of the FD_CLOEXEC flag.
482 Patch: Raphael Barabas
483 - * Added an automatic file-locking alternative for systems lacking flock().
484 Patch: Yang Guilong
485 - * Fixed a memory leak with pixmaps.
486 Patch: Keith Packard
487 - * Fixed the link color switch with scroll wheel mouses (BUG#348)
488 Patch: Stephen Lewis
489 - * Made the bookmarks server keep a backup file: bm.txt.bak.
490 * Fixed not loading the bookmarks file (and erasing the bookmarks).
491 * Added some missing EINTR handlers.
492 * Added a handler for unresponsive dpi sockets!
493 * Restricted dpi-requests to dpi-generated pages only.
494 * Used -1 instead of WAIT_ANY (some systems don't have it). (BUG#413)
495 * Fixed a source bug when G_DNS_THREADED is not defined. (BUG#421)
496 * Switched sprintf to g_snprintf which is safer.
497 Patches: Jorge Arellano
498
499
500 dillo-0.7.0 [Feb 17, 2003]
501
502 - * Added IPv6 support! [./configure --enable-ipv6] (BUG#351)
503 Patch: Philip Blundell
504 - * Fixing char escaping/encoding problems with file URIs (BUG#321)
505 * Fixing buffer overflow sources in file.c.
506 * Switched the image tooltip from "alt" to "title" attribute.
507 Patch: Francis Daly, Jorge Arellano
508 - * Added code so that tooltips stay within the screen.
509 Patch: Pekka Lampila, Sebastian Geerken
510 - * Fixed a problem occurring when scrolling with the "b" key.
511 Patch: Livio Baldini
512 - * Fixed a memory leak in DwAlignedPage.
513 Patch: Jörgen Viksell, Sebastian Geerken
514 - * Moved stuff into remove_cookie() and add_cookie() functions.
515 * Made cookies sort once in add_cookie().
516 * Removed some unneeded casts and calls in cookies.
517 * Repairing some minor misbehaviours in Cookies_parse_string().
518 Patches: Jörgen Viksell, Jorge Arellano, Madis Janson
519 - * Fixed a bug in Dw_widget_mouse_event.
520 Patch: Jörgen Viksell
521 - * Fixed a bug in DwPage ("height" argument).
522 Patch: Pekka Lampila
523 - * Removed a segfault source in http.c
524 Patch: Madis Janson
525 - * Removed space around tables.
526 * Implemented the <button> tag! (BUG#276)
527 * Added iterators (DwItetator, DwExtItetator, DwWordItetator).
528 - Rewrote findtext, added highlighting and "case sensitive" option.
529 - Improved findtext dialog placement too!
530 * Implemented "ALIGN = {LEFT|RIGHT|CENTER}" for <table>, and
531 "ALIGN = {LEFT|RIGHT|CENTER|JUSTIFY}" for <tr>.
532 * Implemented character alignment, applied it on ALIGN=CHAR and CHAR for
533 <tr>, <td> and <th>.
534 - New widget DwTableCell.
535 - Some smaller changes in DwAlignedPage and DwPage (virtual word_wrap,
536 ignore_line1_offset_sometimes).
537 * Implemented vertical alignment of table cells.
538 - Changed behavior of Dw_page_size_allocate.
539 - Applied it on "VALIGN={TOP|BOTTOM|MIDDLE|BASELINE}" for <tr>, <td> and
540 <th>.
541 - Fixed splash screen.
542 * Set the height of <BR>'s in non-empty lines to zero.
543 * Moved some code from html.c to a_Dw_page_change_link_color.
544 * Made bullets size depending on the font size.
545 * Fixed too wide widgets in lists (e.g. nested lists).
546 Patches: Sebastian Geerken
547 - * Added support for <input type=image...> (BUG#313)
548 Patch: Madis Janson, Sebastian Geerken, Jorge Arellano
549 - * Made a better EAGAIN handler, and enabled FreeIOVec operation in IOWrite.
550 Patch: Jorge Arellano, Livio Baldini
551 - * Fixed include directives for config.h
552 Patch: Jorge Arellano, Claude Marinier
553 - * Made lots of minor cleanups.
554 Patches: Lex Hider, Jorge Arellano, Rudmer van Dijk
555 - * Added a simple command line interface, and enabled some options (BUG#372).
556 * Added full-window option in command line and dillorc.
557 * Added an option to set offline URLs from CLI.
558 * Made dillo embeddable into other GTK applications.
559 Patches: Jorge Arellano, Melvin Hadasht
560 - * Made drafts for dillo plugins protocol (dpi1)
561 Work: Jorge Arellano, Eric Gaudet
562 - * Avoided a file lock when cookiesrc disables cookies (BUG#358).
563 * Fixed scroll-jumping between widgets when pressing Up&Dn arrows.
564 * Added a tiny warning/handler for meta refresh.
565 * Concomitant Control Chain (CCC):
566 - Extended the theory to allow bidirectional message passing.
567 - Renewed the API.
568 - Improved the debugging code.
569 - Redesigned the old CCCs, and made a new one for plugins (dpi).
570 - Reimplemented dillo's core with the new chains.
571 * Input/Output engine (IO):
572 - Extended the functionallity with a threaded operation that
573 allows buffered writes of small chunks on the same FD.
574 - Created a new IO API, and adapted dillo to it.
575 * Used the new CCC and IO to implement dillo plugins! (dpi).
576 * Implemented the internal support for a bookmarks dpi.
577 * Wrote a dpi-program for bookmarks.
578 * Created capi.c, a meta module for cache.c.
579 * Restructured Html_write so custom HTML can be inserted.
580 * Set BackSpace and Shift+BackSpace to work as Back/Forward buttons.
581 * Set the escape key as a dialog closing shortcut.
582 * Removed a segfault in find text with a string of spaces (BUG#393)
583 * Added wrappers/whitespace filtering for pasted/typed/CLI URLs. (RFC-2396)
584 * Added an HTML warning message for illegal characters inside URLs.
585 * Made dpi communication go through unix domain sockets.
586 * Enabled dillo to launch the bookmarks plugin!
587 * Made some cleanups in IO/.
588 Patches: Jorge Arellano
589
590
591 dillo-0.6.6 [May 30, 2002]
592
593 - * Added a few canonical casts to fix some obvious 64bit issues.
594 Patch: pvalchev
595 - * Fixed a bug with cookies path parsing.
596 * Fixed persistent-cookies obliteration (BUG#312, BUG#314)
597 * Set max 20 persistent cookies for each domain.
598 Patches: Jörgen Viksell
599 - * Switched flock to lockf.
600 Patch: Andreas Schweitzer
601 - * Made a little bugfix in doc/Makefile.am.
602 Patch: Grigory Bakunov
603 - * Removed the < 256 hostname length restraint from http queries.
604 * Made a date-parser that copes with three HTTP date-syntaxes (BUG#335)
605 * Made the HTML parser a bit more robust with bad HTML (BUG#325, BUG#326)
606 Patches: Jorge Arellano
607
608
609 dillo-0.6.5 [Apr 26, 2002]
610
611 - * Improved a bit table rendering speed.
612 Patch: Mark Schreiber
613 - * Extended Dw crossing events.
614 Patch: Sebastian Geerken
615 - * Added code to autoresize the "View source" window (BUG#198).
616 Patch: Andreas Schweitzer
617 - * Improved *BSD detection code at './configure' time.
618 Patch: Andreas Schweitzer, Jorge Arellano
619 - * Added a (pthread_t) cast in dns.c
620 * Fixed a problem with #fragment hash-lookup (in anchors_table).
621 * Added code to install/test usr/local/etc/dillorc (BUG#287)
622 * Added control-character filtering for pasted/typed URLs.
623 * Replaced the old cache list with a hash table!
624 Patches: Livio Baldini
625 - * Fixed a momentous memory leak in png decoding.
626 * Fixed a segfault source in GIF colormap handling.
627 Patch: Livio Baldini, Jorge Arellano
628 - * Added fontname selection to dillorc.
629 Patch: Arvind Narayanan
630 - * Removed a segfault source under G_IO_ERR conditions in IO.c.
631 Patch: Madis Janson
632 - * Removed a wild deallocation chance in klist.c
633 Patch: Pekka Lampila
634 - * Fixed saving of pages that result from POST.
635 Patch: Nikita Borodikhin
636 - * Fixed a tiny bug with dillorc parsing on certain locales (BUG#301)
637 Patch: Lars Clausen, Jorge Arellano
638 - * Added support for cookies! RFC-2965 (BUG#82)
639 Patch: Jörgen Viksell, Lars Clausen, Jorge Arellano
640 - * Added code to detect redirect-loops (BUG#260)
641 Patch: Jorge Arellano, Chet Murthy
642 - * Added support for missing Content-Type in HTTP headers (BUG#216)
643 * Added support for a bare '>' inside attribute values (BUG#306)
644 Patch: Jorge Arellano, Andreas Schweitzer
645 - * Allowed enter to submit forms when there's a single text entry.
646 * Added 'generate_submit' and 'enterpress_forces_submit' to dillorc.
647 Patch: Jorge Arellano, Mark Schreiber.
648 - * Added support for rendering adjacent <BR>, Tabs in <PRE>, and linebreak
649 handling (BUG#244, BUG#179, BUG#291).
650 Patch: Jorge Arellano, Mark Schreiber, Sebastian Geerken.
651 - * Switched a_List_* methods to three parameters (and wiped BUG#286)
652 * Fixed two little bugs within url.c (BUG#294)
653 * Created an API for nav_stack usage (a handy cleanup).
654 * Set the attribute parser to trim leading and trailing white space.
655 * Fixed a problem with NULL requests to the dns (BUG#296).
656 * Added Tru64(tm) detection code at './configure' time.
657 * Fixed the parser to skip <style> and <script> contents (BUG#316).
658 * Bound the space key to PgDn, and 'b' | 'B' to PgUp.
659 * Allowed 'query' and 'fragment' in POST submitions (BUG#284).
660 * Changed the url module API (the URL_* macros), and updated the calling
661 modules, removing several potential bugs at the same time --toilsome.
662 Patches: Jorge Arellano
663
664
665 dillo-0.6.4 [Jan 29, 2002]
666
667 - * Implemented remembering of page-scrolling-position! (BUG#219)
668 Patch: Jorge Arellano, Livio Baldini
669 - * Moved jpeg's include directory from CFLAGS to CPPFLAGS in configure.in
670 Patch: John L. Utz, Lionel Ulmer
671 - * Made a standarization cleanup to every *.h
672 * Cleaned some casts to use the GPOINTER_TO_INT and GINT_TO_POINTER macros.
673 * Added the 'static' qualifier to some module-internal variables.
674 * Added the 'static' qualifier to module-internal functions!
675 Patches: Jörgen Viksell
676 - * New widget DwAlignedPage for alignment of vertical arrays.
677 - New widget DwListItem for nicer list items (based on some extensions
678 of DwPage) BUG#271.
679 * Implemented text alignments (except CHAR).
680 - Extension of DwStyle and DwPage.
681 - Applied it on "ALIGN = {LEFT|RIGHT|CENTER}" for <hr>, and
682 "ALIGN = {LEFT|RIGHT|CENTER|JUSTIFY}" for <p>, <hN>, <div>, <td> and
683 <th>. Implemented <center> --BUGs #215, #189.
684 * Small change in DwPageWord (space_style), fixes problems with spaces and
685 underlining (BUG#278).
686 Patches: Sebastian Geerken
687 - * Added 'force_visited_colors' to dillorc. It asserts a different color
688 on visited links, regardless of the author's setting.
689 Patch: Jorge Arellano, Sebastian Geerken
690 - * Updated and improved several #include directives inside *.c
691 * Added history.c for linear history and scroll-position tracking.
692 Now the navigation-stack references linear history and nav-expect
693 holds a DilloUrl (history.c provides an API).
694 * Fixed a rare data-integrity race-condition with popups (BUG#225)
695 * Made small icons a bit narrower.
696 * Fixed a problem with image-maps handling code (BUG#277)
697 * Added support for several domains in dillorc's 'no_proxy' variable.
698 * Fixed a small boundary-bug in named-colors parsing.
699 * Implemented IOs validity-test with klist (avoids a rare segfault source).
700 Patches: Jorge Arellano
701
702
703 dillo-0.6.3 [Dec 23, 2001]
704
705 - * Removed a_Dw_widget_set_usize.
706 * Removed *_indent in DwStyle, this is now done by nested widgets.
707 * List items are now single widgets, this fixes bug #78.
708 * Extended queue_resize and related code, removed fast resizing.
709 - Applied these changes on DwPage (many changes!).
710 * Changes in requisition of DwPage.
711 * Added a nice indenter to the pagemarks! ("Jump to..." menu).
712 Patches: Sebastian Geerken
713 - * Reworked the dicache to use a hash table and use image versions.
714 * Wiped some dicache glitches, and added a dillorc option turn it off!
715 (reducing memory usage significatively).
716 Patches: Livio Baldini
717 - * Added support for OSes that use a slightly different 'struct sockaddr'.
718 Patch: Johan Danielsson
719 - * Removed a cache leak when reloading (BUG#257).
720 Patch: Livio Baldini, Jorge Arellano
721 - * Added full-screen mode! (left double-click toggles it).
722 Patch: Jorge Arellano, Sebastian Geerken
723 - * Extended interface customization options in dillorc (a must for iPAQ).
724 Patch: Jorge Arellano, Sam Engström
725 - * Rewrote the whole tag-parsing code with a new scheme (single pass FSM!)
726 (BUG#190, BUG#197, BUG#207, BUG#228, BUG#239) --Big work here.
727 Patch: Jorge Arellano, Jörgen Viksell
728 - * Set form encoding to escape everything but alphanumeric and -_.* (BUG#236)
729 * Rewrote Html_tag_open_input.
730 * Extended BACK and FWD key shortcuts to: {ALT | MOD*} + {, | .} :-)
731 * Fixed URI fragment parsing (BUG#247).
732 * Centered FindText and OpenUrl dialog windows.
733 * Structured dillorc (now it's more readable! ;)
734 * Added a dillorc option to force transient_dialogs.
735 * Fixed a subtle bug with HTTP end-to-end reload (BUG#234).
736 * Fixed form submition when action has <query> or <fragment> (BUG#255)
737 * Added fast URL resolving methods! (96% rfc2396 compliant by now) BUG#256
738 * Switched form-urlencoded CR to be sent as CR LF pair (BUG#266).
739 * Fixed leaving open FDs when the socket connection fails (BUG#268).
740 Patches: Jorge Arellano
741
742
743 dillo-0.6.2 [Oct 17, 2001]
744
745 - * Added code to parse away <?...> tags (BUG#203).
746 Patch: Sebastian Geerken
747 - * Made an explicit ISO8859-1 requirement in font loading (BUG#193).
748 Patch: Karsten M. Self
749 - * Added a temporary handler for frames! (lynx/w3m like).
750 Patch: Livio Baldini
751 - * Added gtk_set_locale to dillo's init sequence (BUG#173).
752 Patch: Eric Gaudet, Martynas Jocius
753 - * Added support for <big> and <small> tags (BUG#221).
754 Patch: Livio Baldini, Jorge Arellano
755 - * Added back and forward history popup menus! (BUG#227)
756 Patch: Jorge Arellano, Eric Gaudet, Olaf Dietsche
757 - * Removed anchors from to-proxy queries (also added some checks, BUG#210).
758 * Removed a leak in url.c
759 * Fixed a bug with command-line HTML files that reference images (BUG#217).
760 * Improved status-bar messages a bit, modified toolbar pixmaps and
761 reduced the number of a_Url_dup calls.
762 * Set Ctrl-Q to close window and Alt-Q to quit.
763 * Devised an abstract model for parsing, wrote it into HtmlParser.txt and
764 made dillo compliant with it!
765 * Fixed CR/LF entities parsing inside <PRE> (BUG#188)
766 * Added an error message for unsupported protocols (BUG#226)
767 * Removed some warnings detected with different gcc versions.
768 Patches: Jorge Arellano
769
770
771 dillo-0.6.1 [Sep 13, 2001]
772
773 - * Changed calculation of shaded colors.
774 * Eliminated redundant code when drawing background colors.
775 * Fixed a bug in DwStyle drawing functions.
776 * Fixed a bug in Dw_page_calc_widget_size.
777 * Some changes for <hr> (also BUG#168).
778 * Added <tr> backgrounds.
779 Patches: Sebastian Geerken
780 - * Added support for hexadecimal character references, as &#xA1; (BUG#183)
781 Patch: Liam Quinn
782 - * Replaced atoi(3) calls with strtol(3).
783 * Made path comparison case sensitive in a_Url_cmp.
784 Patches: Livio Baldini
785 - * Added a tiny handler for <DIV>
786 Patch: Robert J. Thomson
787 - * Fixed a segfault source in color parsing, and extended it a bit.
788 Patch: Scott Cooper, Jorge Arellano
789 - * Removed a leak with the DilloImage structure (when image is not found).
790 * Fixed (and made faster) Url_str_resolve_relative (BUG#194)
791 Patch: Jorge Arellano, Livio Baldini
792 - * Added parsing support for %HexHex escape sequences in file URIs
793 Patch: Jorge Arellano, Livio Baldini, Agustín Ferrín :)
794 - * Implemented Ctrl-W (close window) (BUG#87)
795 Patch: Jorge Arellano, Martynas Jocius
796 - * Fixed a segfault when dillo cannot access ~/.dillo for some reason.
797 Patch: Jorge Arellano, Amit Vainsencher
798 - * Fixed the segfault from untrue Content-Length in HTTP header (BUG#187)
799 * Fixed closing an active browser window from the window manager (bug#91)
800 * Eliminated anchors from HTTP queries (BUG#195)
801 * Fixed the repeated reload segfault (BUG#69)
802 * Updated some docs in doc/ dir.
803 * Added a keyed-list ADT (klist.[ch])
804 * Removed a segfault source in dns.c.
805 * Massive changes in Cache module: redesigned the external and internal API,
806 implemented new methods, changed several algorithms, removed transitory
807 and obsoleted code, removed a segfault source and improved CCC operations.
808 * Changes in Http module: extended error handling, improved abort sequences,
809 and added code that's aware of race conditions (based on klist ADT).
810 * Uniformed CCC start operation in IO, http and cache modules.
811 Patches: Jorge Arellano
812
813
814 dillo-0.6.0 [July 31, 2001]
815
816 - * Fixed a bunch of memory leaks!
817 * Fixed links on pages with only one line, tuned text-entries size and
818 fixed the HTTP header problem (BUG#180)
819 Patches: Jörgen Viksell
820 - * Improved dialogs handling: find_text, view_source, open_url, open_file,
821 save_link and save_page (also removed a leak here).
822 Patches: Jorge Arellano, Jörgen Viksell
823 - * Modified GtkDwScrolledWindow and GtkDwViewport, now scrollbars work much
824 better. This also fixes of the wrong viewport length (BUG#137).
825 * Implemented tables! (incomplete)
826 - Changes in Dw: DwTable and DwWidget::get_extremes.
827 - html.c: extended DilloHtmlState, added code for table parsing, moved
828 some attributes from DwPage into the HTML linkblock.
829 * Restructured code for image maps (works now with tables).
830 * Removed "alt" attribute from <a> tag (no standard).
831 * Fixed a bug in a_Url_dup.
832 * Extended Dw events: leave_notify_event is now called for more widgets.
833 * Extended DwPage and DwImage signal "link_entered".
834 * Extended DwStyle by CSS-style boxes, background colors and border_spacing:
835 - Implemented borders around image links (BUG#169).
836 * Fixed the wrong PNG background? (BUG#172)
837 * Corrected handling of styles by the html parser.
838 * Added alternative, "fast" resizing method.
839 * Added a simple possibility to scroll long option menus (BUG#158)
840 * Added backing pixmap, this prevents flickering (BUG#174).
841 * Changes and extensions in handling lenghts, see doc/DwStyle.txt.
842 * Added option "limit_text_width".
843 Patches: Sebastian Geerken
844 - * Added nowrap attribute to DwStyle, and applied it to <pre> (BUG#134),
845 <td> and <th>.
846 Patch: Jörgen Viksell, Sebastian Geerken
847 - * Added a_List_resize to list.h methods.
848 * Added debug.h to standarize debugging messages.
849 Patches: Sebastian Geerken, Jorge Arellano
850 - * Added file selection while saving pages or links.
851 Patch: Livio Baldini
852 - * Added a few 'const', a missing header and some strength reductions.
853 Patch: Aaron Lehmann
854 - * Made dillo to also check '/etc/dillorc' for options.
855 Patch: Eduardo Marcel Maçan, Jorge Arellano
856 - * Made a help page, and linked it to 'about:help' (BUG#72)
857 Patch: Jorge Arellano, Kristian A. Rink
858 - * Added an "alt" camp to DilloUrl
859 * Fixed the linkblock memory-leak (BUG#163)
860 * Fixed local file loading from the command line (BUG#164)
861 * Fixed server-side image maps support (BUG#165)
862 * Added code for accel-keys on toolbar buttons
863 * Fixed the segfault with unconnected servers (BUG#170)
864 * Fixed the open HTTP-sockets problem (BUG#171)
865 * Reimplemented the low-level file descriptor handling with GIOChannels
866 (and dillo became even faster!)
867 * Made reload-button to request an end-to-end reload (BUG#64)
868 * Fixed the multiple-POST problem, and added a confirmation dialog (BUG#175)
869 * Finished fixing the repeated link-click problem (BUG#74)
870 * Misc: rewrote the 'about:splash' method, tuned DwPage for minimal
871 memory usage, improved a_Color_parse and Html_read_coords, cleaned-up
872 popup-menus and linkblock initialization, eliminated a lock-source in
873 Html_parse_length.
874 * Added DEBUG_HTML_MSG macro for invalid HTML messages.
875 * Fixed the nav-stack (and multiple #anchors) problem (BUG#177)
876 * Added code to avoid segfaults with unhandled MIME types.
877 * Fixed dns.c from solving the same address on different channels (BUG#178)
878 * Improved error handling and extended the CCC scope! (mainly HTTP).
879 * Fixed a Dw-leak that was affecting: hr, bullets, images, tables (&pages)!
880 * Made several cleanups and added/fixed comments in various places.
881 * Reimplemented find-text with a faster algorithm and extended semantics!!
882 * Fixed some oddities with our autoconf stuff.
883 Patches: Jorge Arellano
884
885
886 dillo-0.5.1 [May 30, 2001]
887
888 - * Designed a new URL handling scheme, and integrated it throughout the code!
889 Patch: Livio Baldini, Jorge Arellano
890 - * Removed a significative memory leak in dw_page.
891 * Added support for EAGAIN in IO.c
892 Patches: Livio Baldini
893 - * Removed 6 memory leaks! (of varying significance)
894 Patches: Jörgen Viksell
895 - * Fixed a bug in DwPage (BUG#162, crash when clicking on links).
896 * Removed a_Dw_gtk_viewport_queue_anchor and related code (becomes obsolete
897 with the new URL handling scheme).
898 * Speed-optimized key moving in GtkDwScrolledFrame (no more blocking).
899 * Fixed two memory leaks, in Dw_style_color_remove, and
900 Dw_style_font_remove.
901 Patches: Sebastian Geerken
902 - * Implemented the splash screen with "about:" (No more splash-file saving!)
903 * Set all pthreads to run in detached state.
904 * Reworked dillo's interface so now there're six options; available by
905 changing 'panel_size' and the new 'small_icons' in dillorc.
906 * Removed a minor leak in dns.c and a wild-deallocation source.
907 Patches: Jorge Arellano
908
909
910 dillo-0.5.0 [May 10, 2001]
911
912 - * Implemented <IMG> ALT as tooltip.
913 * Fixed bug #135 (incorrect update of statusbar when leaving "ismap" img).
914 Patches: Livio Baldini, Sebastian Geerken
915 - * Completed image scaling (BUG#75).
916 Patch: Sebastian Geerken, Jorge Arellano
917 - * Fixed proxy support (BUG#150).
918 Patch: Livio Baldini
919 - * Fixed two bugs in the Dw event handling.
920 * Fixed bugs in DwEmbedGtk and GtkDwViewport: idle functions are now
921 removed properly.
922 * Fixed bug in DwEmbedGtk (added call of parent_class->destroy).
923 * Moved DwPageAttr into a new submodule (DwStyle).
924 - Applied DwStyle to DwBullet (BUG#146).
925 - Implemented immediate changing of link color provisionally (BUG#152).
926 * Fixed positioning of headers (half of BUG#118).
927 * Fixed rendering of <b><i> and <i><b> (BUG#145).
928 * Fixed unrecognized dillorc text_color setting (BUG#151).
929 Patches: Sebastian Geerken
930 - * Changed word/line structure of DwPage
931 * Improved FORM sending (standar name/value submits) and processing;
932 added READONLY, SIZE, MAXLENGTH attributes, type=BUTTON and some cleanups
933 * Fixed VERBATIM parsing mode (BUG#130)
934 * Fixed a bug in calculating the page width (BUG#136)
935 Patches: Jörgen Viksell
936 - * Added a dillorc option to set the location entry within the menu bar.
937 Patch: Eric Gaudet
938 - * Integrated some modifications to ease compiling on GNU Darwin.
939 * Added support for leading whitespaces in HREF (BUG#120)
940 * Fixed anchor's hash_table and a few more quirks (were warnings on Alpha)
941 * Fixed entities parsing in URI attributes (BUG#114)
942 * Fixed stop button's sensitivity on plain files (BUG#142)
943 * Made filesize more accurate on directory listings (BUG#143)
944 * Introduced the new Concomitant Control Chain (CCC) design!
945 - All the way in the quering branch
946 - Halfway in the answering branch
947 - Introduced more error handling and status messages
948 - Started implementing error control using the CCC!
949 - Fixed too much caching (BUG#84)
950 - Fixed a CPU hog error condition (BUG#144)
951 - Eliminated the segfault from outdated dns answers (BUG#140)
952 - Fixed repeated Back (faster than rendering) segfault.
953 * Cleaned the header include files
954 * Incremented the valid-charset for dillorc identifiers (BUG#149)
955 * Added support for unterminated quotation of attribute values (BUG#155)
956 Patches: Jorge Arellano
957
958
959 dillo-0.4.0 [March 3, 2001]
960
961 - * Rewrote most of the Dw module from scratch:
962 - Page widget: ported, added support for relative sizes of widgets, and
963 changed behaviour for pressing button 2 on a link.
964 - Removed the now unnecessary event boxes for check and radio buttons.
965 - Modified the code outside to use new Dw.
966 * Started to implement relative sizes for images (in html.c)
967 * Implemented attributes WIDTH, SIZE and NOSHADE of the <hr> tag.
968 * Added focus adjustment for selection lists (<SELECT>)
969 * Implemented TAB, Shift+TAB navigation in FORMS (BUG#86)
970 Patches: Sebastian Geerken
971 - * Included a scaling font_factor into dillorc!
972 Patch: Bruno Widmann
973 - * Fixed bugs #125 and #129 (menu item focus and radio reset in forms)
974 Patch: Jörgen Viksell
975 - * Added code to ignore anything inside STYLE tags.
976 Patch: Mark McLoughlin
977 - * Implemented image rendering based on GdkRGB and DwImage!
978 * Fixed 4 bit color planes support, cleaned the image code,
979 removed a few leaks and added documentation (Images.txt).
980 * Ported every patch from 0.3.2 to 0.4.0
981 Patches: Jorge Arellano
982
983
984 dillo-0.3.2 [February 22, 2001]
985
986 - * Added the option to use oblique font instead of italic (dillorc)
987 Patch: Eric Gaudet, Sebastian Geerken, Jorge Arellano
988 - * Changed Dw_page_find_line_index to use a binary search
989 Patch: Eric Gaudet, Jorge Arellano
990 - * Added a visual hint for visited links (BUG#117)
991 * Repaired the dillorc parser to skip unknown symbols (BUG#119)
992 Patch: Eric Gaudet
993 - * Fixed the segfault for controls outside FORM and SELECT elements (BUG#121)
994 Patch: Eric Gaudet, Jörgen Viksell
995 - * Added support for SUB and SUP tags (BUG#115)
996 Patch: Jörgen Viksell
997 - * Added a geometry directive to dillorc (sets initial browser size)
998 Patch: Livio Baldini, Jorge Arellano
999 - * Fixed bookmarks loading in new browser windows (BUG#110)
1000 * Included a workaround for BUG#71
1001 Patch: Livio Baldini
1002 - * Fixed a CPU hog when clicking ftp URLs (BUG#123)
1003 * Set a 64 bytes threshold on pagemark headers
1004 Patch: Jorge Arellano
1005 - * Added check for negative image sizes.
1006 Patch: Livio Baldini, Sebastian Geerken
1007
1008
1009 dillo-0.3.1 [December 22, 2000]
1010
1011 - * Implemented basic Find-text functionality
1012 Patch: Sam Dennis, Sebastian Geerken, Allan Clark and Jorge Arellano :-)
1013 - * Implemented "Pagemarks" (Kind of a headings-based page index!)
1014 Patch: Sebastian Geerken and Eric Gaudet
1015 - * Fixed nested-lists numbering, and added support for "type" (BUG#76)
1016 * Added support for image maps, both usemap and ismap! (BUG#27)
1017 * Set "on" as default value for check boxes
1018 Patch: Eric Gaudet, Jorge Arellano
1019 - * Added "Copy link location" to the link menu
1020 Patch: Eric Gaudet
1021 - * Removed redundant functions from misc.c
1022 * Added support for BASE, CODE, DFN, KBD, SAMP and VAR tags (BUG#106)
1023 * Added support for TAB characters in plain text files (BUG#112)
1024 Patches: Jörgen Viksell, Jorge Arellano
1025 - * Fixed a_Url_squeeze (BUG#100)
1026 Patch: Livio Baldini, Jorge Arellano
1027 - * Added gamma support and basic transparency for PNG images (BUG#60)
1028 * Moved menu_popup into the 'bw' structure (BUG#96)
1029 * Fixed the gif decoder to get image size from the right place (BUG#98)
1030 * Made the new browser window size the same as the parent (BUG#55)
1031 Patch: Livio Baldini
1032 - * Added support for ISINDEX method (BUG#15)
1033 Patch: Sam Dennis
1034 - * Added support for bare '<' character parsing
1035 * Removed every sign-conflict warnings given by gcc with '-W -Wall'
1036 * Fixed several identation problems (rendering)
1037 * Implemented "Save link as" (link menu)
1038 * Removed the subtle bug that used to segfault when deleting and processing
1039 queue clients at the same time (BUG#111).
1040 * + Some comments, cleanups, size reductions, minor optimizations etc.
1041 Patches: Jorge Arellano Cid
1042
1043
1044 dillo-0.3.0 [November 13, 2000]
1045 (Lots of patches are pending!)
1046
1047 - * Added support for <strike>, <s>, <del> and <u> tags.
1048 Patch: Jörgen Viksell
1049 - * Fixed a bug in #anchors code
1050 Patch: Sebastian Geerken
1051 - * Parsed text between script tags, out of the rendering part.
1052 * Added support for decimal entities that start with 0.
1053 * Added some comments to html.c
1054 Patches: Sean 'Shaleh' Perry
1055 - * Added support for corrupted png images (avoids segfaults!)
1056 Patch: Eric Gaudet, Jorge Arellano
1057 - * Fixed empty title bookmarking (BUG#85 and #88)
1058 Patch: Livio
1059 - * Fixed view-source to take its URL from the right place.
1060 Patch: Sam Dennis
1061 - * Added font support for the compaq iPaq linux distribution.
1062 Patch: Eric Christianson
1063 - * Fixed spaced attribute parsing (BUG#79).
1064 * Fixed concurrent save and downloading!
1065 * Added alpha support for external (simple) plugins.
1066 ? * Added a workaround (maybe a bug fix) for BUG#77 (No segfault).
1067 * Introduced a new design layer between the IO and Dw:
1068 - The imgsink stuff was completely removed.
1069 - The dicache was rewritten from scratch and integrated
1070 into the normal cache.
1071 - A single client queue is being used for both caches.
1072 - The file descriptors were replaced by cache keys that serve
1073 as connection handlers.
1074 - The image data structure and related sources got changed.
1075 - Every decoder (png, gif, jpeg) was adapted to the new scheme.
1076 * Fixed the file-images caching problem and the associated memory-leaks.
1077 * Improved progress bar accuracy for images.
1078 * Added progress bar functionality for plain text (+comments +cleanups)
1079 * Fixed the right-click-over-plain-text segfault (BUG#80).
1080 * Started improving the right-mouse-button menus.
1081 Patches: Jorge Arellano Cid
1082
1083
1084 dillo-0.2.4 [August 21, 2000]
1085
1086 - * Fixed the white square bug with PNG images (BUG #4)
1087 Patch: Luca Rota
1088 - * Added support for #anchors! (BUG #10)
1089 * Added support for resolving relative #anchors (BUG #65)
1090 Patches: Sebastian Geerken
1091 - * Fixed a segfault-source that produced BUG #61.
1092 * Made several cleanups and standarizations in html.c
1093 * Extended entity-parsing scope, and the list of supported entities.
1094 * Rearranged TagInfo data into a new structure.
1095 * Added the base for refresh support in META tags.
1096 Patches: Sean 'Shaleh' Perry
1097 - * Added support for TEXTAREA tags!
1098 Patch: Jörgen Viksell
1099 - * Improved and fixed Html_parse_entities.
1100 * Reimplemented the Stash buffer with a GString.
1101 * Fixed a bug with \r\n-terminated HTML lines.
1102 * Added redirection support for relative URLs (BUG #73).
1103 * Added some comments and minor fixes to patches.
1104 Patches: Jorge Arellano Cid
1105 - * Linked "open link in new window" to mouse button #2 (#3 also works)
1106 Patch: Eric Gaudet
1107
1108
1109 dillo-0.2.3 [August 4, 2000]
1110
1111 - * Removed "search.h" include in http.c (freeBSD compatibility).
1112 Patch: Kurt J. Lidl
1113 - * Removed several memory leaks that were sprinkled through the code.
1114 Patches: Jörgen Viksell
1115 - * Fixed a segfault crash when hitting PgDn in the URL box (BUG #54).
1116 * Removed a segfault source in commands.c
1117 * Made some minor fixes to Dw and added more comments to the code.
1118 * Made changes in dw_gtk_view.c, and fixed the rendering problem that
1119 arise when changing from a scrolled page into another (BUG #58).
1120 * Changes in hruler dynamic resize --not finished though.
1121 * Removed a floating point exception bug in image handling code (image.c)
1122 * Dramatically improved rendering speed!!! Most notably long HTML pages
1123 with lots of links; Improvement ranges from 2 to 5 times faster! (aprox.)
1124 * Fixed misplaced rendering of small pages (BUG #35)
1125 * Fixed the bookmark bug with empty title strings (BUG #57, #67)
1126 * Completed support for "\r", "\n" and "\r\n" in PRE tags.
1127 * Fixed text rendering between multiple selection boxes (BUG #56)
1128 * Added several minor enhancements (comments, formatting, speed, etc)
1129 * Added extensive documentation! (IO.txt, DilloWidget.txt and Dw.txt)
1130 Patches: Jorge Arellano Cid
1131
1132
1133 dillo-0.2.2 [July 9, 2000]
1134
1135 - * Added a gtk_window_set_wmclass to all windows to prevent dialogs
1136 from having the same size as the main window. (Ex: with Sawfish)
1137 * Made some width and height changes to the SELECT-stuff
1138 * Added "submit" to submit buttons without a value.
1139 Patches: Jörgen Viksell
1140 - * Fixed a segfault when calling "about:" method
1141 Patch: Dominic Wong
1142 - * Added an option to force dillorc-defined colors (Try it with slashdot!)
1143 * Fixed display of encoded-URL-links on the status bar
1144 Patches: Adam Sampson
1145 - * Removed several compiler dependencies
1146 (detected with lcc on a 64 bit machine)
1147 * Modified mime.c and Url.c to use list.h, and eliminated hdlr.c
1148 * Standarized unsigned types to glib all around the code
1149 * Added some includes for libc5 compatibility
1150 * Modified IO_callback to avoid a CPU-hog (it happened in some systems).
1151 * Fixed a bug that added a trailing ampersand to GET and POST queries.
1152 * FIxed attribute parsing. It had nasty side effects; as providing
1153 wrong attribute values to POST and GET methods.
1154 * Joined Url.c and url.c into a single module.
1155 * Reimplemented URL resolving functions.
1156 * Implemented a new parser for "file:" URLs (Try "file:" & "file:.").
1157 * Removed child_linkblock and changed the HTML stack handling
1158 (both changes result in a simpler, easier to understand code).
1159 * Modified and removed a segfault source in Html_lb_new.
1160 * Modified forms handling to be more tolerant with messy HTML.
1161 * Linked "image/pjpeg" in MIME types (progressive jpeg)
1162 * Fixed form submittion when there's no submit button (bug #49)
1163 Now dillo can search on freshmeat, altavista, etc!
1164 Patches: Jorge Arellano Cid
1165
1166
1167 dillo-0.2.1 [June 17, 2000]
1168
1169 - * Modified the pixmaps for better interface perception ;)
1170 * Modified Dw_gtk_view_adjustment_value_changed to update the visible
1171 rectangle even though the widget is not realized; it seems to work!
1172 * Implemented the horizontal ruler as a Dw --dw_hruler.[ch]
1173 Fixed its expose problems (Bug #13). (todo: resizing).
1174 * Changed Dw_gtk_progressbar module to "progressbar" --naming stuff
1175 * Added Content-length in file headers (avoids reallocations)
1176 * Modified form submittion and encoding to use dynamic memory allocation
1177 * Eliminated a dns.c hack that passed an int as a void* ;)
1178 * Updated the documentation with an extensive IO description.
1179 Patches: Jorge Arellano Cid
1180 - * Added some functionality to reload button (not complete yet)
1181 Patch: Luca Rota , Jorge Arellano Cid
1182 - * Fixed hash handling within URL parsing. (Bug #9)
1183 Patch: Marcos Ramírez , Jorge Arellano Cid
1184
1185
1186 dillo-0.2.0 [June 2, 2000]
1187 *** THIS IS A HALF-NEW BROWSER ***
1188
1189 - * Finally reimplemented the whole networking process (***BIG changes***)
1190 Rewrote from scratch: IO, cache, web, http, socket, ...
1191 Modified: gif, png, jpeg, html, nav, plain, ... (Every client)
1192 All the querying, retrieving, caching and delivering is NEW!!!
1193 * Eliminated CPU-hogging while waiting for a DNS query to complete
1194 * Eliminated CPU-hogging when facing redirections
1195 * Implemented basic redirection functionality
1196 * Eliminated several segmentation fault bugs
1197 * Modified autoconf stuff
1198 * Modified source-code tree and libraries
1199 * Reduced binary size
1200 * Eliminated a memory leak in socket connections
1201 * Created a new socket connection scheme
1202 * Implemented Cache as the main networking abstraction layer
1203 * Joined almost every URL-retrieving module into libDio
1204 * Set the basis for save-link-as functionality (see save function)
1205 * Modified the navigation stack to a cleaner design
1206 * Improved status bar messages when connecting
1207 * Changed some function names
1208 * Created new pixmaps for the toolbar!
1209 * Added a "new" button near the URL to clear the entry!
1210 * Added a_List_remove to list.h
1211 * Updated documentation in doc/
1212 (README, Cache.txt, store.txt, Dillo.txt, Images.txt and IO.txt)
1213 Patches: Jorge Arellano Cid
1214 - * Added a workaround patch for BUG #35 (page expose problems)
1215 Patch: Andrew McPherson
1216
1217
1218 dillo-0.1.0 [Mar 30, 2000]
1219
1220 - * Fixed a bug that used to lock hostname queries.
1221 ('DNS can't resolve name' mesg.)
1222 * Fixed the wrong parent link when browsing directory contents
1223 * Changed the file/directory HTML-output-layout
1224 * Finally rewrote the whole file.c module :-)
1225 * Made Http_query buffer overflow-safe
1226 * Commented and cleaned web.c
1227 * Changed the licence to GPL. (Raph agreed on that)
1228 * Fixed a tag-search bug in html.c; it produced rendering problems.
1229 * Fixed a parsing problem with tags that were split on different lines
1230 * Fixed the after-tables parsing problem
1231 * Added a startup page
1232 Patches: Jorge Arellano Cid
1233 - * Fixed a bug with http queries that sometimes produced infinite loops
1234 Patch: Marcos Ramírez
1235
1236
1237 dillo-0.0.6 [Mar 9, 2000]
1238
1239 - * Readded an old, wiped-by-mistake, bug fix.
1240 * Added preferences settings using a readable config (dillorc)
1241 * Added a page-title trimmer facility (39 chars) to bookmarks saving.
1242 Patch: Luca Rota
1243 - * Fixed three memory leaks in bookmarks reading
1244 * Added 'Open link in a new window' within the right button pop-up-menu
1245 Patch: Sammy Mannaert
1246 - * Fixed a bug that used to put two slashes on directory file anchors
1247 * Actualized plugin.txt to current code base (and a bit of fix)
1248 * Changed "fprintf(stderr..." to "g_print(..."
1249 * Improved list.h
1250 * Fixed image URLs both for HTTP and local files!
1251 * Fixed tag attribute parsing (The trimmed-text-inside-buttons bug)
1252 * Wrote several documentation files (placed them in doc/)
1253 * Fixed transparent image rendering
1254 * Implemented a binary search for HTML tags (just a bit faster)
1255 * Small leak fixes and some corrections to http.c
1256 * Made style fixes, added function comments and things like that.
1257 Patches: Jorge Arellano Cid
1258
1259
1260 dillo-0.0.5 [Feb 3, 2000]
1261
1262 - * Added progress bars (to be improved)
1263 Patch: James McCollough, Jorge Arellano Cid
1264 - * Rearranged, changed and commented the decompressed image cache code
1265 * Fixed autoconf stuff to include zlib
1266 * Added memory deallocating functions to cache, dicache, socket, http & dns
1267 * Fixed memory leaks in jpeg.c, png.c and gif.c
1268 * Made several changes in dw_page.c and dw_image.c
1269 * Introduced 'list.h' source, and standarized the whole code to use it
1270 * Fixed image rendering (Fixed algorithms and data structures) BIG CHANGES
1271 * Removed some false comments and added true ones (I hope ;)
1272 * Made several "standarizing" changes all over the code and
1273 * some other changes (not worth listing)
1274 Patches: Jorge Arellano Cid
1275 - * Added support for 'text' and 'link' colors inside <BODY> tags
1276 * Standarized all memory management to glib functions
1277 * Fixed the plugin API to work again (forked)
1278 * Removed a warning (remove not implemented for Dw_view and Dw_scroller)
1279 * Solved the page-without-title bug in bookmarks.
1280 Patches: Luca Rota
1281
1282
1283 dillo-0.0.4 [Jan 4, 2000]
1284
1285 - * Removed the test widget
1286 * Added a jpeg image decoder error-handler
1287 Patches: Sammy Mannaert
1288 - * Changed some ADTs to glib to be compatible with newer glibc2 systems
1289 * Added background color alternative when bg. is white (or not specified)
1290 * Improved connecting time status messages
1291 Patches: Jorge Arellano Cid
1292 - * Added background color support.
1293 Patch: Luca Rota, James McCollough
1294 - * Added support for <OL></OL> tags
1295 * Added view-source and view-bookmarks functionality
1296 * Improved PgUP/PgDown and Up/Down response. (No need to grab focus!)
1297 * Fixed openfile gtk run-time warning
1298 * Fixed the focus problem with text camps
1299 * Fixed the title-linger bug with pages that don't specify title.
1300 * Added a preliminary right button menu
1301 * Added POST method support
1302 Patches: Luca Rota
1303 - * Added PNG image support.
1304 Source Code: Geoff Lane, Patch: Jorge Arellano
1305
1306
1307 dillo-0.0.3.tar.gz [Dec 18, 1999]
1308
1309 - * Finished the whole Naming&Coding effort!!!
1310 Stage 2 worked by: Luca Rota and Jorge Arellano
1311 - * Removed all compile time warnings (at least with gcc 2.7.2.3)
1312 * Added more documentation inside the code
1313 * Removed the '~/.dillo' directory creation bug.
1314 * Integrated a patch for menu module
1315 * Renamed menus.c to menu.c
1316 * And some other minor things...
1317 Patches: Jorge Arellano Cid
1318
1319
1320 dillo-0.0.2.tar.gz [Dec, 1999 --Does anyone remember the day?]
1321
1322 - * Finished stage one of the naming&coding design (Hey, it's 1.3 Mb of code!)
1323 Worked by: Jorge Arellano, Sammy Mannaert, James McCollough and Luca Rota
1324 - * Removed some bugs and renamed the source files.
1325 * Heavily rearranged URL/ an IO/ files for better understanding & debugging
1326 * Added more documentation within the sources
1327 * Recoded automake stuff
1328 * Integrated some queued patches
1329 * (And several things that I have no time to write now! :-)
1330 Patches: Jorge Arellano Cid
1331
1332
1333 dillo-0.0.1.tar.gz [Dec, 1999]
1334
1335 - * Halfway release, amidst stage one of the naming&coding effort.
1336 Worked by: Jorge Arellano, Sammy Mannaert, James McCollough and Luca Rota
1337
1338
1339 dillo-0.0.0.tar.gz [Dec, 1999]
1340
1341 - * Applied a cleanning patch to menus.[ch]
1342 Patch: Sammy Mannaert
1343 - * Made a threaded DNS scheme (several improvements: now it works with gdb)
1344 * Bug fix on TMP_FAILURE_RETRY
1345 * Bug fix on links not been followed (Url parsing)
1346 * Changed the default pixmaps
1347 * Maked automake, autoconf, autoheader, changes
1348 * Changed binary name
1349 Patches: Jorge Arellano Cid
1350
1351
0
1 This is the Changelog file that existed before Dillo began.
2
3
4 ===========================================================================
5 Gzilla project
6
7 Wed 28 Oct 1999 Christopher Reid Palmer <chrisp@innerfireworks.com>
8 Rota Luca
9 * support for arrow keys
10 * proper tool bar
11 * cursor changing
12
13 Randy Maas
14 * documentation
15 * URL sub-tree cleanups and speed-ups
16
17 Sammy Mannaert
18 * bookmarks.c fix
19 * menu hotkeys, menu pulloffs and Location pull-off
20 * file:/ URL bugfix, for larger than 12 char names
21
22 David Press
23 * fix to cursor change
24
25 Fri Jul 23 22:12:14 1999 ObiTuarY <obituary@freshmeat.net>
26
27 * *.*: Upgraded to autoconf 2.13 and automake 1.4. Reorganization
28 of the source.
29 * src/interface.{c,h}: Added pixmaps for the buttons. Removed the
30 open button. Doesn't it feel redundant with the location text entry
31 just under it ? ;-)
32 * src/pixmaps.h: New file holding the pixmaps.
33
34 0.2.0 17-18 Jul 1999 Christopher Reid Palmer <chrisp@innerFireWorks.com>
35 * Restructured menus, beginning to implement functionality of new menu items.
36 * Closed hole for when ~/.gzilla exists but is not a dir.
37 * Poked at bookmarks.
38
39
40
41 ===========================================================================
42 Raph Levien project
43
44
45 Mon Dec 21 00:07:36 1998 Raph Levien <raph@gimp.org>
46
47 * gzilladns.c: fixed a bug (adding the input handler more
48 than once) that got triggered in Gtk 1.1.x.
49 * gzillageturl.c: added an "about:" mechanism with simple
50 redirects. This is 0.1.7.
51
52 Sun Dec 20 22:25:17 1998 Raph Levien <raph@gimp.org>
53
54 * Accelerator group code so that it now compiles with both
55 Gtk 1.0.x and 1.1.x
56 * Added directory scanning support to gzilla_file (thanks to Jim
57 McBeath!)
58
59 0.1.5 (2 Jan 1998 RLL)
60 * Finished moving junk out of bytesink (mostly into linkblock).
61 Now, the former abstraction is lean and mean, and the latter
62 is a junkheap :).
63 * Tried to hook up most of the abort/destroy logic, so it should
64 leak a lot less memory now.
65 * I'm moving images from gtk_preview to gzw_image as well.
66
67 0.1.4 (26 Nov 1997 RLL)
68 * Pages > 32kpixels actually scroll now.
69 * Pretty significant rework of the whole abort architecture (see
70 abort.html for more details). Also separated out some of the
71 web-specific stuff in bytesink to linkblock.
72 * It seems relatively stable now, but it's nowhere near freeing
73 all of the memory it allocates.
74
75 0.1.3
76 * Switched page widget from gtk to gzw.
77 * Significant enhancements in functionality and stability.
78
79 0.0.10
80 * Major change is reorganization of network code - all fetches now
81 go through gzilla_url_get.
82
83 * Aborts (i.e. the implementation of the Stop button) are now
84 handled by a new bytesink signal ("abort"). The abort logic has
85 been partially upgraded to handle multiple windows - now,
86 gzilla_bw_abort_all aborts all bytesinks in a window.
87
88
0 Basic Installation
1 ==================
2
3 These are generic installation instructions.
4
5 The `configure' shell script attempts to guess correct values for
6 various system-dependent variables used during compilation. It uses
7 those values to create a `Makefile' in each directory of the package.
8 It may also create one or more `.h' files containing system-dependent
9 definitions. Finally, it creates a shell script `config.status' that
10 you can run in the future to recreate the current configuration, a file
11 `config.cache' that saves the results of its tests to speed up
12 reconfiguring, and a file `config.log' containing compiler output
13 (useful mainly for debugging `configure').
14
15 If you need to do unusual things to compile the package, please try
16 to figure out how `configure' could check whether to do them, and mail
17 diffs or instructions to the address given in the `README' so they can
18 be considered for the next release. If at some point `config.cache'
19 contains results you don't want to keep, you may remove or edit it.
20
21 The file `configure.in' is used to create `configure' by a program
22 called `autoconf'. You only need `configure.in' if you want to change
23 it or regenerate `configure' using a newer version of `autoconf'.
24
25 The simplest way to compile this package is:
26
27 1. `cd' to the directory containing the package's source code and type
28 `./configure' to configure the package for your system. If you're
29 using `csh' on an old version of System V, you might need to type
30 `sh ./configure' instead to prevent `csh' from trying to execute
31 `configure' itself.
32
33 Running `configure' takes a while. While running, it prints some
34 messages telling which features it is checking for.
35
36 2. Type `make' to compile the package.
37
38 3. Optionally, type `make check' to run any self-tests that come with
39 the package.
40
41 4. Type `make install' to install the programs and any data files and
42 documentation.
43
44 5. You can remove the program binaries and object files from the
45 source code directory by typing `make clean'. To also remove the
46 files that `configure' created (so you can compile the package for
47 a different kind of computer), type `make distclean'. There is
48 also a `make maintainer-clean' target, but that is intended mainly
49 for the package's developers. If you use it, you may have to get
50 all sorts of other programs in order to regenerate files that came
51 with the distribution.
52
53 Compilers and Options
54 =====================
55
56 Some systems require unusual options for compilation or linking that
57 the `configure' script does not know about. You can give `configure'
58 initial values for variables by setting them in the environment. Using
59 a Bourne-compatible shell, you can do that on the command line like
60 this:
61 CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
62
63 Or on systems that have the `env' program, you can do it like this:
64 env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
65
66 Compiling For Multiple Architectures
67 ====================================
68
69 You can compile the package for more than one kind of computer at the
70 same time, by placing the object files for each architecture in their
71 own directory. To do this, you must use a version of `make' that
72 supports the `VPATH' variable, such as GNU `make'. `cd' to the
73 directory where you want the object files and executables to go and run
74 the `configure' script. `configure' automatically checks for the
75 source code in the directory that `configure' is in and in `..'.
76
77 If you have to use a `make' that does not supports the `VPATH'
78 variable, you have to compile the package for one architecture at a time
79 in the source code directory. After you have installed the package for
80 one architecture, use `make distclean' before reconfiguring for another
81 architecture.
82
83 Installation Names
84 ==================
85
86 By default, `make install' will install the package's files in
87 `/usr/local/bin', `/usr/local/man', etc. You can specify an
88 installation prefix other than `/usr/local' by giving `configure' the
89 option `--prefix=PATH'.
90
91 You can specify separate installation prefixes for
92 architecture-specific files and architecture-independent files. If you
93 give `configure' the option `--exec-prefix=PATH', the package will use
94 PATH as the prefix for installing programs and libraries.
95 Documentation and other data files will still use the regular prefix.
96
97 If the package supports it, you can cause programs to be installed
98 with an extra prefix or suffix on their names by giving `configure' the
99 option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
100
101 Optional Features
102 =================
103
104 Some packages pay attention to `--enable-FEATURE' options to
105 `configure', where FEATURE indicates an optional part of the package.
106 They may also pay attention to `--with-PACKAGE' options, where PACKAGE
107 is something like `gnu-as' or `x' (for the X Window System). The
108 `README' should mention any `--enable-' and `--with-' options that the
109 package recognizes.
110
111 For packages that use the X Window System, `configure' can usually
112 find the X include and library files automatically, but if it doesn't,
113 you can use the `configure' options `--x-includes=DIR' and
114 `--x-libraries=DIR' to specify their locations.
115
116 Specifying the System Type
117 ==========================
118
119 There may be some features `configure' can not figure out
120 automatically, but needs to determine by the type of host the package
121 will run on. Usually `configure' can figure that out, but if it prints
122 a message saying it can not guess the host type, give it the
123 `--host=TYPE' option. TYPE can either be a short name for the system
124 type, such as `sun4', or a canonical name with three fields:
125 CPU-COMPANY-SYSTEM
126
127 See the file `config.sub' for the possible values of each field. If
128 `config.sub' isn't included in this package, then this package doesn't
129 need to know the host type.
130
131 If you are building compiler tools for cross-compiling, you can also
132 use the `--target=TYPE' option to select the type of system they will
133 produce code for and the `--build=TYPE' option to select the type of
134 system on which you are compiling the package.
135
136 Sharing Defaults
137 ================
138
139 If you want to set default values for `configure' scripts to share,
140 you can create a site shell script called `config.site' that gives
141 default values for variables like `CC', `cache_file', and `prefix'.
142 `configure' looks for `PREFIX/share/config.site' if it exists, then
143 `PREFIX/etc/config.site' if it exists. Or, you can set the
144 `CONFIG_SITE' environment variable to the location of the site script.
145 A warning: not all `configure' scripts look for a site script.
146
147 Operation Controls
148 ==================
149
150 `configure' recognizes the following options to control how it
151 operates.
152
153 `--cache-file=FILE'
154 Use and save the results of the tests in FILE instead of
155 `./config.cache'. Set FILE to `/dev/null' to disable caching, for
156 debugging `configure'.
157
158 `--help'
159 Print a summary of the options to `configure', and exit.
160
161 `--quiet'
162 `--silent'
163 `-q'
164 Do not print messages saying which checks are being made.
165
166 `--srcdir=DIR'
167 Look for the package's source code in directory DIR. Usually
168 `configure' can determine that directory automatically.
169
170 `--version'
171 Print the version of Autoconf used to generate the `configure'
172 script, and exit.
173
174 `configure' also accepts some other, not widely useful, options.
175
0 SUBDIRS = doc dpip src dpid dpi
1
2 EXTRA_DIST = ChangeLog.old dillorc install-dpi-local
3
4 sysconf_DATA = dillorc
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 srcdir = @srcdir@
17 top_srcdir = @top_srcdir@
18 VPATH = @srcdir@
19 pkgdatadir = $(datadir)/@PACKAGE@
20 pkglibdir = $(libdir)/@PACKAGE@
21 pkgincludedir = $(includedir)/@PACKAGE@
22 top_builddir = .
23 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
24 INSTALL = @INSTALL@
25 install_sh_DATA = $(install_sh) -c -m 644
26 install_sh_PROGRAM = $(install_sh) -c
27 install_sh_SCRIPT = $(install_sh) -c
28 INSTALL_HEADER = $(INSTALL_DATA)
29 transform = $(program_transform_name)
30 NORMAL_INSTALL = :
31 PRE_INSTALL = :
32 POST_INSTALL = :
33 NORMAL_UNINSTALL = :
34 PRE_UNINSTALL = :
35 POST_UNINSTALL = :
36 build_triplet = @build@
37 host_triplet = @host@
38 target_triplet = @target@
39 DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
40 $(srcdir)/Makefile.in $(srcdir)/config.h.in \
41 $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
42 config.guess config.sub depcomp install-sh missing
43 subdir = .
44 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
45 am__aclocal_m4_deps = $(top_srcdir)/configure.in
46 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
47 $(ACLOCAL_M4)
48 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
49 configure.lineno configure.status.lineno
50 mkinstalldirs = $(install_sh) -d
51 CONFIG_HEADER = config.h
52 CONFIG_CLEAN_FILES =
53 SOURCES =
54 DIST_SOURCES =
55 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
56 html-recursive info-recursive install-data-recursive \
57 install-exec-recursive install-info-recursive \
58 install-recursive installcheck-recursive installdirs-recursive \
59 pdf-recursive ps-recursive uninstall-info-recursive \
60 uninstall-recursive
61 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
62 am__vpath_adj = case $$p in \
63 $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
64 *) f=$$p;; \
65 esac;
66 am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
67 am__installdirs = "$(DESTDIR)$(sysconfdir)"
68 sysconfDATA_INSTALL = $(INSTALL_DATA)
69 DATA = $(sysconf_DATA)
70 ETAGS = etags
71 CTAGS = ctags
72 DIST_SUBDIRS = $(SUBDIRS)
73 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
74 distdir = $(PACKAGE)-$(VERSION)
75 top_distdir = $(distdir)
76 am__remove_distdir = \
77 { test ! -d $(distdir) \
78 || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
79 && rm -fr $(distdir); }; }
80 DIST_ARCHIVES = $(distdir).tar.gz
81 GZIP_ENV = --best
82 distuninstallcheck_listfiles = find . -type f -print
83 distcleancheck_listfiles = find . -type f -print
84 ACLOCAL = @ACLOCAL@
85 AMDEP_FALSE = @AMDEP_FALSE@
86 AMDEP_TRUE = @AMDEP_TRUE@
87 AMTAR = @AMTAR@
88 AUTOCONF = @AUTOCONF@
89 AUTOHEADER = @AUTOHEADER@
90 AUTOMAKE = @AUTOMAKE@
91 AWK = @AWK@
92 CC = @CC@
93 CCDEPMODE = @CCDEPMODE@
94 CFLAGS = @CFLAGS@
95 CPP = @CPP@
96 CPPFLAGS = @CPPFLAGS@
97 CXX = @CXX@
98 CXXDEPMODE = @CXXDEPMODE@
99 CXXFLAGS = @CXXFLAGS@
100 CYGPATH_W = @CYGPATH_W@
101 DEFS = @DEFS@
102 DEPDIR = @DEPDIR@
103 DLGUI_FALSE = @DLGUI_FALSE@
104 DLGUI_TRUE = @DLGUI_TRUE@
105 ECHO_C = @ECHO_C@
106 ECHO_N = @ECHO_N@
107 ECHO_T = @ECHO_T@
108 EGREP = @EGREP@
109 EXEEXT = @EXEEXT@
110 GLIB_CFLAGS = @GLIB_CFLAGS@
111 GLIB_CONFIG = @GLIB_CONFIG@
112 GLIB_LIBS = @GLIB_LIBS@
113 GTK_CFLAGS = @GTK_CFLAGS@
114 GTK_CONFIG = @GTK_CONFIG@
115 GTK_LIBS = @GTK_LIBS@
116 INSTALL_DATA = @INSTALL_DATA@
117 INSTALL_PROGRAM = @INSTALL_PROGRAM@
118 INSTALL_SCRIPT = @INSTALL_SCRIPT@
119 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
120 LDFLAGS = @LDFLAGS@
121 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
122 LIBFLTK_LIBS = @LIBFLTK_LIBS@
123 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
124 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
125 LIBJPEG_LIBS = @LIBJPEG_LIBS@
126 LIBOBJS = @LIBOBJS@
127 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
128 LIBPNG_LIBS = @LIBPNG_LIBS@
129 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
130 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
131 LIBS = @LIBS@
132 LIBSSL_LIBS = @LIBSSL_LIBS@
133 LIBZ_LIBS = @LIBZ_LIBS@
134 LTLIBOBJS = @LTLIBOBJS@
135 MAKEINFO = @MAKEINFO@
136 OBJEXT = @OBJEXT@
137 PACKAGE = @PACKAGE@
138 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
139 PACKAGE_NAME = @PACKAGE_NAME@
140 PACKAGE_STRING = @PACKAGE_STRING@
141 PACKAGE_TARNAME = @PACKAGE_TARNAME@
142 PACKAGE_VERSION = @PACKAGE_VERSION@
143 PATH_SEPARATOR = @PATH_SEPARATOR@
144 RANLIB = @RANLIB@
145 SET_MAKE = @SET_MAKE@
146 SHELL = @SHELL@
147 STRIP = @STRIP@
148 VERSION = @VERSION@
149 ac_ct_CC = @ac_ct_CC@
150 ac_ct_CXX = @ac_ct_CXX@
151 ac_ct_RANLIB = @ac_ct_RANLIB@
152 ac_ct_STRIP = @ac_ct_STRIP@
153 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
154 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
155 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
156 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
157 am__include = @am__include@
158 am__leading_dot = @am__leading_dot@
159 am__quote = @am__quote@
160 am__tar = @am__tar@
161 am__untar = @am__untar@
162 bindir = @bindir@
163 build = @build@
164 build_alias = @build_alias@
165 build_cpu = @build_cpu@
166 build_os = @build_os@
167 build_vendor = @build_vendor@
168 datadir = @datadir@
169 exec_prefix = @exec_prefix@
170 host = @host@
171 host_alias = @host_alias@
172 host_cpu = @host_cpu@
173 host_os = @host_os@
174 host_vendor = @host_vendor@
175 includedir = @includedir@
176 infodir = @infodir@
177 install_sh = @install_sh@
178 libdir = @libdir@
179 libexecdir = @libexecdir@
180 localstatedir = @localstatedir@
181 mandir = @mandir@
182 mkdir_p = @mkdir_p@
183 oldincludedir = @oldincludedir@
184 prefix = @prefix@
185 program_transform_name = @program_transform_name@
186 sbindir = @sbindir@
187 sharedstatedir = @sharedstatedir@
188 sysconfdir = @sysconfdir@
189 target = @target@
190 target_alias = @target_alias@
191 target_cpu = @target_cpu@
192 target_os = @target_os@
193 target_vendor = @target_vendor@
194 SUBDIRS = doc dpip src dpid dpi
195 EXTRA_DIST = ChangeLog.old dillorc install-dpi-local
196 sysconf_DATA = dillorc
197 all: config.h
198 $(MAKE) $(AM_MAKEFLAGS) all-recursive
199
200 .SUFFIXES:
201 am--refresh:
202 @:
203 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
204 @for dep in $?; do \
205 case '$(am__configure_deps)' in \
206 *$$dep*) \
207 echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \
208 cd $(srcdir) && $(AUTOMAKE) --gnu \
209 && exit 0; \
210 exit 1;; \
211 esac; \
212 done; \
213 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \
214 cd $(top_srcdir) && \
215 $(AUTOMAKE) --gnu Makefile
216 .PRECIOUS: Makefile
217 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
218 @case '$?' in \
219 *config.status*) \
220 echo ' $(SHELL) ./config.status'; \
221 $(SHELL) ./config.status;; \
222 *) \
223 echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
224 cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
225 esac;
226
227 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
228 $(SHELL) ./config.status --recheck
229
230 $(top_srcdir)/configure: $(am__configure_deps)
231 cd $(srcdir) && $(AUTOCONF)
232 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
233 cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
234
235 config.h: stamp-h1
236 @if test ! -f $@; then \
237 rm -f stamp-h1; \
238 $(MAKE) stamp-h1; \
239 else :; fi
240
241 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
242 @rm -f stamp-h1
243 cd $(top_builddir) && $(SHELL) ./config.status config.h
244 $(srcdir)/config.h.in: $(am__configure_deps)
245 cd $(top_srcdir) && $(AUTOHEADER)
246 rm -f stamp-h1
247 touch $@
248
249 distclean-hdr:
250 -rm -f config.h stamp-h1
251 uninstall-info-am:
252 install-sysconfDATA: $(sysconf_DATA)
253 @$(NORMAL_INSTALL)
254 test -z "$(sysconfdir)" || $(mkdir_p) "$(DESTDIR)$(sysconfdir)"
255 @list='$(sysconf_DATA)'; for p in $$list; do \
256 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
257 f=$(am__strip_dir) \
258 echo " $(sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \
259 $(sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \
260 done
261
262 uninstall-sysconfDATA:
263 @$(NORMAL_UNINSTALL)
264 @list='$(sysconf_DATA)'; for p in $$list; do \
265 f=$(am__strip_dir) \
266 echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \
267 rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \
268 done
269
270 # This directory's subdirectories are mostly independent; you can cd
271 # into them and run `make' without going through this Makefile.
272 # To change the values of `make' variables: instead of editing Makefiles,
273 # (1) if the variable is set in `config.status', edit `config.status'
274 # (which will cause the Makefiles to be regenerated when you run `make');
275 # (2) otherwise, pass the desired values on the `make' command line.
276 $(RECURSIVE_TARGETS):
277 @failcom='exit 1'; \
278 for f in x $$MAKEFLAGS; do \
279 case $$f in \
280 *=* | --[!k]*);; \
281 *k*) failcom='fail=yes';; \
282 esac; \
283 done; \
284 dot_seen=no; \
285 target=`echo $@ | sed s/-recursive//`; \
286 list='$(SUBDIRS)'; for subdir in $$list; do \
287 echo "Making $$target in $$subdir"; \
288 if test "$$subdir" = "."; then \
289 dot_seen=yes; \
290 local_target="$$target-am"; \
291 else \
292 local_target="$$target"; \
293 fi; \
294 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
295 || eval $$failcom; \
296 done; \
297 if test "$$dot_seen" = "no"; then \
298 $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
299 fi; test -z "$$fail"
300
301 mostlyclean-recursive clean-recursive distclean-recursive \
302 maintainer-clean-recursive:
303 @failcom='exit 1'; \
304 for f in x $$MAKEFLAGS; do \
305 case $$f in \
306 *=* | --[!k]*);; \
307 *k*) failcom='fail=yes';; \
308 esac; \
309 done; \
310 dot_seen=no; \
311 case "$@" in \
312 distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
313 *) list='$(SUBDIRS)' ;; \
314 esac; \
315 rev=''; for subdir in $$list; do \
316 if test "$$subdir" = "."; then :; else \
317 rev="$$subdir $$rev"; \
318 fi; \
319 done; \
320 rev="$$rev ."; \
321 target=`echo $@ | sed s/-recursive//`; \
322 for subdir in $$rev; do \
323 echo "Making $$target in $$subdir"; \
324 if test "$$subdir" = "."; then \
325 local_target="$$target-am"; \
326 else \
327 local_target="$$target"; \
328 fi; \
329 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
330 || eval $$failcom; \
331 done && test -z "$$fail"
332 tags-recursive:
333 list='$(SUBDIRS)'; for subdir in $$list; do \
334 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
335 done
336 ctags-recursive:
337 list='$(SUBDIRS)'; for subdir in $$list; do \
338 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
339 done
340
341 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
342 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
343 unique=`for i in $$list; do \
344 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
345 done | \
346 $(AWK) ' { files[$$0] = 1; } \
347 END { for (i in files) print i; }'`; \
348 mkid -fID $$unique
349 tags: TAGS
350
351 TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
352 $(TAGS_FILES) $(LISP)
353 tags=; \
354 here=`pwd`; \
355 if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
356 include_option=--etags-include; \
357 empty_fix=.; \
358 else \
359 include_option=--include; \
360 empty_fix=; \
361 fi; \
362 list='$(SUBDIRS)'; for subdir in $$list; do \
363 if test "$$subdir" = .; then :; else \
364 test ! -f $$subdir/TAGS || \
365 tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
366 fi; \
367 done; \
368 list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
369 unique=`for i in $$list; do \
370 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
371 done | \
372 $(AWK) ' { files[$$0] = 1; } \
373 END { for (i in files) print i; }'`; \
374 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
375 test -n "$$unique" || unique=$$empty_fix; \
376 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
377 $$tags $$unique; \
378 fi
379 ctags: CTAGS
380 CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
381 $(TAGS_FILES) $(LISP)
382 tags=; \
383 here=`pwd`; \
384 list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
385 unique=`for i in $$list; do \
386 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
387 done | \
388 $(AWK) ' { files[$$0] = 1; } \
389 END { for (i in files) print i; }'`; \
390 test -z "$(CTAGS_ARGS)$$tags$$unique" \
391 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
392 $$tags $$unique
393
394 GTAGS:
395 here=`$(am__cd) $(top_builddir) && pwd` \
396 && cd $(top_srcdir) \
397 && gtags -i $(GTAGS_ARGS) $$here
398
399 distclean-tags:
400 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
401
402 distdir: $(DISTFILES)
403 $(am__remove_distdir)
404 mkdir $(distdir)
405 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
406 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
407 list='$(DISTFILES)'; for file in $$list; do \
408 case $$file in \
409 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
410 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
411 esac; \
412 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
413 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
414 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
415 dir="/$$dir"; \
416 $(mkdir_p) "$(distdir)$$dir"; \
417 else \
418 dir=''; \
419 fi; \
420 if test -d $$d/$$file; then \
421 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
422 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
423 fi; \
424 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
425 else \
426 test -f $(distdir)/$$file \
427 || cp -p $$d/$$file $(distdir)/$$file \
428 || exit 1; \
429 fi; \
430 done
431 list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
432 if test "$$subdir" = .; then :; else \
433 test -d "$(distdir)/$$subdir" \
434 || $(mkdir_p) "$(distdir)/$$subdir" \
435 || exit 1; \
436 distdir=`$(am__cd) $(distdir) && pwd`; \
437 top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
438 (cd $$subdir && \
439 $(MAKE) $(AM_MAKEFLAGS) \
440 top_distdir="$$top_distdir" \
441 distdir="$$distdir/$$subdir" \
442 distdir) \
443 || exit 1; \
444 fi; \
445 done
446 -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
447 ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
448 ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
449 ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
450 || chmod -R a+r $(distdir)
451 dist-gzip: distdir
452 tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
453 $(am__remove_distdir)
454
455 dist-bzip2: distdir
456 tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
457 $(am__remove_distdir)
458
459 dist-tarZ: distdir
460 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
461 $(am__remove_distdir)
462
463 dist-shar: distdir
464 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
465 $(am__remove_distdir)
466
467 dist-zip: distdir
468 -rm -f $(distdir).zip
469 zip -rq $(distdir).zip $(distdir)
470 $(am__remove_distdir)
471
472 dist dist-all: distdir
473 tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
474 $(am__remove_distdir)
475
476 # This target untars the dist file and tries a VPATH configuration. Then
477 # it guarantees that the distribution is self-contained by making another
478 # tarfile.
479 distcheck: dist
480 case '$(DIST_ARCHIVES)' in \
481 *.tar.gz*) \
482 GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
483 *.tar.bz2*) \
484 bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
485 *.tar.Z*) \
486 uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
487 *.shar.gz*) \
488 GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
489 *.zip*) \
490 unzip $(distdir).zip ;;\
491 esac
492 chmod -R a-w $(distdir); chmod a+w $(distdir)
493 mkdir $(distdir)/_build
494 mkdir $(distdir)/_inst
495 chmod a-w $(distdir)
496 dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
497 && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
498 && cd $(distdir)/_build \
499 && ../configure --srcdir=.. --prefix="$$dc_install_base" \
500 $(DISTCHECK_CONFIGURE_FLAGS) \
501 && $(MAKE) $(AM_MAKEFLAGS) \
502 && $(MAKE) $(AM_MAKEFLAGS) dvi \
503 && $(MAKE) $(AM_MAKEFLAGS) check \
504 && $(MAKE) $(AM_MAKEFLAGS) install \
505 && $(MAKE) $(AM_MAKEFLAGS) installcheck \
506 && $(MAKE) $(AM_MAKEFLAGS) uninstall \
507 && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
508 distuninstallcheck \
509 && chmod -R a-w "$$dc_install_base" \
510 && ({ \
511 (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
512 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
513 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
514 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
515 distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
516 } || { rm -rf "$$dc_destdir"; exit 1; }) \
517 && rm -rf "$$dc_destdir" \
518 && $(MAKE) $(AM_MAKEFLAGS) dist \
519 && rm -rf $(DIST_ARCHIVES) \
520 && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
521 $(am__remove_distdir)
522 @(echo "$(distdir) archives ready for distribution: "; \
523 list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
524 sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
525 distuninstallcheck:
526 @cd $(distuninstallcheck_dir) \
527 && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
528 || { echo "ERROR: files left after uninstall:" ; \
529 if test -n "$(DESTDIR)"; then \
530 echo " (check DESTDIR support)"; \
531 fi ; \
532 $(distuninstallcheck_listfiles) ; \
533 exit 1; } >&2
534 distcleancheck: distclean
535 @if test '$(srcdir)' = . ; then \
536 echo "ERROR: distcleancheck can only run from a VPATH build" ; \
537 exit 1 ; \
538 fi
539 @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
540 || { echo "ERROR: files left in build directory after distclean:" ; \
541 $(distcleancheck_listfiles) ; \
542 exit 1; } >&2
543 check-am: all-am
544 check: check-recursive
545 all-am: Makefile $(DATA) config.h
546 installdirs: installdirs-recursive
547 installdirs-am:
548 for dir in "$(DESTDIR)$(sysconfdir)"; do \
549 test -z "$$dir" || $(mkdir_p) "$$dir"; \
550 done
551 install: install-recursive
552 install-exec: install-exec-recursive
553 install-data: install-data-recursive
554 uninstall: uninstall-recursive
555
556 install-am: all-am
557 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
558
559 installcheck: installcheck-recursive
560 install-strip:
561 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
562 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
563 `test -z '$(STRIP)' || \
564 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
565 mostlyclean-generic:
566
567 clean-generic:
568
569 distclean-generic:
570 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
571
572 maintainer-clean-generic:
573 @echo "This command is intended for maintainers to use"
574 @echo "it deletes files that may require special tools to rebuild."
575 clean: clean-recursive
576
577 clean-am: clean-generic mostlyclean-am
578
579 distclean: distclean-recursive
580 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
581 -rm -f Makefile
582 distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
583
584 dvi: dvi-recursive
585
586 dvi-am:
587
588 html: html-recursive
589
590 info: info-recursive
591
592 info-am:
593
594 install-data-am:
595
596 install-exec-am: install-sysconfDATA
597
598 install-info: install-info-recursive
599
600 install-man:
601
602 installcheck-am:
603
604 maintainer-clean: maintainer-clean-recursive
605 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
606 -rm -rf $(top_srcdir)/autom4te.cache
607 -rm -f Makefile
608 maintainer-clean-am: distclean-am maintainer-clean-generic
609
610 mostlyclean: mostlyclean-recursive
611
612 mostlyclean-am: mostlyclean-generic
613
614 pdf: pdf-recursive
615
616 pdf-am:
617
618 ps: ps-recursive
619
620 ps-am:
621
622 uninstall-am: uninstall-info-am uninstall-sysconfDATA
623
624 uninstall-info: uninstall-info-recursive
625
626 .PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \
627 check-am clean clean-generic clean-recursive ctags \
628 ctags-recursive dist dist-all dist-bzip2 dist-gzip dist-shar \
629 dist-tarZ dist-zip distcheck distclean distclean-generic \
630 distclean-hdr distclean-recursive distclean-tags \
631 distcleancheck distdir distuninstallcheck dvi dvi-am html \
632 html-am info info-am install install-am install-data \
633 install-data-am install-exec install-exec-am install-info \
634 install-info-am install-man install-strip install-sysconfDATA \
635 installcheck installcheck-am installdirs installdirs-am \
636 maintainer-clean maintainer-clean-generic \
637 maintainer-clean-recursive mostlyclean mostlyclean-generic \
638 mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \
639 uninstall uninstall-am uninstall-info-am uninstall-sysconfDATA
640
641 # Tell versions [3.59,3.63) of GNU make to not export all variables.
642 # Otherwise a system limit (for SysV at least) may be exceeded.
643 .NOEXPORT:
0 ----
1 NEWS
2 ----
3
4 March 2001:
5
6 Finally the new dillo widget is ready! (dillo >= 0.4.0).
7 0.4.0 is able to cope with low resolution depths.
8
9
10 Nov 2000:
11
12 Introduced a new design layer between the IO and the Dw.
13
14 We'll try to focus on table rendering.
15
16
17 Apr 2002:
18
19 We moved to: http://dillo.cipsga.org.br/
20
21
22 Dec 2002
23
24 We moved to: http://dillo.auriga.wearlab.de/
25
26 Jun 2003
27
28 http://www.dillo.org/ (hosted at the wearlab!)
29
30
31 Jorge.-
32 jcid@inf.utfsm.cl
33 Project maintainer, core developer, patcher, you name it! :-)
34
0 =======
1 Dillo
2 =======
3
4
5 If you're using libc5, don't worry, I used to use libc5! If
6 you're brave, edit 'dns.c' and uncomment line 46, pray and
7 compile. If it doesn't work, email me (most probably a header
8 include problem); if you succeed, report it to me anyway!
9
10 Dillo needs the following packages:
11 glib
12 GTK+-1.2.x (2.0 will not compile).
13 jpeg, png (for displaying images).
14 wget (for getting files via ftp; wget is not compiled into
15 the dillo binaries)
16
17 If you get an error that dillo can't load the shared library,
18 try "setenv LD_LIBRARY_PATH /usr/local/lib", or wherever you have
19 your gtk+ libs.
20
21 Dillo can be invoked with command line options. Just type
22 'dillo --help' to know about them.
23
24 You may experience some font handling problems derived from
25 locale handling (for instance if you use LC_ALL=sv_SE). Just try
26 LC_ALL=POSIX and it will be fine.
27
28 Once you have dillo running for the first time, the next step
29 is to read the help! (there's a link from the splash screen).
30
31 This is release should be regarded as "very stable beta".
32
33
34 Here's a list of some well known problems:
35
36 * no FRAMES rendering
37 * no http authentication
38 * no https -- Read the online FAQ to enable a protoype.
39
40
41 ------------
42 Dpi programs
43 ------------
44
45 These are installed by "make install". If you don't have root
46 access, copy "dillo" and "dpid" to some directory in your path
47 and install the dpis by running "./install-dpi-local" from the
48 top directory (they will be installed under ~/.dillo).
49
50 -----
51 Linux
52 -----
53
54 There's a small chance of experiencing dillo "freezes" with
55 certain linux kernels. This is a _kernel bug_, inside the 2.2.x
56 series up to 2.4.8. From 2.4.9 it has been corrected. The bug is
57 inside poll() and the best that you can do is to upgrade.
58 If you can't upgrade the kernel though, this workaround patch
59 for dillo may help you:
60 http://www.ime.usp.br/~livio/dillo/patches/poll-fix-fast.diff
61
62 ----
63 *BSD
64 ----
65
66 Dillo compiles on *BSD systems; Starting from dillo-0.6.5, we
67 included special detection code (at ./configure time) that should
68 arrange everything for a clean compile, but please note that
69 you'll need GNU make.
70
71 From OpenBSD >= 3.3, gethost* calls are not thread safe. If
72 your dillo crashes or locks at times, just use:
73
74 ./configure --disable-threaded-dns
75
76 so dillo uses a single thread for name resolving.
77
78
79 -------
80 Solaris
81 -------
82
83 Dillo compiles and runs OK on Solaris but:
84
85 * use gmake (a symbolic link make -> gmake works OK)
86 * If you have trouble with GTK and GLIB from the Freeware
87 Companion CD, get the ones at www.sunfreeware.com instead.
88
89 These environment vars may help setting yours:
90
91 PATH=/usr/bin:/usr/sbin:/usr/dt/bin:/usr/openwin/bin:/usr/local/bin:/opt/sfw/bi:/usr/ccs/bin:/usr/ucb
92 LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:/usr/local/lib/glib:/opt/sfw/lib:/usr/ucblib
93
94 Solaris is very inconsistent so you may need to add/remove:
95
96 -lrt -lposix4
97
98 at link time.
99
100
101 ---
102 gcc
103 ---
104
105 gcc 2.95.2 has problems with -O2:
106 "-O2 -mcpu=pentium" works fine.
107 "-O0 -mcpu=k6 -march=pentium -g3" works fine also.
108 "-O -mcpu=k6 -march=pentium -g3" works fine as well, but
109 "-O2 -mcpu=k6 -march=pentium -g3" doesn't work. Then again
110 "-O6 -mcpu=pentium -march=pentium -g3" DOES work.
111
112
113
114
115 Jorge.-
116 (jcid@dillo.org)
117 Mar 24, 2006
0 # generated automatically by aclocal 1.9.5 -*- Autoconf -*-
1
2 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 # 2005 Free Software Foundation, Inc.
4 # This file is free software; the Free Software Foundation
5 # gives unlimited permission to copy and/or distribute it,
6 # with or without modifications, as long as this notice is preserved.
7
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
10 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 # PARTICULAR PURPOSE.
12
13 # Configure paths for GLIB
14 # Owen Taylor 97-11-3
15
16 dnl AM_PATH_GLIB([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
17 dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if "gmodule" or
18 dnl gthread is specified in MODULES, pass to glib-config
19 dnl
20 AC_DEFUN(AM_PATH_GLIB,
21 [dnl
22 dnl Get the cflags and libraries from the glib-config script
23 dnl
24 AC_ARG_WITH(glib-prefix,[ --with-glib-prefix=PFX Prefix where GLIB is installed (optional)],
25 glib_config_prefix="$withval", glib_config_prefix="")
26 AC_ARG_WITH(glib-exec-prefix,[ --with-glib-exec-prefix=PFX Exec prefix where GLIB is installed (optional)],
27 glib_config_exec_prefix="$withval", glib_config_exec_prefix="")
28 AC_ARG_ENABLE(glibtest, [ --disable-glibtest Do not try to compile and run a test GLIB program],
29 , enable_glibtest=yes)
30
31 if test x$glib_config_exec_prefix != x ; then
32 glib_config_args="$glib_config_args --exec-prefix=$glib_config_exec_prefix"
33 if test x${GLIB_CONFIG+set} != xset ; then
34 GLIB_CONFIG=$glib_config_exec_prefix/bin/glib-config
35 fi
36 fi
37 if test x$glib_config_prefix != x ; then
38 glib_config_args="$glib_config_args --prefix=$glib_config_prefix"
39 if test x${GLIB_CONFIG+set} != xset ; then
40 GLIB_CONFIG=$glib_config_prefix/bin/glib-config
41 fi
42 fi
43
44 for module in . $4
45 do
46 case "$module" in
47 gmodule)
48 glib_config_args="$glib_config_args gmodule"
49 ;;
50 gthread)
51 glib_config_args="$glib_config_args gthread"
52 ;;
53 esac
54 done
55
56 AC_PATH_PROG(GLIB_CONFIG, glib-config, no)
57 min_glib_version=ifelse([$1], ,0.99.7,$1)
58 AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
59 no_glib=""
60 if test "$GLIB_CONFIG" = "no" ; then
61 no_glib=yes
62 else
63 GLIB_CFLAGS=`$GLIB_CONFIG $glib_config_args --cflags`
64 GLIB_LIBS=`$GLIB_CONFIG $glib_config_args --libs`
65 glib_config_major_version=`$GLIB_CONFIG $glib_config_args --version | \
66 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
67 glib_config_minor_version=`$GLIB_CONFIG $glib_config_args --version | \
68 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
69 glib_config_micro_version=`$GLIB_CONFIG $glib_config_args --version | \
70 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
71 if test "x$enable_glibtest" = "xyes" ; then
72 ac_save_CFLAGS="$CFLAGS"
73 ac_save_LIBS="$LIBS"
74 CFLAGS="$CFLAGS $GLIB_CFLAGS"
75 LIBS="$GLIB_LIBS $LIBS"
76 dnl
77 dnl Now check if the installed GLIB is sufficiently new. (Also sanity
78 dnl checks the results of glib-config to some extent
79 dnl
80 rm -f conf.glibtest
81 AC_TRY_RUN([
82 #include <glib.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85
86 int
87 main ()
88 {
89 int major, minor, micro;
90 char *tmp_version;
91
92 system ("touch conf.glibtest");
93
94 /* HP/UX 9 (%@#!) writes to sscanf strings */
95 tmp_version = g_strdup("$min_glib_version");
96 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
97 printf("%s, bad version string\n", "$min_glib_version");
98 exit(1);
99 }
100
101 if ((glib_major_version != $glib_config_major_version) ||
102 (glib_minor_version != $glib_config_minor_version) ||
103 (glib_micro_version != $glib_config_micro_version))
104 {
105 printf("\n*** 'glib-config --version' returned %d.%d.%d, but GLIB (%d.%d.%d)\n",
106 $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
107 glib_major_version, glib_minor_version, glib_micro_version);
108 printf ("*** was found! If glib-config was correct, then it is best\n");
109 printf ("*** to remove the old version of GLIB. You may also be able to fix the error\n");
110 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
111 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
112 printf("*** required on your system.\n");
113 printf("*** If glib-config was wrong, set the environment variable GLIB_CONFIG\n");
114 printf("*** to point to the correct copy of glib-config, and remove the file config.cache\n");
115 printf("*** before re-running configure\n");
116 }
117 else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
118 (glib_minor_version != GLIB_MINOR_VERSION) ||
119 (glib_micro_version != GLIB_MICRO_VERSION))
120 {
121 printf("*** GLIB header files (version %d.%d.%d) do not match\n",
122 GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
123 printf("*** library (version %d.%d.%d)\n",
124 glib_major_version, glib_minor_version, glib_micro_version);
125 }
126 else
127 {
128 if ((glib_major_version > major) ||
129 ((glib_major_version == major) && (glib_minor_version > minor)) ||
130 ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
131 {
132 return 0;
133 }
134 else
135 {
136 printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n",
137 glib_major_version, glib_minor_version, glib_micro_version);
138 printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n",
139 major, minor, micro);
140 printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
141 printf("***\n");
142 printf("*** If you have already installed a sufficiently new version, this error\n");
143 printf("*** probably means that the wrong copy of the glib-config shell script is\n");
144 printf("*** being found. The easiest way to fix this is to remove the old version\n");
145 printf("*** of GLIB, but you can also set the GLIB_CONFIG environment to point to the\n");
146 printf("*** correct copy of glib-config. (In this case, you will have to\n");
147 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
148 printf("*** so that the correct libraries are found at run-time))\n");
149 }
150 }
151 return 1;
152 }
153 ],, no_glib=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
154 CFLAGS="$ac_save_CFLAGS"
155 LIBS="$ac_save_LIBS"
156 fi
157 fi
158 if test "x$no_glib" = x ; then
159 AC_MSG_RESULT(yes)
160 ifelse([$2], , :, [$2])
161 else
162 AC_MSG_RESULT(no)
163 if test "$GLIB_CONFIG" = "no" ; then
164 echo "*** The glib-config script installed by GLIB could not be found"
165 echo "*** If GLIB was installed in PREFIX, make sure PREFIX/bin is in"
166 echo "*** your path, or set the GLIB_CONFIG environment variable to the"
167 echo "*** full path to glib-config."
168 else
169 if test -f conf.glibtest ; then
170 :
171 else
172 echo "*** Could not run GLIB test program, checking why..."
173 CFLAGS="$CFLAGS $GLIB_CFLAGS"
174 LIBS="$LIBS $GLIB_LIBS"
175 AC_TRY_LINK([
176 #include <glib.h>
177 #include <stdio.h>
178 ], [ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ],
179 [ echo "*** The test program compiled, but did not run. This usually means"
180 echo "*** that the run-time linker is not finding GLIB or finding the wrong"
181 echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
182 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
183 echo "*** to the installed location Also, make sure you have run ldconfig if that"
184 echo "*** is required on your system"
185 echo "***"
186 echo "*** If you have an old version installed, it is best to remove it, although"
187 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
188 echo "***"
189 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
190 echo "*** came with the system with the command"
191 echo "***"
192 echo "*** rpm --erase --nodeps gtk gtk-devel" ],
193 [ echo "*** The test program failed to compile or link. See the file config.log for the"
194 echo "*** exact error that occured. This usually means GLIB was incorrectly installed"
195 echo "*** or that you have moved GLIB since it was installed. In the latter case, you"
196 echo "*** may want to edit the glib-config script: $GLIB_CONFIG" ])
197 CFLAGS="$ac_save_CFLAGS"
198 LIBS="$ac_save_LIBS"
199 fi
200 fi
201 GLIB_CFLAGS=""
202 GLIB_LIBS=""
203 ifelse([$3], , :, [$3])
204 fi
205 AC_SUBST(GLIB_CFLAGS)
206 AC_SUBST(GLIB_LIBS)
207 rm -f conf.glibtest
208 ])
209
210 # Configure paths for GTK+
211 # Owen Taylor 97-11-3
212
213 dnl AM_PATH_GTK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
214 dnl Test for GTK, and define GTK_CFLAGS and GTK_LIBS
215 dnl
216 AC_DEFUN(AM_PATH_GTK,
217 [dnl
218 dnl Get the cflags and libraries from the gtk-config script
219 dnl
220 AC_ARG_WITH(gtk-prefix,[ --with-gtk-prefix=PFX Prefix where GTK is installed (optional)],
221 gtk_config_prefix="$withval", gtk_config_prefix="")
222 AC_ARG_WITH(gtk-exec-prefix,[ --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)],
223 gtk_config_exec_prefix="$withval", gtk_config_exec_prefix="")
224 AC_ARG_ENABLE(gtktest, [ --disable-gtktest Do not try to compile and run a test GTK program],
225 , enable_gtktest=yes)
226
227 for module in . $4
228 do
229 case "$module" in
230 gthread)
231 gtk_config_args="$gtk_config_args gthread"
232 ;;
233 esac
234 done
235
236 if test x$gtk_config_exec_prefix != x ; then
237 gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
238 if test x${GTK_CONFIG+set} != xset ; then
239 GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
240 fi
241 fi
242 if test x$gtk_config_prefix != x ; then
243 gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
244 if test x${GTK_CONFIG+set} != xset ; then
245 GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
246 fi
247 fi
248
249 AC_PATH_PROG(GTK_CONFIG, gtk-config, no)
250 min_gtk_version=ifelse([$1], ,0.99.7,$1)
251 AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
252 no_gtk=""
253 if test "$GTK_CONFIG" = "no" ; then
254 no_gtk=yes
255 else
256 GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
257 GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
258 gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
259 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
260 gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
261 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
262 gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
263 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
264 if test "x$enable_gtktest" = "xyes" ; then
265 ac_save_CFLAGS="$CFLAGS"
266 ac_save_LIBS="$LIBS"
267 CFLAGS="$CFLAGS $GTK_CFLAGS"
268 LIBS="$GTK_LIBS $LIBS"
269 dnl
270 dnl Now check if the installed GTK is sufficiently new. (Also sanity
271 dnl checks the results of gtk-config to some extent
272 dnl
273 rm -f conf.gtktest
274 AC_TRY_RUN([
275 #include <gtk/gtk.h>
276 #include <stdio.h>
277 #include <stdlib.h>
278
279 int
280 main ()
281 {
282 int major, minor, micro;
283 char *tmp_version;
284
285 system ("touch conf.gtktest");
286
287 /* HP/UX 9 (%@#!) writes to sscanf strings */
288 tmp_version = g_strdup("$min_gtk_version");
289 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
290 printf("%s, bad version string\n", "$min_gtk_version");
291 exit(1);
292 }
293
294 if ((gtk_major_version != $gtk_config_major_version) ||
295 (gtk_minor_version != $gtk_config_minor_version) ||
296 (gtk_micro_version != $gtk_config_micro_version))
297 {
298 printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
299 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
300 gtk_major_version, gtk_minor_version, gtk_micro_version);
301 printf ("*** was found! If gtk-config was correct, then it is best\n");
302 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
303 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
304 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
305 printf("*** required on your system.\n");
306 printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
307 printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
308 printf("*** before re-running configure\n");
309 }
310 #if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
311 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
312 (gtk_minor_version != GTK_MINOR_VERSION) ||
313 (gtk_micro_version != GTK_MICRO_VERSION))
314 {
315 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
316 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
317 printf("*** library (version %d.%d.%d)\n",
318 gtk_major_version, gtk_minor_version, gtk_micro_version);
319 }
320 #endif /* defined (GTK_MAJOR_VERSION) ... */
321 else
322 {
323 if ((gtk_major_version > major) ||
324 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
325 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
326 {
327 return 0;
328 }
329 else
330 {
331 printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
332 gtk_major_version, gtk_minor_version, gtk_micro_version);
333 printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
334 major, minor, micro);
335 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
336 printf("***\n");
337 printf("*** If you have already installed a sufficiently new version, this error\n");
338 printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
339 printf("*** being found. The easiest way to fix this is to remove the old version\n");
340 printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
341 printf("*** correct copy of gtk-config. (In this case, you will have to\n");
342 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
343 printf("*** so that the correct libraries are found at run-time))\n");
344 }
345 }
346 return 1;
347 }
348 ],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
349 CFLAGS="$ac_save_CFLAGS"
350 LIBS="$ac_save_LIBS"
351 fi
352 fi
353 if test "x$no_gtk" = x ; then
354 AC_MSG_RESULT(yes)
355 ifelse([$2], , :, [$2])
356 else
357 AC_MSG_RESULT(no)
358 if test "$GTK_CONFIG" = "no" ; then
359 echo "*** The gtk-config script installed by GTK could not be found"
360 echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
361 echo "*** your path, or set the GTK_CONFIG environment variable to the"
362 echo "*** full path to gtk-config."
363 else
364 if test -f conf.gtktest ; then
365 :
366 else
367 echo "*** Could not run GTK test program, checking why..."
368 CFLAGS="$CFLAGS $GTK_CFLAGS"
369 LIBS="$LIBS $GTK_LIBS"
370 AC_TRY_LINK([
371 #include <gtk/gtk.h>
372 #include <stdio.h>
373 ], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
374 [ echo "*** The test program compiled, but did not run. This usually means"
375 echo "*** that the run-time linker is not finding GTK or finding the wrong"
376 echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
377 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
378 echo "*** to the installed location Also, make sure you have run ldconfig if that"
379 echo "*** is required on your system"
380 echo "***"
381 echo "*** If you have an old version installed, it is best to remove it, although"
382 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
383 echo "***"
384 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
385 echo "*** came with the system with the command"
386 echo "***"
387 echo "*** rpm --erase --nodeps gtk gtk-devel" ],
388 [ echo "*** The test program failed to compile or link. See the file config.log for the"
389 echo "*** exact error that occured. This usually means GTK was incorrectly installed"
390 echo "*** or that you have moved GTK since it was installed. In the latter case, you"
391 echo "*** may want to edit the gtk-config script: $GTK_CONFIG" ])
392 CFLAGS="$ac_save_CFLAGS"
393 LIBS="$ac_save_LIBS"
394 fi
395 fi
396 GTK_CFLAGS=""
397 GTK_LIBS=""
398 ifelse([$3], , :, [$3])
399 fi
400 AC_SUBST(GTK_CFLAGS)
401 AC_SUBST(GTK_LIBS)
402 rm -f conf.gtktest
403 ])
404
405 # Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
406 #
407 # This file is free software; the Free Software Foundation
408 # gives unlimited permission to copy and/or distribute it,
409 # with or without modifications, as long as this notice is preserved.
410
411 # AM_AUTOMAKE_VERSION(VERSION)
412 # ----------------------------
413 # Automake X.Y traces this macro to ensure aclocal.m4 has been
414 # generated from the m4 files accompanying Automake X.Y.
415 AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
416
417 # AM_SET_CURRENT_AUTOMAKE_VERSION
418 # -------------------------------
419 # Call AM_AUTOMAKE_VERSION so it can be traced.
420 # This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
421 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
422 [AM_AUTOMAKE_VERSION([1.9.5])])
423
424 # AM_AUX_DIR_EXPAND -*- Autoconf -*-
425
426 # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
427 #
428 # This file is free software; the Free Software Foundation
429 # gives unlimited permission to copy and/or distribute it,
430 # with or without modifications, as long as this notice is preserved.
431
432 # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
433 # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
434 # `$srcdir', `$srcdir/..', or `$srcdir/../..'.
435 #
436 # Of course, Automake must honor this variable whenever it calls a
437 # tool from the auxiliary directory. The problem is that $srcdir (and
438 # therefore $ac_aux_dir as well) can be either absolute or relative,
439 # depending on how configure is run. This is pretty annoying, since
440 # it makes $ac_aux_dir quite unusable in subdirectories: in the top
441 # source directory, any form will work fine, but in subdirectories a
442 # relative path needs to be adjusted first.
443 #
444 # $ac_aux_dir/missing
445 # fails when called from a subdirectory if $ac_aux_dir is relative
446 # $top_srcdir/$ac_aux_dir/missing
447 # fails if $ac_aux_dir is absolute,
448 # fails when called from a subdirectory in a VPATH build with
449 # a relative $ac_aux_dir
450 #
451 # The reason of the latter failure is that $top_srcdir and $ac_aux_dir
452 # are both prefixed by $srcdir. In an in-source build this is usually
453 # harmless because $srcdir is `.', but things will broke when you
454 # start a VPATH build or use an absolute $srcdir.
455 #
456 # So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
457 # iff we strip the leading $srcdir from $ac_aux_dir. That would be:
458 # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
459 # and then we would define $MISSING as
460 # MISSING="\${SHELL} $am_aux_dir/missing"
461 # This will work as long as MISSING is not called from configure, because
462 # unfortunately $(top_srcdir) has no meaning in configure.
463 # However there are other variables, like CC, which are often used in
464 # configure, and could therefore not use this "fixed" $ac_aux_dir.
465 #
466 # Another solution, used here, is to always expand $ac_aux_dir to an
467 # absolute PATH. The drawback is that using absolute paths prevent a
468 # configured tree to be moved without reconfiguration.
469
470 AC_DEFUN([AM_AUX_DIR_EXPAND],
471 [dnl Rely on autoconf to set up CDPATH properly.
472 AC_PREREQ([2.50])dnl
473 # expand $ac_aux_dir to an absolute path
474 am_aux_dir=`cd $ac_aux_dir && pwd`
475 ])
476
477
478 # Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2005
479 # Free Software Foundation, Inc.
480 #
481 # This file is free software; the Free Software Foundation
482 # gives unlimited permission to copy and/or distribute it,
483 # with or without modifications, as long as this notice is preserved.
484
485 # serial 4
486
487 # This was merged into AC_PROG_CC in Autoconf.
488
489 AU_DEFUN([AM_PROG_CC_STDC],
490 [AC_PROG_CC
491 AC_DIAGNOSE([obsolete], [$0:
492 your code should no longer depend upon `am_cv_prog_cc_stdc', but upon
493 `ac_cv_prog_cc_stdc'. Remove this warning and the assignment when
494 you adjust the code. You can also remove the above call to
495 AC_PROG_CC if you already called it elsewhere.])
496 am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
497 ])
498 AU_DEFUN([fp_PROG_CC_STDC])
499
500 # AM_CONDITIONAL -*- Autoconf -*-
501
502 # Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
503 # Free Software Foundation, Inc.
504 #
505 # This file is free software; the Free Software Foundation
506 # gives unlimited permission to copy and/or distribute it,
507 # with or without modifications, as long as this notice is preserved.
508
509 # serial 7
510
511 # AM_CONDITIONAL(NAME, SHELL-CONDITION)
512 # -------------------------------------
513 # Define a conditional.
514 AC_DEFUN([AM_CONDITIONAL],
515 [AC_PREREQ(2.52)dnl
516 ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
517 [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
518 AC_SUBST([$1_TRUE])
519 AC_SUBST([$1_FALSE])
520 if $2; then
521 $1_TRUE=
522 $1_FALSE='#'
523 else
524 $1_TRUE='#'
525 $1_FALSE=
526 fi
527 AC_CONFIG_COMMANDS_PRE(
528 [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
529 AC_MSG_ERROR([[conditional "$1" was never defined.
530 Usually this means the macro was only invoked conditionally.]])
531 fi])])
532
533
534 # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
535 # Free Software Foundation, Inc.
536 #
537 # This file is free software; the Free Software Foundation
538 # gives unlimited permission to copy and/or distribute it,
539 # with or without modifications, as long as this notice is preserved.
540
541 # serial 8
542
543 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
544 # written in clear, in which case automake, when reading aclocal.m4,
545 # will think it sees a *use*, and therefore will trigger all it's
546 # C support machinery. Also note that it means that autoscan, seeing
547 # CC etc. in the Makefile, will ask for an AC_PROG_CC use...
548
549
550 # _AM_DEPENDENCIES(NAME)
551 # ----------------------
552 # See how the compiler implements dependency checking.
553 # NAME is "CC", "CXX", "GCJ", or "OBJC".
554 # We try a few techniques and use that to set a single cache variable.
555 #
556 # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
557 # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
558 # dependency, and given that the user is not expected to run this macro,
559 # just rely on AC_PROG_CC.
560 AC_DEFUN([_AM_DEPENDENCIES],
561 [AC_REQUIRE([AM_SET_DEPDIR])dnl
562 AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
563 AC_REQUIRE([AM_MAKE_INCLUDE])dnl
564 AC_REQUIRE([AM_DEP_TRACK])dnl
565
566 ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
567 [$1], CXX, [depcc="$CXX" am_compiler_list=],
568 [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
569 [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
570 [depcc="$$1" am_compiler_list=])
571
572 AC_CACHE_CHECK([dependency style of $depcc],
573 [am_cv_$1_dependencies_compiler_type],
574 [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
575 # We make a subdir and do the tests there. Otherwise we can end up
576 # making bogus files that we don't know about and never remove. For
577 # instance it was reported that on HP-UX the gcc test will end up
578 # making a dummy file named `D' -- because `-MD' means `put the output
579 # in D'.
580 mkdir conftest.dir
581 # Copy depcomp to subdir because otherwise we won't find it if we're
582 # using a relative directory.
583 cp "$am_depcomp" conftest.dir
584 cd conftest.dir
585 # We will build objects and dependencies in a subdirectory because
586 # it helps to detect inapplicable dependency modes. For instance
587 # both Tru64's cc and ICC support -MD to output dependencies as a
588 # side effect of compilation, but ICC will put the dependencies in
589 # the current directory while Tru64 will put them in the object
590 # directory.
591 mkdir sub
592
593 am_cv_$1_dependencies_compiler_type=none
594 if test "$am_compiler_list" = ""; then
595 am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
596 fi
597 for depmode in $am_compiler_list; do
598 # Setup a source with many dependencies, because some compilers
599 # like to wrap large dependency lists on column 80 (with \), and
600 # we should not choose a depcomp mode which is confused by this.
601 #
602 # We need to recreate these files for each test, as the compiler may
603 # overwrite some of them when testing with obscure command lines.
604 # This happens at least with the AIX C compiler.
605 : > sub/conftest.c
606 for i in 1 2 3 4 5 6; do
607 echo '#include "conftst'$i'.h"' >> sub/conftest.c
608 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
609 # Solaris 8's {/usr,}/bin/sh.
610 touch sub/conftst$i.h
611 done
612 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
613
614 case $depmode in
615 nosideeffect)
616 # after this tag, mechanisms are not by side-effect, so they'll
617 # only be used when explicitly requested
618 if test "x$enable_dependency_tracking" = xyes; then
619 continue
620 else
621 break
622 fi
623 ;;
624 none) break ;;
625 esac
626 # We check with `-c' and `-o' for the sake of the "dashmstdout"
627 # mode. It turns out that the SunPro C++ compiler does not properly
628 # handle `-M -o', and we need to detect this.
629 if depmode=$depmode \
630 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
631 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
632 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
633 >/dev/null 2>conftest.err &&
634 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
635 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
636 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
637 # icc doesn't choke on unknown options, it will just issue warnings
638 # or remarks (even with -Werror). So we grep stderr for any message
639 # that says an option was ignored or not supported.
640 # When given -MP, icc 7.0 and 7.1 complain thusly:
641 # icc: Command line warning: ignoring option '-M'; no argument required
642 # The diagnosis changed in icc 8.0:
643 # icc: Command line remark: option '-MP' not supported
644 if (grep 'ignoring option' conftest.err ||
645 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
646 am_cv_$1_dependencies_compiler_type=$depmode
647 break
648 fi
649 fi
650 done
651
652 cd ..
653 rm -rf conftest.dir
654 else
655 am_cv_$1_dependencies_compiler_type=none
656 fi
657 ])
658 AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
659 AM_CONDITIONAL([am__fastdep$1], [
660 test "x$enable_dependency_tracking" != xno \
661 && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
662 ])
663
664
665 # AM_SET_DEPDIR
666 # -------------
667 # Choose a directory name for dependency files.
668 # This macro is AC_REQUIREd in _AM_DEPENDENCIES
669 AC_DEFUN([AM_SET_DEPDIR],
670 [AC_REQUIRE([AM_SET_LEADING_DOT])dnl
671 AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
672 ])
673
674
675 # AM_DEP_TRACK
676 # ------------
677 AC_DEFUN([AM_DEP_TRACK],
678 [AC_ARG_ENABLE(dependency-tracking,
679 [ --disable-dependency-tracking speeds up one-time build
680 --enable-dependency-tracking do not reject slow dependency extractors])
681 if test "x$enable_dependency_tracking" != xno; then
682 am_depcomp="$ac_aux_dir/depcomp"
683 AMDEPBACKSLASH='\'
684 fi
685 AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
686 AC_SUBST([AMDEPBACKSLASH])
687 ])
688
689 # Generate code to set up dependency tracking. -*- Autoconf -*-
690
691 # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
692 # Free Software Foundation, Inc.
693 #
694 # This file is free software; the Free Software Foundation
695 # gives unlimited permission to copy and/or distribute it,
696 # with or without modifications, as long as this notice is preserved.
697
698 #serial 3
699
700 # _AM_OUTPUT_DEPENDENCY_COMMANDS
701 # ------------------------------
702 AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
703 [for mf in $CONFIG_FILES; do
704 # Strip MF so we end up with the name of the file.
705 mf=`echo "$mf" | sed -e 's/:.*$//'`
706 # Check whether this is an Automake generated Makefile or not.
707 # We used to match only the files named `Makefile.in', but
708 # some people rename them; so instead we look at the file content.
709 # Grep'ing the first line is not enough: some people post-process
710 # each Makefile.in and add a new line on top of each file to say so.
711 # So let's grep whole file.
712 if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
713 dirpart=`AS_DIRNAME("$mf")`
714 else
715 continue
716 fi
717 # Extract the definition of DEPDIR, am__include, and am__quote
718 # from the Makefile without running `make'.
719 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
720 test -z "$DEPDIR" && continue
721 am__include=`sed -n 's/^am__include = //p' < "$mf"`
722 test -z "am__include" && continue
723 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
724 # When using ansi2knr, U may be empty or an underscore; expand it
725 U=`sed -n 's/^U = //p' < "$mf"`
726 # Find all dependency output files, they are included files with
727 # $(DEPDIR) in their names. We invoke sed twice because it is the
728 # simplest approach to changing $(DEPDIR) to its actual value in the
729 # expansion.
730 for file in `sed -n "
731 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
732 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
733 # Make sure the directory exists.
734 test -f "$dirpart/$file" && continue
735 fdir=`AS_DIRNAME(["$file"])`
736 AS_MKDIR_P([$dirpart/$fdir])
737 # echo "creating $dirpart/$file"
738 echo '# dummy' > "$dirpart/$file"
739 done
740 done
741 ])# _AM_OUTPUT_DEPENDENCY_COMMANDS
742
743
744 # AM_OUTPUT_DEPENDENCY_COMMANDS
745 # -----------------------------
746 # This macro should only be invoked once -- use via AC_REQUIRE.
747 #
748 # This code is only required when automatic dependency tracking
749 # is enabled. FIXME. This creates each `.P' file that we will
750 # need in order to bootstrap the dependency handling code.
751 AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
752 [AC_CONFIG_COMMANDS([depfiles],
753 [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
754 [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
755 ])
756
757 # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
758 # Free Software Foundation, Inc.
759 #
760 # This file is free software; the Free Software Foundation
761 # gives unlimited permission to copy and/or distribute it,
762 # with or without modifications, as long as this notice is preserved.
763
764 # serial 8
765
766 # AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS.
767 AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
768
769 # Do all the work for Automake. -*- Autoconf -*-
770
771 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
772 # Free Software Foundation, Inc.
773 #
774 # This file is free software; the Free Software Foundation
775 # gives unlimited permission to copy and/or distribute it,
776 # with or without modifications, as long as this notice is preserved.
777
778 # serial 12
779
780 # This macro actually does too much. Some checks are only needed if
781 # your package does certain things. But this isn't really a big deal.
782
783 # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
784 # AM_INIT_AUTOMAKE([OPTIONS])
785 # -----------------------------------------------
786 # The call with PACKAGE and VERSION arguments is the old style
787 # call (pre autoconf-2.50), which is being phased out. PACKAGE
788 # and VERSION should now be passed to AC_INIT and removed from
789 # the call to AM_INIT_AUTOMAKE.
790 # We support both call styles for the transition. After
791 # the next Automake release, Autoconf can make the AC_INIT
792 # arguments mandatory, and then we can depend on a new Autoconf
793 # release and drop the old call support.
794 AC_DEFUN([AM_INIT_AUTOMAKE],
795 [AC_PREREQ([2.58])dnl
796 dnl Autoconf wants to disallow AM_ names. We explicitly allow
797 dnl the ones we care about.
798 m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
799 AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
800 AC_REQUIRE([AC_PROG_INSTALL])dnl
801 # test to see if srcdir already configured
802 if test "`cd $srcdir && pwd`" != "`pwd`" &&
803 test -f $srcdir/config.status; then
804 AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
805 fi
806
807 # test whether we have cygpath
808 if test -z "$CYGPATH_W"; then
809 if (cygpath --version) >/dev/null 2>/dev/null; then
810 CYGPATH_W='cygpath -w'
811 else
812 CYGPATH_W=echo
813 fi
814 fi
815 AC_SUBST([CYGPATH_W])
816
817 # Define the identity of the package.
818 dnl Distinguish between old-style and new-style calls.
819 m4_ifval([$2],
820 [m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
821 AC_SUBST([PACKAGE], [$1])dnl
822 AC_SUBST([VERSION], [$2])],
823 [_AM_SET_OPTIONS([$1])dnl
824 AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
825 AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
826
827 _AM_IF_OPTION([no-define],,
828 [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
829 AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
830
831 # Some tools Automake needs.
832 AC_REQUIRE([AM_SANITY_CHECK])dnl
833 AC_REQUIRE([AC_ARG_PROGRAM])dnl
834 AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
835 AM_MISSING_PROG(AUTOCONF, autoconf)
836 AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
837 AM_MISSING_PROG(AUTOHEADER, autoheader)
838 AM_MISSING_PROG(MAKEINFO, makeinfo)
839 AM_PROG_INSTALL_SH
840 AM_PROG_INSTALL_STRIP
841 AC_REQUIRE([AM_PROG_MKDIR_P])dnl
842 # We need awk for the "check" target. The system "awk" is bad on
843 # some platforms.
844 AC_REQUIRE([AC_PROG_AWK])dnl
845 AC_REQUIRE([AC_PROG_MAKE_SET])dnl
846 AC_REQUIRE([AM_SET_LEADING_DOT])dnl
847 _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
848 [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
849 [_AM_PROG_TAR([v7])])])
850 _AM_IF_OPTION([no-dependencies],,
851 [AC_PROVIDE_IFELSE([AC_PROG_CC],
852 [_AM_DEPENDENCIES(CC)],
853 [define([AC_PROG_CC],
854 defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
855 AC_PROVIDE_IFELSE([AC_PROG_CXX],
856 [_AM_DEPENDENCIES(CXX)],
857 [define([AC_PROG_CXX],
858 defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
859 ])
860 ])
861
862
863 # When config.status generates a header, we must update the stamp-h file.
864 # This file resides in the same directory as the config header
865 # that is generated. The stamp files are numbered to have different names.
866
867 # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
868 # loop where config.status creates the headers, so we can generate
869 # our stamp files there.
870 AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
871 [# Compute $1's index in $config_headers.
872 _am_stamp_count=1
873 for _am_header in $config_headers :; do
874 case $_am_header in
875 $1 | $1:* )
876 break ;;
877 * )
878 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
879 esac
880 done
881 echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
882
883 # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
884 #
885 # This file is free software; the Free Software Foundation
886 # gives unlimited permission to copy and/or distribute it,
887 # with or without modifications, as long as this notice is preserved.
888
889 # AM_PROG_INSTALL_SH
890 # ------------------
891 # Define $install_sh.
892 AC_DEFUN([AM_PROG_INSTALL_SH],
893 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
894 install_sh=${install_sh-"$am_aux_dir/install-sh"}
895 AC_SUBST(install_sh)])
896
897 # Copyright (C) 2003, 2005 Free Software Foundation, Inc.
898 #
899 # This file is free software; the Free Software Foundation
900 # gives unlimited permission to copy and/or distribute it,
901 # with or without modifications, as long as this notice is preserved.
902
903 # serial 2
904
905 # Check whether the underlying file-system supports filenames
906 # with a leading dot. For instance MS-DOS doesn't.
907 AC_DEFUN([AM_SET_LEADING_DOT],
908 [rm -rf .tst 2>/dev/null
909 mkdir .tst 2>/dev/null
910 if test -d .tst; then
911 am__leading_dot=.
912 else
913 am__leading_dot=_
914 fi
915 rmdir .tst 2>/dev/null
916 AC_SUBST([am__leading_dot])])
917
918 # Check to see how 'make' treats includes. -*- Autoconf -*-
919
920 # Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
921 #
922 # This file is free software; the Free Software Foundation
923 # gives unlimited permission to copy and/or distribute it,
924 # with or without modifications, as long as this notice is preserved.
925
926 # serial 3
927
928 # AM_MAKE_INCLUDE()
929 # -----------------
930 # Check to see how make treats includes.
931 AC_DEFUN([AM_MAKE_INCLUDE],
932 [am_make=${MAKE-make}
933 cat > confinc << 'END'
934 am__doit:
935 @echo done
936 .PHONY: am__doit
937 END
938 # If we don't find an include directive, just comment out the code.
939 AC_MSG_CHECKING([for style of include used by $am_make])
940 am__include="#"
941 am__quote=
942 _am_result=none
943 # First try GNU make style include.
944 echo "include confinc" > confmf
945 # We grep out `Entering directory' and `Leaving directory'
946 # messages which can occur if `w' ends up in MAKEFLAGS.
947 # In particular we don't look at `^make:' because GNU make might
948 # be invoked under some other name (usually "gmake"), in which
949 # case it prints its new name instead of `make'.
950 if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
951 am__include=include
952 am__quote=
953 _am_result=GNU
954 fi
955 # Now try BSD make style include.
956 if test "$am__include" = "#"; then
957 echo '.include "confinc"' > confmf
958 if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
959 am__include=.include
960 am__quote="\""
961 _am_result=BSD
962 fi
963 fi
964 AC_SUBST([am__include])
965 AC_SUBST([am__quote])
966 AC_MSG_RESULT([$_am_result])
967 rm -f confinc confmf
968 ])
969
970 # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
971
972 # Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
973 # Free Software Foundation, Inc.
974 #
975 # This file is free software; the Free Software Foundation
976 # gives unlimited permission to copy and/or distribute it,
977 # with or without modifications, as long as this notice is preserved.
978
979 # serial 4
980
981 # AM_MISSING_PROG(NAME, PROGRAM)
982 # ------------------------------
983 AC_DEFUN([AM_MISSING_PROG],
984 [AC_REQUIRE([AM_MISSING_HAS_RUN])
985 $1=${$1-"${am_missing_run}$2"}
986 AC_SUBST($1)])
987
988
989 # AM_MISSING_HAS_RUN
990 # ------------------
991 # Define MISSING if not defined so far and test if it supports --run.
992 # If it does, set am_missing_run to use it, otherwise, to nothing.
993 AC_DEFUN([AM_MISSING_HAS_RUN],
994 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
995 test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
996 # Use eval to expand $SHELL
997 if eval "$MISSING --run true"; then
998 am_missing_run="$MISSING --run "
999 else
1000 am_missing_run=
1001 AC_MSG_WARN([`missing' script is too old or missing])
1002 fi
1003 ])
1004
1005 # Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
1006 #
1007 # This file is free software; the Free Software Foundation
1008 # gives unlimited permission to copy and/or distribute it,
1009 # with or without modifications, as long as this notice is preserved.
1010
1011 # AM_PROG_MKDIR_P
1012 # ---------------
1013 # Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
1014 #
1015 # Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
1016 # created by `make install' are always world readable, even if the
1017 # installer happens to have an overly restrictive umask (e.g. 077).
1018 # This was a mistake. There are at least two reasons why we must not
1019 # use `-m 0755':
1020 # - it causes special bits like SGID to be ignored,
1021 # - it may be too restrictive (some setups expect 775 directories).
1022 #
1023 # Do not use -m 0755 and let people choose whatever they expect by
1024 # setting umask.
1025 #
1026 # We cannot accept any implementation of `mkdir' that recognizes `-p'.
1027 # Some implementations (such as Solaris 8's) are not thread-safe: if a
1028 # parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
1029 # concurrently, both version can detect that a/ is missing, but only
1030 # one can create it and the other will error out. Consequently we
1031 # restrict ourselves to GNU make (using the --version option ensures
1032 # this.)
1033 AC_DEFUN([AM_PROG_MKDIR_P],
1034 [if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
1035 # We used to keeping the `.' as first argument, in order to
1036 # allow $(mkdir_p) to be used without argument. As in
1037 # $(mkdir_p) $(somedir)
1038 # where $(somedir) is conditionally defined. However this is wrong
1039 # for two reasons:
1040 # 1. if the package is installed by a user who cannot write `.'
1041 # make install will fail,
1042 # 2. the above comment should most certainly read
1043 # $(mkdir_p) $(DESTDIR)$(somedir)
1044 # so it does not work when $(somedir) is undefined and
1045 # $(DESTDIR) is not.
1046 # To support the latter case, we have to write
1047 # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
1048 # so the `.' trick is pointless.
1049 mkdir_p='mkdir -p --'
1050 else
1051 # On NextStep and OpenStep, the `mkdir' command does not
1052 # recognize any option. It will interpret all options as
1053 # directories to create, and then abort because `.' already
1054 # exists.
1055 for d in ./-p ./--version;
1056 do
1057 test -d $d && rmdir $d
1058 done
1059 # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
1060 if test -f "$ac_aux_dir/mkinstalldirs"; then
1061 mkdir_p='$(mkinstalldirs)'
1062 else
1063 mkdir_p='$(install_sh) -d'
1064 fi
1065 fi
1066 AC_SUBST([mkdir_p])])
1067
1068 # Helper functions for option handling. -*- Autoconf -*-
1069
1070 # Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
1071 #
1072 # This file is free software; the Free Software Foundation
1073 # gives unlimited permission to copy and/or distribute it,
1074 # with or without modifications, as long as this notice is preserved.
1075
1076 # serial 3
1077
1078 # _AM_MANGLE_OPTION(NAME)
1079 # -----------------------
1080 AC_DEFUN([_AM_MANGLE_OPTION],
1081 [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
1082
1083 # _AM_SET_OPTION(NAME)
1084 # ------------------------------
1085 # Set option NAME. Presently that only means defining a flag for this option.
1086 AC_DEFUN([_AM_SET_OPTION],
1087 [m4_define(_AM_MANGLE_OPTION([$1]), 1)])
1088
1089 # _AM_SET_OPTIONS(OPTIONS)
1090 # ----------------------------------
1091 # OPTIONS is a space-separated list of Automake options.
1092 AC_DEFUN([_AM_SET_OPTIONS],
1093 [AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
1094
1095 # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
1096 # -------------------------------------------
1097 # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
1098 AC_DEFUN([_AM_IF_OPTION],
1099 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
1100
1101 # Check to make sure that the build environment is sane. -*- Autoconf -*-
1102
1103 # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
1104 # Free Software Foundation, Inc.
1105 #
1106 # This file is free software; the Free Software Foundation
1107 # gives unlimited permission to copy and/or distribute it,
1108 # with or without modifications, as long as this notice is preserved.
1109
1110 # serial 4
1111
1112 # AM_SANITY_CHECK
1113 # ---------------
1114 AC_DEFUN([AM_SANITY_CHECK],
1115 [AC_MSG_CHECKING([whether build environment is sane])
1116 # Just in case
1117 sleep 1
1118 echo timestamp > conftest.file
1119 # Do `set' in a subshell so we don't clobber the current shell's
1120 # arguments. Must try -L first in case configure is actually a
1121 # symlink; some systems play weird games with the mod time of symlinks
1122 # (eg FreeBSD returns the mod time of the symlink's containing
1123 # directory).
1124 if (
1125 set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
1126 if test "$[*]" = "X"; then
1127 # -L didn't work.
1128 set X `ls -t $srcdir/configure conftest.file`
1129 fi
1130 rm -f conftest.file
1131 if test "$[*]" != "X $srcdir/configure conftest.file" \
1132 && test "$[*]" != "X conftest.file $srcdir/configure"; then
1133
1134 # If neither matched, then we have a broken ls. This can happen
1135 # if, for instance, CONFIG_SHELL is bash and it inherits a
1136 # broken ls alias from the environment. This has actually
1137 # happened. Such a system could not be considered "sane".
1138 AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
1139 alias in your environment])
1140 fi
1141
1142 test "$[2]" = conftest.file
1143 )
1144 then
1145 # Ok.
1146 :
1147 else
1148 AC_MSG_ERROR([newly created file is older than distributed files!
1149 Check your system clock])
1150 fi
1151 AC_MSG_RESULT(yes)])
1152
1153 # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
1154 #
1155 # This file is free software; the Free Software Foundation
1156 # gives unlimited permission to copy and/or distribute it,
1157 # with or without modifications, as long as this notice is preserved.
1158
1159 # AM_PROG_INSTALL_STRIP
1160 # ---------------------
1161 # One issue with vendor `install' (even GNU) is that you can't
1162 # specify the program used to strip binaries. This is especially
1163 # annoying in cross-compiling environments, where the build's strip
1164 # is unlikely to handle the host's binaries.
1165 # Fortunately install-sh will honor a STRIPPROG variable, so we
1166 # always use install-sh in `make install-strip', and initialize
1167 # STRIPPROG with the value of the STRIP variable (set by the user).
1168 AC_DEFUN([AM_PROG_INSTALL_STRIP],
1169 [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
1170 # Installed binaries are usually stripped using `strip' when the user
1171 # run `make install-strip'. However `strip' might not be the right
1172 # tool to use in cross-compilation environments, therefore Automake
1173 # will honor the `STRIP' environment variable to overrule this program.
1174 dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
1175 if test "$cross_compiling" != no; then
1176 AC_CHECK_TOOL([STRIP], [strip], :)
1177 fi
1178 INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
1179 AC_SUBST([INSTALL_STRIP_PROGRAM])])
1180
1181 # Check how to create a tarball. -*- Autoconf -*-
1182
1183 # Copyright (C) 2004, 2005 Free Software Foundation, Inc.
1184 #
1185 # This file is free software; the Free Software Foundation
1186 # gives unlimited permission to copy and/or distribute it,
1187 # with or without modifications, as long as this notice is preserved.
1188
1189 # serial 2
1190
1191 # _AM_PROG_TAR(FORMAT)
1192 # --------------------
1193 # Check how to create a tarball in format FORMAT.
1194 # FORMAT should be one of `v7', `ustar', or `pax'.
1195 #
1196 # Substitute a variable $(am__tar) that is a command
1197 # writing to stdout a FORMAT-tarball containing the directory
1198 # $tardir.
1199 # tardir=directory && $(am__tar) > result.tar
1200 #
1201 # Substitute a variable $(am__untar) that extract such
1202 # a tarball read from stdin.
1203 # $(am__untar) < result.tar
1204 AC_DEFUN([_AM_PROG_TAR],
1205 [# Always define AMTAR for backward compatibility.
1206 AM_MISSING_PROG([AMTAR], [tar])
1207 m4_if([$1], [v7],
1208 [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
1209 [m4_case([$1], [ustar],, [pax],,
1210 [m4_fatal([Unknown tar format])])
1211 AC_MSG_CHECKING([how to create a $1 tar archive])
1212 # Loop over all known methods to create a tar archive until one works.
1213 _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
1214 _am_tools=${am_cv_prog_tar_$1-$_am_tools}
1215 # Do not fold the above two line into one, because Tru64 sh and
1216 # Solaris sh will not grok spaces in the rhs of `-'.
1217 for _am_tool in $_am_tools
1218 do
1219 case $_am_tool in
1220 gnutar)
1221 for _am_tar in tar gnutar gtar;
1222 do
1223 AM_RUN_LOG([$_am_tar --version]) && break
1224 done
1225 am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
1226 am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
1227 am__untar="$_am_tar -xf -"
1228 ;;
1229 plaintar)
1230 # Must skip GNU tar: if it does not support --format= it doesn't create
1231 # ustar tarball either.
1232 (tar --version) >/dev/null 2>&1 && continue
1233 am__tar='tar chf - "$$tardir"'
1234 am__tar_='tar chf - "$tardir"'
1235 am__untar='tar xf -'
1236 ;;
1237 pax)
1238 am__tar='pax -L -x $1 -w "$$tardir"'
1239 am__tar_='pax -L -x $1 -w "$tardir"'
1240 am__untar='pax -r'
1241 ;;
1242 cpio)
1243 am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
1244 am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
1245 am__untar='cpio -i -H $1 -d'
1246 ;;
1247 none)
1248 am__tar=false
1249 am__tar_=false
1250 am__untar=false
1251 ;;
1252 esac
1253
1254 # If the value was cached, stop now. We just wanted to have am__tar
1255 # and am__untar set.
1256 test -n "${am_cv_prog_tar_$1}" && break
1257
1258 # tar/untar a dummy directory, and stop if the command works
1259 rm -rf conftest.dir
1260 mkdir conftest.dir
1261 echo GrepMe > conftest.dir/file
1262 AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
1263 rm -rf conftest.dir
1264 if test -s conftest.tar; then
1265 AM_RUN_LOG([$am__untar <conftest.tar])
1266 grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
1267 fi
1268 done
1269 rm -rf conftest.dir
1270
1271 AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
1272 AC_MSG_RESULT([$am_cv_prog_tar_$1])])
1273 AC_SUBST([am__tar])
1274 AC_SUBST([am__untar])
1275 ]) # _AM_PROG_TAR
1276
0 #! /bin/sh
1 # Attempt to guess a canonical system name.
2 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
3 # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4
5 timestamp='2005-02-10'
6
7 # This file is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #
21 # As a special exception to the GNU General Public License, if you
22 # distribute this file as part of a program that contains a
23 # configuration script generated by Autoconf, you may include it under
24 # the same distribution terms that you use for the rest of that program.
25
26 # Originally written by Per Bothner <per@bothner.com>.
27 # Please send patches to <config-patches@gnu.org>. Submit a context
28 # diff and a properly formatted ChangeLog entry.
29 #
30 # This script attempts to guess a canonical system name similar to
31 # config.sub. If it succeeds, it prints the system name on stdout, and
32 # exits with 0. Otherwise, it exits with 1.
33 #
34 # The plan is that this can be called by configure scripts if you
35 # don't specify an explicit build system type.
36
37 me=`echo "$0" | sed -e 's,.*/,,'`
38
39 usage="\
40 Usage: $0 [OPTION]
41
42 Output the configuration name of the system \`$me' is run on.
43
44 Operation modes:
45 -h, --help print this help, then exit
46 -t, --time-stamp print date of last modification, then exit
47 -v, --version print version number, then exit
48
49 Report bugs and patches to <config-patches@gnu.org>."
50
51 version="\
52 GNU config.guess ($timestamp)
53
54 Originally written by Per Bothner.
55 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
56 Free Software Foundation, Inc.
57
58 This is free software; see the source for copying conditions. There is NO
59 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
60
61 help="
62 Try \`$me --help' for more information."
63
64 # Parse command line
65 while test $# -gt 0 ; do
66 case $1 in
67 --time-stamp | --time* | -t )
68 echo "$timestamp" ; exit 0 ;;
69 --version | -v )
70 echo "$version" ; exit 0 ;;
71 --help | --h* | -h )
72 echo "$usage"; exit 0 ;;
73 -- ) # Stop option processing
74 shift; break ;;
75 - ) # Use stdin as input.
76 break ;;
77 -* )
78 echo "$me: invalid option $1$help" >&2
79 exit 1 ;;
80 * )
81 break ;;
82 esac
83 done
84
85 if test $# != 0; then
86 echo "$me: too many arguments$help" >&2
87 exit 1
88 fi
89
90 trap 'exit 1' 1 2 15
91
92 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a
93 # compiler to aid in system detection is discouraged as it requires
94 # temporary files to be created and, as you can see below, it is a
95 # headache to deal with in a portable fashion.
96
97 # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
98 # use `HOST_CC' if defined, but it is deprecated.
99
100 # Portable tmp directory creation inspired by the Autoconf team.
101
102 set_cc_for_build='
103 trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
104 trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
105 : ${TMPDIR=/tmp} ;
106 { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
107 { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
108 { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
109 { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
110 dummy=$tmp/dummy ;
111 tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
112 case $CC_FOR_BUILD,$HOST_CC,$CC in
113 ,,) echo "int x;" > $dummy.c ;
114 for c in cc gcc c89 c99 ; do
115 if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
116 CC_FOR_BUILD="$c"; break ;
117 fi ;
118 done ;
119 if test x"$CC_FOR_BUILD" = x ; then
120 CC_FOR_BUILD=no_compiler_found ;
121 fi
122 ;;
123 ,,*) CC_FOR_BUILD=$CC ;;
124 ,*,*) CC_FOR_BUILD=$HOST_CC ;;
125 esac ;'
126
127 # This is needed to find uname on a Pyramid OSx when run in the BSD universe.
128 # (ghazi@noc.rutgers.edu 1994-08-24)
129 if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
130 PATH=$PATH:/.attbin ; export PATH
131 fi
132
133 UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
134 UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
135 UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
136 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
137
138 # Note: order is significant - the case branches are not exclusive.
139
140 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
141 *:NetBSD:*:*)
142 # NetBSD (nbsd) targets should (where applicable) match one or
143 # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
144 # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
145 # switched to ELF, *-*-netbsd* would select the old
146 # object file format. This provides both forward
147 # compatibility and a consistent mechanism for selecting the
148 # object file format.
149 #
150 # Note: NetBSD doesn't particularly care about the vendor
151 # portion of the name. We always set it to "unknown".
152 sysctl="sysctl -n hw.machine_arch"
153 UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
154 /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
155 case "${UNAME_MACHINE_ARCH}" in
156 armeb) machine=armeb-unknown ;;
157 arm*) machine=arm-unknown ;;
158 sh3el) machine=shl-unknown ;;
159 sh3eb) machine=sh-unknown ;;
160 *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
161 esac
162 # The Operating System including object format, if it has switched
163 # to ELF recently, or will in the future.
164 case "${UNAME_MACHINE_ARCH}" in
165 arm*|i386|m68k|ns32k|sh3*|sparc|vax)
166 eval $set_cc_for_build
167 if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
168 | grep __ELF__ >/dev/null
169 then
170 # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
171 # Return netbsd for either. FIX?
172 os=netbsd
173 else
174 os=netbsdelf
175 fi
176 ;;
177 *)
178 os=netbsd
179 ;;
180 esac
181 # The OS release
182 # Debian GNU/NetBSD machines have a different userland, and
183 # thus, need a distinct triplet. However, they do not need
184 # kernel version information, so it can be replaced with a
185 # suitable tag, in the style of linux-gnu.
186 case "${UNAME_VERSION}" in
187 Debian*)
188 release='-gnu'
189 ;;
190 *)
191 release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
192 ;;
193 esac
194 # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
195 # contains redundant information, the shorter form:
196 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
197 echo "${machine}-${os}${release}"
198 exit 0 ;;
199 amd64:OpenBSD:*:*)
200 echo x86_64-unknown-openbsd${UNAME_RELEASE}
201 exit 0 ;;
202 amiga:OpenBSD:*:*)
203 echo m68k-unknown-openbsd${UNAME_RELEASE}
204 exit 0 ;;
205 cats:OpenBSD:*:*)
206 echo arm-unknown-openbsd${UNAME_RELEASE}
207 exit 0 ;;
208 hp300:OpenBSD:*:*)
209 echo m68k-unknown-openbsd${UNAME_RELEASE}
210 exit 0 ;;
211 luna88k:OpenBSD:*:*)
212 echo m88k-unknown-openbsd${UNAME_RELEASE}
213 exit 0 ;;
214 mac68k:OpenBSD:*:*)
215 echo m68k-unknown-openbsd${UNAME_RELEASE}
216 exit 0 ;;
217 macppc:OpenBSD:*:*)
218 echo powerpc-unknown-openbsd${UNAME_RELEASE}
219 exit 0 ;;
220 mvme68k:OpenBSD:*:*)
221 echo m68k-unknown-openbsd${UNAME_RELEASE}
222 exit 0 ;;
223 mvme88k:OpenBSD:*:*)
224 echo m88k-unknown-openbsd${UNAME_RELEASE}
225 exit 0 ;;
226 mvmeppc:OpenBSD:*:*)
227 echo powerpc-unknown-openbsd${UNAME_RELEASE}
228 exit 0 ;;
229 sgi:OpenBSD:*:*)
230 echo mips64-unknown-openbsd${UNAME_RELEASE}
231 exit 0 ;;
232 sun3:OpenBSD:*:*)
233 echo m68k-unknown-openbsd${UNAME_RELEASE}
234 exit 0 ;;
235 *:OpenBSD:*:*)
236 echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
237 exit 0 ;;
238 *:ekkoBSD:*:*)
239 echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
240 exit 0 ;;
241 macppc:MirBSD:*:*)
242 echo powerppc-unknown-mirbsd${UNAME_RELEASE}
243 exit 0 ;;
244 *:MirBSD:*:*)
245 echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
246 exit 0 ;;
247 alpha:OSF1:*:*)
248 case $UNAME_RELEASE in
249 *4.0)
250 UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
251 ;;
252 *5.*)
253 UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
254 ;;
255 esac
256 # According to Compaq, /usr/sbin/psrinfo has been available on
257 # OSF/1 and Tru64 systems produced since 1995. I hope that
258 # covers most systems running today. This code pipes the CPU
259 # types through head -n 1, so we only detect the type of CPU 0.
260 ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
261 case "$ALPHA_CPU_TYPE" in
262 "EV4 (21064)")
263 UNAME_MACHINE="alpha" ;;
264 "EV4.5 (21064)")
265 UNAME_MACHINE="alpha" ;;
266 "LCA4 (21066/21068)")
267 UNAME_MACHINE="alpha" ;;
268 "EV5 (21164)")
269 UNAME_MACHINE="alphaev5" ;;
270 "EV5.6 (21164A)")
271 UNAME_MACHINE="alphaev56" ;;
272 "EV5.6 (21164PC)")
273 UNAME_MACHINE="alphapca56" ;;
274 "EV5.7 (21164PC)")
275 UNAME_MACHINE="alphapca57" ;;
276 "EV6 (21264)")
277 UNAME_MACHINE="alphaev6" ;;
278 "EV6.7 (21264A)")
279 UNAME_MACHINE="alphaev67" ;;
280 "EV6.8CB (21264C)")
281 UNAME_MACHINE="alphaev68" ;;
282 "EV6.8AL (21264B)")
283 UNAME_MACHINE="alphaev68" ;;
284 "EV6.8CX (21264D)")
285 UNAME_MACHINE="alphaev68" ;;
286 "EV6.9A (21264/EV69A)")
287 UNAME_MACHINE="alphaev69" ;;
288 "EV7 (21364)")
289 UNAME_MACHINE="alphaev7" ;;
290 "EV7.9 (21364A)")
291 UNAME_MACHINE="alphaev79" ;;
292 esac
293 # A Pn.n version is a patched version.
294 # A Vn.n version is a released version.
295 # A Tn.n version is a released field test version.
296 # A Xn.n version is an unreleased experimental baselevel.
297 # 1.2 uses "1.2" for uname -r.
298 echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
299 exit 0 ;;
300 Alpha\ *:Windows_NT*:*)
301 # How do we know it's Interix rather than the generic POSIX subsystem?
302 # Should we change UNAME_MACHINE based on the output of uname instead
303 # of the specific Alpha model?
304 echo alpha-pc-interix
305 exit 0 ;;
306 21064:Windows_NT:50:3)
307 echo alpha-dec-winnt3.5
308 exit 0 ;;
309 Amiga*:UNIX_System_V:4.0:*)
310 echo m68k-unknown-sysv4
311 exit 0;;
312 *:[Aa]miga[Oo][Ss]:*:*)
313 echo ${UNAME_MACHINE}-unknown-amigaos
314 exit 0 ;;
315 *:[Mm]orph[Oo][Ss]:*:*)
316 echo ${UNAME_MACHINE}-unknown-morphos
317 exit 0 ;;
318 *:OS/390:*:*)
319 echo i370-ibm-openedition
320 exit 0 ;;
321 *:z/VM:*:*)
322 echo s390-ibm-zvmoe
323 exit 0 ;;
324 *:OS400:*:*)
325 echo powerpc-ibm-os400
326 exit 0 ;;
327 arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
328 echo arm-acorn-riscix${UNAME_RELEASE}
329 exit 0;;
330 SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
331 echo hppa1.1-hitachi-hiuxmpp
332 exit 0;;
333 Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
334 # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
335 if test "`(/bin/universe) 2>/dev/null`" = att ; then
336 echo pyramid-pyramid-sysv3
337 else
338 echo pyramid-pyramid-bsd
339 fi
340 exit 0 ;;
341 NILE*:*:*:dcosx)
342 echo pyramid-pyramid-svr4
343 exit 0 ;;
344 DRS?6000:unix:4.0:6*)
345 echo sparc-icl-nx6
346 exit 0 ;;
347 DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
348 case `/usr/bin/uname -p` in
349 sparc) echo sparc-icl-nx7 && exit 0 ;;
350 esac ;;
351 sun4H:SunOS:5.*:*)
352 echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
353 exit 0 ;;
354 sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
355 echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
356 exit 0 ;;
357 i86pc:SunOS:5.*:*)
358 echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
359 exit 0 ;;
360 sun4*:SunOS:6*:*)
361 # According to config.sub, this is the proper way to canonicalize
362 # SunOS6. Hard to guess exactly what SunOS6 will be like, but
363 # it's likely to be more like Solaris than SunOS4.
364 echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
365 exit 0 ;;
366 sun4*:SunOS:*:*)
367 case "`/usr/bin/arch -k`" in
368 Series*|S4*)
369 UNAME_RELEASE=`uname -v`
370 ;;
371 esac
372 # Japanese Language versions have a version number like `4.1.3-JL'.
373 echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
374 exit 0 ;;
375 sun3*:SunOS:*:*)
376 echo m68k-sun-sunos${UNAME_RELEASE}
377 exit 0 ;;
378 sun*:*:4.2BSD:*)
379 UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
380 test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
381 case "`/bin/arch`" in
382 sun3)
383 echo m68k-sun-sunos${UNAME_RELEASE}
384 ;;
385 sun4)
386 echo sparc-sun-sunos${UNAME_RELEASE}
387 ;;
388 esac
389 exit 0 ;;
390 aushp:SunOS:*:*)
391 echo sparc-auspex-sunos${UNAME_RELEASE}
392 exit 0 ;;
393 # The situation for MiNT is a little confusing. The machine name
394 # can be virtually everything (everything which is not
395 # "atarist" or "atariste" at least should have a processor
396 # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
397 # to the lowercase version "mint" (or "freemint"). Finally
398 # the system name "TOS" denotes a system which is actually not
399 # MiNT. But MiNT is downward compatible to TOS, so this should
400 # be no problem.
401 atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
402 echo m68k-atari-mint${UNAME_RELEASE}
403 exit 0 ;;
404 atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
405 echo m68k-atari-mint${UNAME_RELEASE}
406 exit 0 ;;
407 *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
408 echo m68k-atari-mint${UNAME_RELEASE}
409 exit 0 ;;
410 milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
411 echo m68k-milan-mint${UNAME_RELEASE}
412 exit 0 ;;
413 hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
414 echo m68k-hades-mint${UNAME_RELEASE}
415 exit 0 ;;
416 *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
417 echo m68k-unknown-mint${UNAME_RELEASE}
418 exit 0 ;;
419 m68k:machten:*:*)
420 echo m68k-apple-machten${UNAME_RELEASE}
421 exit 0 ;;
422 powerpc:machten:*:*)
423 echo powerpc-apple-machten${UNAME_RELEASE}
424 exit 0 ;;
425 RISC*:Mach:*:*)
426 echo mips-dec-mach_bsd4.3
427 exit 0 ;;
428 RISC*:ULTRIX:*:*)
429 echo mips-dec-ultrix${UNAME_RELEASE}
430 exit 0 ;;
431 VAX*:ULTRIX*:*:*)
432 echo vax-dec-ultrix${UNAME_RELEASE}
433 exit 0 ;;
434 2020:CLIX:*:* | 2430:CLIX:*:*)
435 echo clipper-intergraph-clix${UNAME_RELEASE}
436 exit 0 ;;
437 mips:*:*:UMIPS | mips:*:*:RISCos)
438 eval $set_cc_for_build
439 sed 's/^ //' << EOF >$dummy.c
440 #ifdef __cplusplus
441 #include <stdio.h> /* for printf() prototype */
442 int main (int argc, char *argv[]) {
443 #else
444 int main (argc, argv) int argc; char *argv[]; {
445 #endif
446 #if defined (host_mips) && defined (MIPSEB)
447 #if defined (SYSTYPE_SYSV)
448 printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
449 #endif
450 #if defined (SYSTYPE_SVR4)
451 printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
452 #endif
453 #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
454 printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
455 #endif
456 #endif
457 exit (-1);
458 }
459 EOF
460 $CC_FOR_BUILD -o $dummy $dummy.c \
461 && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
462 && exit 0
463 echo mips-mips-riscos${UNAME_RELEASE}
464 exit 0 ;;
465 Motorola:PowerMAX_OS:*:*)
466 echo powerpc-motorola-powermax
467 exit 0 ;;
468 Motorola:*:4.3:PL8-*)
469 echo powerpc-harris-powermax
470 exit 0 ;;
471 Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
472 echo powerpc-harris-powermax
473 exit 0 ;;
474 Night_Hawk:Power_UNIX:*:*)
475 echo powerpc-harris-powerunix
476 exit 0 ;;
477 m88k:CX/UX:7*:*)
478 echo m88k-harris-cxux7
479 exit 0 ;;
480 m88k:*:4*:R4*)
481 echo m88k-motorola-sysv4
482 exit 0 ;;
483 m88k:*:3*:R3*)
484 echo m88k-motorola-sysv3
485 exit 0 ;;
486 AViiON:dgux:*:*)
487 # DG/UX returns AViiON for all architectures
488 UNAME_PROCESSOR=`/usr/bin/uname -p`
489 if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
490 then
491 if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
492 [ ${TARGET_BINARY_INTERFACE}x = x ]
493 then
494 echo m88k-dg-dgux${UNAME_RELEASE}
495 else
496 echo m88k-dg-dguxbcs${UNAME_RELEASE}
497 fi
498 else
499 echo i586-dg-dgux${UNAME_RELEASE}
500 fi
501 exit 0 ;;
502 M88*:DolphinOS:*:*) # DolphinOS (SVR3)
503 echo m88k-dolphin-sysv3
504 exit 0 ;;
505 M88*:*:R3*:*)
506 # Delta 88k system running SVR3
507 echo m88k-motorola-sysv3
508 exit 0 ;;
509 XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
510 echo m88k-tektronix-sysv3
511 exit 0 ;;
512 Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
513 echo m68k-tektronix-bsd
514 exit 0 ;;
515 *:IRIX*:*:*)
516 echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
517 exit 0 ;;
518 ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
519 echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
520 exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
521 i*86:AIX:*:*)
522 echo i386-ibm-aix
523 exit 0 ;;
524 ia64:AIX:*:*)
525 if [ -x /usr/bin/oslevel ] ; then
526 IBM_REV=`/usr/bin/oslevel`
527 else
528 IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
529 fi
530 echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
531 exit 0 ;;
532 *:AIX:2:3)
533 if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
534 eval $set_cc_for_build
535 sed 's/^ //' << EOF >$dummy.c
536 #include <sys/systemcfg.h>
537
538 main()
539 {
540 if (!__power_pc())
541 exit(1);
542 puts("powerpc-ibm-aix3.2.5");
543 exit(0);
544 }
545 EOF
546 $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
547 echo rs6000-ibm-aix3.2.5
548 elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
549 echo rs6000-ibm-aix3.2.4
550 else
551 echo rs6000-ibm-aix3.2
552 fi
553 exit 0 ;;
554 *:AIX:*:[45])
555 IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
556 if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
557 IBM_ARCH=rs6000
558 else
559 IBM_ARCH=powerpc
560 fi
561 if [ -x /usr/bin/oslevel ] ; then
562 IBM_REV=`/usr/bin/oslevel`
563 else
564 IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
565 fi
566 echo ${IBM_ARCH}-ibm-aix${IBM_REV}
567 exit 0 ;;
568 *:AIX:*:*)
569 echo rs6000-ibm-aix
570 exit 0 ;;
571 ibmrt:4.4BSD:*|romp-ibm:BSD:*)
572 echo romp-ibm-bsd4.4
573 exit 0 ;;
574 ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
575 echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
576 exit 0 ;; # report: romp-ibm BSD 4.3
577 *:BOSX:*:*)
578 echo rs6000-bull-bosx
579 exit 0 ;;
580 DPX/2?00:B.O.S.:*:*)
581 echo m68k-bull-sysv3
582 exit 0 ;;
583 9000/[34]??:4.3bsd:1.*:*)
584 echo m68k-hp-bsd
585 exit 0 ;;
586 hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
587 echo m68k-hp-bsd4.4
588 exit 0 ;;
589 9000/[34678]??:HP-UX:*:*)
590 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
591 case "${UNAME_MACHINE}" in
592 9000/31? ) HP_ARCH=m68000 ;;
593 9000/[34]?? ) HP_ARCH=m68k ;;
594 9000/[678][0-9][0-9])
595 if [ -x /usr/bin/getconf ]; then
596 sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
597 sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
598 case "${sc_cpu_version}" in
599 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
600 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
601 532) # CPU_PA_RISC2_0
602 case "${sc_kernel_bits}" in
603 32) HP_ARCH="hppa2.0n" ;;
604 64) HP_ARCH="hppa2.0w" ;;
605 '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
606 esac ;;
607 esac
608 fi
609 if [ "${HP_ARCH}" = "" ]; then
610 eval $set_cc_for_build
611 sed 's/^ //' << EOF >$dummy.c
612
613 #define _HPUX_SOURCE
614 #include <stdlib.h>
615 #include <unistd.h>
616
617 int main ()
618 {
619 #if defined(_SC_KERNEL_BITS)
620 long bits = sysconf(_SC_KERNEL_BITS);
621 #endif
622 long cpu = sysconf (_SC_CPU_VERSION);
623
624 switch (cpu)
625 {
626 case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
627 case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
628 case CPU_PA_RISC2_0:
629 #if defined(_SC_KERNEL_BITS)
630 switch (bits)
631 {
632 case 64: puts ("hppa2.0w"); break;
633 case 32: puts ("hppa2.0n"); break;
634 default: puts ("hppa2.0"); break;
635 } break;
636 #else /* !defined(_SC_KERNEL_BITS) */
637 puts ("hppa2.0"); break;
638 #endif
639 default: puts ("hppa1.0"); break;
640 }
641 exit (0);
642 }
643 EOF
644 (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
645 test -z "$HP_ARCH" && HP_ARCH=hppa
646 fi ;;
647 esac
648 if [ ${HP_ARCH} = "hppa2.0w" ]
649 then
650 # avoid double evaluation of $set_cc_for_build
651 test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
652 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
653 then
654 HP_ARCH="hppa2.0w"
655 else
656 HP_ARCH="hppa64"
657 fi
658 fi
659 echo ${HP_ARCH}-hp-hpux${HPUX_REV}
660 exit 0 ;;
661 ia64:HP-UX:*:*)
662 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
663 echo ia64-hp-hpux${HPUX_REV}
664 exit 0 ;;
665 3050*:HI-UX:*:*)
666 eval $set_cc_for_build
667 sed 's/^ //' << EOF >$dummy.c
668 #include <unistd.h>
669 int
670 main ()
671 {
672 long cpu = sysconf (_SC_CPU_VERSION);
673 /* The order matters, because CPU_IS_HP_MC68K erroneously returns
674 true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
675 results, however. */
676 if (CPU_IS_PA_RISC (cpu))
677 {
678 switch (cpu)
679 {
680 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
681 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
682 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
683 default: puts ("hppa-hitachi-hiuxwe2"); break;
684 }
685 }
686 else if (CPU_IS_HP_MC68K (cpu))
687 puts ("m68k-hitachi-hiuxwe2");
688 else puts ("unknown-hitachi-hiuxwe2");
689 exit (0);
690 }
691 EOF
692 $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
693 echo unknown-hitachi-hiuxwe2
694 exit 0 ;;
695 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
696 echo hppa1.1-hp-bsd
697 exit 0 ;;
698 9000/8??:4.3bsd:*:*)
699 echo hppa1.0-hp-bsd
700 exit 0 ;;
701 *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
702 echo hppa1.0-hp-mpeix
703 exit 0 ;;
704 hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
705 echo hppa1.1-hp-osf
706 exit 0 ;;
707 hp8??:OSF1:*:*)
708 echo hppa1.0-hp-osf
709 exit 0 ;;
710 i*86:OSF1:*:*)
711 if [ -x /usr/sbin/sysversion ] ; then
712 echo ${UNAME_MACHINE}-unknown-osf1mk
713 else
714 echo ${UNAME_MACHINE}-unknown-osf1
715 fi
716 exit 0 ;;
717 parisc*:Lites*:*:*)
718 echo hppa1.1-hp-lites
719 exit 0 ;;
720 C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
721 echo c1-convex-bsd
722 exit 0 ;;
723 C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
724 if getsysinfo -f scalar_acc
725 then echo c32-convex-bsd
726 else echo c2-convex-bsd
727 fi
728 exit 0 ;;
729 C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
730 echo c34-convex-bsd
731 exit 0 ;;
732 C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
733 echo c38-convex-bsd
734 exit 0 ;;
735 C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
736 echo c4-convex-bsd
737 exit 0 ;;
738 CRAY*Y-MP:*:*:*)
739 echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
740 exit 0 ;;
741 CRAY*[A-Z]90:*:*:*)
742 echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
743 | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
744 -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
745 -e 's/\.[^.]*$/.X/'
746 exit 0 ;;
747 CRAY*TS:*:*:*)
748 echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
749 exit 0 ;;
750 CRAY*T3E:*:*:*)
751 echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
752 exit 0 ;;
753 CRAY*SV1:*:*:*)
754 echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
755 exit 0 ;;
756 *:UNICOS/mp:*:*)
757 echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
758 exit 0 ;;
759 F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
760 FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
761 FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
762 FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
763 echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
764 exit 0 ;;
765 5000:UNIX_System_V:4.*:*)
766 FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
767 FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
768 echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
769 exit 0 ;;
770 i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
771 echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
772 exit 0 ;;
773 sparc*:BSD/OS:*:*)
774 echo sparc-unknown-bsdi${UNAME_RELEASE}
775 exit 0 ;;
776 *:BSD/OS:*:*)
777 echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
778 exit 0 ;;
779 *:FreeBSD:*:*)
780 echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
781 exit 0 ;;
782 i*:CYGWIN*:*)
783 echo ${UNAME_MACHINE}-pc-cygwin
784 exit 0 ;;
785 i*:MINGW*:*)
786 echo ${UNAME_MACHINE}-pc-mingw32
787 exit 0 ;;
788 i*:PW*:*)
789 echo ${UNAME_MACHINE}-pc-pw32
790 exit 0 ;;
791 x86:Interix*:[34]*)
792 echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
793 exit 0 ;;
794 [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
795 echo i${UNAME_MACHINE}-pc-mks
796 exit 0 ;;
797 i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
798 # How do we know it's Interix rather than the generic POSIX subsystem?
799 # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
800 # UNAME_MACHINE based on the output of uname instead of i386?
801 echo i586-pc-interix
802 exit 0 ;;
803 i*:UWIN*:*)
804 echo ${UNAME_MACHINE}-pc-uwin
805 exit 0 ;;
806 amd64:CYGWIN*:*:*)
807 echo x86_64-unknown-cygwin
808 exit 0 ;;
809 p*:CYGWIN*:*)
810 echo powerpcle-unknown-cygwin
811 exit 0 ;;
812 prep*:SunOS:5.*:*)
813 echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
814 exit 0 ;;
815 *:GNU:*:*)
816 # the GNU system
817 echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
818 exit 0 ;;
819 *:GNU/*:*:*)
820 # other systems with GNU libc and userland
821 echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
822 exit 0 ;;
823 i*86:Minix:*:*)
824 echo ${UNAME_MACHINE}-pc-minix
825 exit 0 ;;
826 arm*:Linux:*:*)
827 echo ${UNAME_MACHINE}-unknown-linux-gnu
828 exit 0 ;;
829 cris:Linux:*:*)
830 echo cris-axis-linux-gnu
831 exit 0 ;;
832 crisv32:Linux:*:*)
833 echo crisv32-axis-linux-gnu
834 exit 0 ;;
835 frv:Linux:*:*)
836 echo frv-unknown-linux-gnu
837 exit 0 ;;
838 ia64:Linux:*:*)
839 echo ${UNAME_MACHINE}-unknown-linux-gnu
840 exit 0 ;;
841 m32r*:Linux:*:*)
842 echo ${UNAME_MACHINE}-unknown-linux-gnu
843 exit 0 ;;
844 m68*:Linux:*:*)
845 echo ${UNAME_MACHINE}-unknown-linux-gnu
846 exit 0 ;;
847 mips:Linux:*:*)
848 eval $set_cc_for_build
849 sed 's/^ //' << EOF >$dummy.c
850 #undef CPU
851 #undef mips
852 #undef mipsel
853 #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
854 CPU=mipsel
855 #else
856 #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
857 CPU=mips
858 #else
859 CPU=
860 #endif
861 #endif
862 EOF
863 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
864 test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
865 ;;
866 mips64:Linux:*:*)
867 eval $set_cc_for_build
868 sed 's/^ //' << EOF >$dummy.c
869 #undef CPU
870 #undef mips64
871 #undef mips64el
872 #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
873 CPU=mips64el
874 #else
875 #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
876 CPU=mips64
877 #else
878 CPU=
879 #endif
880 #endif
881 EOF
882 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
883 test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
884 ;;
885 ppc:Linux:*:*)
886 echo powerpc-unknown-linux-gnu
887 exit 0 ;;
888 ppc64:Linux:*:*)
889 echo powerpc64-unknown-linux-gnu
890 exit 0 ;;
891 alpha:Linux:*:*)
892 case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
893 EV5) UNAME_MACHINE=alphaev5 ;;
894 EV56) UNAME_MACHINE=alphaev56 ;;
895 PCA56) UNAME_MACHINE=alphapca56 ;;
896 PCA57) UNAME_MACHINE=alphapca56 ;;
897 EV6) UNAME_MACHINE=alphaev6 ;;
898 EV67) UNAME_MACHINE=alphaev67 ;;
899 EV68*) UNAME_MACHINE=alphaev68 ;;
900 esac
901 objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
902 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
903 echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
904 exit 0 ;;
905 parisc:Linux:*:* | hppa:Linux:*:*)
906 # Look for CPU level
907 case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
908 PA7*) echo hppa1.1-unknown-linux-gnu ;;
909 PA8*) echo hppa2.0-unknown-linux-gnu ;;
910 *) echo hppa-unknown-linux-gnu ;;
911 esac
912 exit 0 ;;
913 parisc64:Linux:*:* | hppa64:Linux:*:*)
914 echo hppa64-unknown-linux-gnu
915 exit 0 ;;
916 s390:Linux:*:* | s390x:Linux:*:*)
917 echo ${UNAME_MACHINE}-ibm-linux
918 exit 0 ;;
919 sh64*:Linux:*:*)
920 echo ${UNAME_MACHINE}-unknown-linux-gnu
921 exit 0 ;;
922 sh*:Linux:*:*)
923 echo ${UNAME_MACHINE}-unknown-linux-gnu
924 exit 0 ;;
925 sparc:Linux:*:* | sparc64:Linux:*:*)
926 echo ${UNAME_MACHINE}-unknown-linux-gnu
927 exit 0 ;;
928 x86_64:Linux:*:*)
929 echo x86_64-unknown-linux-gnu
930 exit 0 ;;
931 i*86:Linux:*:*)
932 # The BFD linker knows what the default object file format is, so
933 # first see if it will tell us. cd to the root directory to prevent
934 # problems with other programs or directories called `ld' in the path.
935 # Set LC_ALL=C to ensure ld outputs messages in English.
936 ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
937 | sed -ne '/supported targets:/!d
938 s/[ ][ ]*/ /g
939 s/.*supported targets: *//
940 s/ .*//
941 p'`
942 case "$ld_supported_targets" in
943 elf32-i386)
944 TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
945 ;;
946 a.out-i386-linux)
947 echo "${UNAME_MACHINE}-pc-linux-gnuaout"
948 exit 0 ;;
949 coff-i386)
950 echo "${UNAME_MACHINE}-pc-linux-gnucoff"
951 exit 0 ;;
952 "")
953 # Either a pre-BFD a.out linker (linux-gnuoldld) or
954 # one that does not give us useful --help.
955 echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
956 exit 0 ;;
957 esac
958 # Determine whether the default compiler is a.out or elf
959 eval $set_cc_for_build
960 sed 's/^ //' << EOF >$dummy.c
961 #include <features.h>
962 #ifdef __ELF__
963 # ifdef __GLIBC__
964 # if __GLIBC__ >= 2
965 LIBC=gnu
966 # else
967 LIBC=gnulibc1
968 # endif
969 # else
970 LIBC=gnulibc1
971 # endif
972 #else
973 #ifdef __INTEL_COMPILER
974 LIBC=gnu
975 #else
976 LIBC=gnuaout
977 #endif
978 #endif
979 #ifdef __dietlibc__
980 LIBC=dietlibc
981 #endif
982 EOF
983 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
984 test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
985 test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
986 ;;
987 i*86:DYNIX/ptx:4*:*)
988 # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
989 # earlier versions are messed up and put the nodename in both
990 # sysname and nodename.
991 echo i386-sequent-sysv4
992 exit 0 ;;
993 i*86:UNIX_SV:4.2MP:2.*)
994 # Unixware is an offshoot of SVR4, but it has its own version
995 # number series starting with 2...
996 # I am not positive that other SVR4 systems won't match this,
997 # I just have to hope. -- rms.
998 # Use sysv4.2uw... so that sysv4* matches it.
999 echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
1000 exit 0 ;;
1001 i*86:OS/2:*:*)
1002 # If we were able to find `uname', then EMX Unix compatibility
1003 # is probably installed.
1004 echo ${UNAME_MACHINE}-pc-os2-emx
1005 exit 0 ;;
1006 i*86:XTS-300:*:STOP)
1007 echo ${UNAME_MACHINE}-unknown-stop
1008 exit 0 ;;
1009 i*86:atheos:*:*)
1010 echo ${UNAME_MACHINE}-unknown-atheos
1011 exit 0 ;;
1012 i*86:syllable:*:*)
1013 echo ${UNAME_MACHINE}-pc-syllable
1014 exit 0 ;;
1015 i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
1016 echo i386-unknown-lynxos${UNAME_RELEASE}
1017 exit 0 ;;
1018 i*86:*DOS:*:*)
1019 echo ${UNAME_MACHINE}-pc-msdosdjgpp
1020 exit 0 ;;
1021 i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
1022 UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
1023 if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
1024 echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
1025 else
1026 echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
1027 fi
1028 exit 0 ;;
1029 i*86:*:5:[78]*)
1030 case `/bin/uname -X | grep "^Machine"` in
1031 *486*) UNAME_MACHINE=i486 ;;
1032 *Pentium) UNAME_MACHINE=i586 ;;
1033 *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
1034 esac
1035 echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
1036 exit 0 ;;
1037 i*86:*:3.2:*)
1038 if test -f /usr/options/cb.name; then
1039 UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
1040 echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
1041 elif /bin/uname -X 2>/dev/null >/dev/null ; then
1042 UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
1043 (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
1044 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
1045 && UNAME_MACHINE=i586
1046 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
1047 && UNAME_MACHINE=i686
1048 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
1049 && UNAME_MACHINE=i686
1050 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
1051 else
1052 echo ${UNAME_MACHINE}-pc-sysv32
1053 fi
1054 exit 0 ;;
1055 pc:*:*:*)
1056 # Left here for compatibility:
1057 # uname -m prints for DJGPP always 'pc', but it prints nothing about
1058 # the processor, so we play safe by assuming i386.
1059 echo i386-pc-msdosdjgpp
1060 exit 0 ;;
1061 Intel:Mach:3*:*)
1062 echo i386-pc-mach3
1063 exit 0 ;;
1064 paragon:*:*:*)
1065 echo i860-intel-osf1
1066 exit 0 ;;
1067 i860:*:4.*:*) # i860-SVR4
1068 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
1069 echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
1070 else # Add other i860-SVR4 vendors below as they are discovered.
1071 echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
1072 fi
1073 exit 0 ;;
1074 mini*:CTIX:SYS*5:*)
1075 # "miniframe"
1076 echo m68010-convergent-sysv
1077 exit 0 ;;
1078 mc68k:UNIX:SYSTEM5:3.51m)
1079 echo m68k-convergent-sysv
1080 exit 0 ;;
1081 M680?0:D-NIX:5.3:*)
1082 echo m68k-diab-dnix
1083 exit 0 ;;
1084 M68*:*:R3V[5678]*:*)
1085 test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
1086 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
1087 OS_REL=''
1088 test -r /etc/.relid \
1089 && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
1090 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1091 && echo i486-ncr-sysv4.3${OS_REL} && exit 0
1092 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
1093 && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
1094 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
1095 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1096 && echo i486-ncr-sysv4 && exit 0 ;;
1097 m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
1098 echo m68k-unknown-lynxos${UNAME_RELEASE}
1099 exit 0 ;;
1100 mc68030:UNIX_System_V:4.*:*)
1101 echo m68k-atari-sysv4
1102 exit 0 ;;
1103 TSUNAMI:LynxOS:2.*:*)
1104 echo sparc-unknown-lynxos${UNAME_RELEASE}
1105 exit 0 ;;
1106 rs6000:LynxOS:2.*:*)
1107 echo rs6000-unknown-lynxos${UNAME_RELEASE}
1108 exit 0 ;;
1109 PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
1110 echo powerpc-unknown-lynxos${UNAME_RELEASE}
1111 exit 0 ;;
1112 SM[BE]S:UNIX_SV:*:*)
1113 echo mips-dde-sysv${UNAME_RELEASE}
1114 exit 0 ;;
1115 RM*:ReliantUNIX-*:*:*)
1116 echo mips-sni-sysv4
1117 exit 0 ;;
1118 RM*:SINIX-*:*:*)
1119 echo mips-sni-sysv4
1120 exit 0 ;;
1121 *:SINIX-*:*:*)
1122 if uname -p 2>/dev/null >/dev/null ; then
1123 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1124 echo ${UNAME_MACHINE}-sni-sysv4
1125 else
1126 echo ns32k-sni-sysv
1127 fi
1128 exit 0 ;;
1129 PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
1130 # says <Richard.M.Bartel@ccMail.Census.GOV>
1131 echo i586-unisys-sysv4
1132 exit 0 ;;
1133 *:UNIX_System_V:4*:FTX*)
1134 # From Gerald Hewes <hewes@openmarket.com>.
1135 # How about differentiating between stratus architectures? -djm
1136 echo hppa1.1-stratus-sysv4
1137 exit 0 ;;
1138 *:*:*:FTX*)
1139 # From seanf@swdc.stratus.com.
1140 echo i860-stratus-sysv4
1141 exit 0 ;;
1142 *:VOS:*:*)
1143 # From Paul.Green@stratus.com.
1144 echo hppa1.1-stratus-vos
1145 exit 0 ;;
1146 mc68*:A/UX:*:*)
1147 echo m68k-apple-aux${UNAME_RELEASE}
1148 exit 0 ;;
1149 news*:NEWS-OS:6*:*)
1150 echo mips-sony-newsos6
1151 exit 0 ;;
1152 R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
1153 if [ -d /usr/nec ]; then
1154 echo mips-nec-sysv${UNAME_RELEASE}
1155 else
1156 echo mips-unknown-sysv${UNAME_RELEASE}
1157 fi
1158 exit 0 ;;
1159 BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
1160 echo powerpc-be-beos
1161 exit 0 ;;
1162 BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
1163 echo powerpc-apple-beos
1164 exit 0 ;;
1165 BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
1166 echo i586-pc-beos
1167 exit 0 ;;
1168 SX-4:SUPER-UX:*:*)
1169 echo sx4-nec-superux${UNAME_RELEASE}
1170 exit 0 ;;
1171 SX-5:SUPER-UX:*:*)
1172 echo sx5-nec-superux${UNAME_RELEASE}
1173 exit 0 ;;
1174 SX-6:SUPER-UX:*:*)
1175 echo sx6-nec-superux${UNAME_RELEASE}
1176 exit 0 ;;
1177 Power*:Rhapsody:*:*)
1178 echo powerpc-apple-rhapsody${UNAME_RELEASE}
1179 exit 0 ;;
1180 *:Rhapsody:*:*)
1181 echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
1182 exit 0 ;;
1183 *:Darwin:*:*)
1184 UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
1185 case $UNAME_PROCESSOR in
1186 *86) UNAME_PROCESSOR=i686 ;;
1187 unknown) UNAME_PROCESSOR=powerpc ;;
1188 esac
1189 echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
1190 exit 0 ;;
1191 *:procnto*:*:* | *:QNX:[0123456789]*:*)
1192 UNAME_PROCESSOR=`uname -p`
1193 if test "$UNAME_PROCESSOR" = "x86"; then
1194 UNAME_PROCESSOR=i386
1195 UNAME_MACHINE=pc
1196 fi
1197 echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
1198 exit 0 ;;
1199 *:QNX:*:4*)
1200 echo i386-pc-qnx
1201 exit 0 ;;
1202 NSE-?:NONSTOP_KERNEL:*:*)
1203 echo nse-tandem-nsk${UNAME_RELEASE}
1204 exit 0 ;;
1205 NSR-?:NONSTOP_KERNEL:*:*)
1206 echo nsr-tandem-nsk${UNAME_RELEASE}
1207 exit 0 ;;
1208 *:NonStop-UX:*:*)
1209 echo mips-compaq-nonstopux
1210 exit 0 ;;
1211 BS2000:POSIX*:*:*)
1212 echo bs2000-siemens-sysv
1213 exit 0 ;;
1214 DS/*:UNIX_System_V:*:*)
1215 echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
1216 exit 0 ;;
1217 *:Plan9:*:*)
1218 # "uname -m" is not consistent, so use $cputype instead. 386
1219 # is converted to i386 for consistency with other x86
1220 # operating systems.
1221 if test "$cputype" = "386"; then
1222 UNAME_MACHINE=i386
1223 else
1224 UNAME_MACHINE="$cputype"
1225 fi
1226 echo ${UNAME_MACHINE}-unknown-plan9
1227 exit 0 ;;
1228 *:TOPS-10:*:*)
1229 echo pdp10-unknown-tops10
1230 exit 0 ;;
1231 *:TENEX:*:*)
1232 echo pdp10-unknown-tenex
1233 exit 0 ;;
1234 KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
1235 echo pdp10-dec-tops20
1236 exit 0 ;;
1237 XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
1238 echo pdp10-xkl-tops20
1239 exit 0 ;;
1240 *:TOPS-20:*:*)
1241 echo pdp10-unknown-tops20
1242 exit 0 ;;
1243 *:ITS:*:*)
1244 echo pdp10-unknown-its
1245 exit 0 ;;
1246 SEI:*:*:SEIUX)
1247 echo mips-sei-seiux${UNAME_RELEASE}
1248 exit 0 ;;
1249 *:DragonFly:*:*)
1250 echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
1251 exit 0 ;;
1252 *:*VMS:*:*)
1253 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1254 case "${UNAME_MACHINE}" in
1255 A*) echo alpha-dec-vms && exit 0 ;;
1256 I*) echo ia64-dec-vms && exit 0 ;;
1257 V*) echo vax-dec-vms && exit 0 ;;
1258 esac ;;
1259 *:XENIX:*:SysV)
1260 echo i386-pc-xenix
1261 exit 0 ;;
1262 esac
1263
1264 #echo '(No uname command or uname output not recognized.)' 1>&2
1265 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
1266
1267 eval $set_cc_for_build
1268 cat >$dummy.c <<EOF
1269 #ifdef _SEQUENT_
1270 # include <sys/types.h>
1271 # include <sys/utsname.h>
1272 #endif
1273 main ()
1274 {
1275 #if defined (sony)
1276 #if defined (MIPSEB)
1277 /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
1278 I don't know.... */
1279 printf ("mips-sony-bsd\n"); exit (0);
1280 #else
1281 #include <sys/param.h>
1282 printf ("m68k-sony-newsos%s\n",
1283 #ifdef NEWSOS4
1284 "4"
1285 #else
1286 ""
1287 #endif
1288 ); exit (0);
1289 #endif
1290 #endif
1291
1292 #if defined (__arm) && defined (__acorn) && defined (__unix)
1293 printf ("arm-acorn-riscix"); exit (0);
1294 #endif
1295
1296 #if defined (hp300) && !defined (hpux)
1297 printf ("m68k-hp-bsd\n"); exit (0);
1298 #endif
1299
1300 #if defined (NeXT)
1301 #if !defined (__ARCHITECTURE__)
1302 #define __ARCHITECTURE__ "m68k"
1303 #endif
1304 int version;
1305 version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
1306 if (version < 4)
1307 printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
1308 else
1309 printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
1310 exit (0);
1311 #endif
1312
1313 #if defined (MULTIMAX) || defined (n16)
1314 #if defined (UMAXV)
1315 printf ("ns32k-encore-sysv\n"); exit (0);
1316 #else
1317 #if defined (CMU)
1318 printf ("ns32k-encore-mach\n"); exit (0);
1319 #else
1320 printf ("ns32k-encore-bsd\n"); exit (0);
1321 #endif
1322 #endif
1323 #endif
1324
1325 #if defined (__386BSD__)
1326 printf ("i386-pc-bsd\n"); exit (0);
1327 #endif
1328
1329 #if defined (sequent)
1330 #if defined (i386)
1331 printf ("i386-sequent-dynix\n"); exit (0);
1332 #endif
1333 #if defined (ns32000)
1334 printf ("ns32k-sequent-dynix\n"); exit (0);
1335 #endif
1336 #endif
1337
1338 #if defined (_SEQUENT_)
1339 struct utsname un;
1340
1341 uname(&un);
1342
1343 if (strncmp(un.version, "V2", 2) == 0) {
1344 printf ("i386-sequent-ptx2\n"); exit (0);
1345 }
1346 if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
1347 printf ("i386-sequent-ptx1\n"); exit (0);
1348 }
1349 printf ("i386-sequent-ptx\n"); exit (0);
1350
1351 #endif
1352
1353 #if defined (vax)
1354 # if !defined (ultrix)
1355 # include <sys/param.h>
1356 # if defined (BSD)
1357 # if BSD == 43
1358 printf ("vax-dec-bsd4.3\n"); exit (0);
1359 # else
1360 # if BSD == 199006
1361 printf ("vax-dec-bsd4.3reno\n"); exit (0);
1362 # else
1363 printf ("vax-dec-bsd\n"); exit (0);
1364 # endif
1365 # endif
1366 # else
1367 printf ("vax-dec-bsd\n"); exit (0);
1368 # endif
1369 # else
1370 printf ("vax-dec-ultrix\n"); exit (0);
1371 # endif
1372 #endif
1373
1374 #if defined (alliant) && defined (i860)
1375 printf ("i860-alliant-bsd\n"); exit (0);
1376 #endif
1377
1378 exit (1);
1379 }
1380 EOF
1381
1382 $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
1383
1384 # Apollos put the system type in the environment.
1385
1386 test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
1387
1388 # Convex versions that predate uname can use getsysinfo(1)
1389
1390 if [ -x /usr/convex/getsysinfo ]
1391 then
1392 case `getsysinfo -f cpu_type` in
1393 c1*)
1394 echo c1-convex-bsd
1395 exit 0 ;;
1396 c2*)
1397 if getsysinfo -f scalar_acc
1398 then echo c32-convex-bsd
1399 else echo c2-convex-bsd
1400 fi
1401 exit 0 ;;
1402 c34*)
1403 echo c34-convex-bsd
1404 exit 0 ;;
1405 c38*)
1406 echo c38-convex-bsd
1407 exit 0 ;;
1408 c4*)
1409 echo c4-convex-bsd
1410 exit 0 ;;
1411 esac
1412 fi
1413
1414 cat >&2 <<EOF
1415 $0: unable to guess system type
1416
1417 This script, last modified $timestamp, has failed to recognize
1418 the operating system you are using. It is advised that you
1419 download the most up to date version of the config scripts from
1420
1421 ftp://ftp.gnu.org/pub/gnu/config/
1422
1423 If the version you run ($0) is already up to date, please
1424 send the following data and any information you think might be
1425 pertinent to <config-patches@gnu.org> in order to provide the needed
1426 information to handle your system.
1427
1428 config.guess timestamp = $timestamp
1429
1430 uname -m = `(uname -m) 2>/dev/null || echo unknown`
1431 uname -r = `(uname -r) 2>/dev/null || echo unknown`
1432 uname -s = `(uname -s) 2>/dev/null || echo unknown`
1433 uname -v = `(uname -v) 2>/dev/null || echo unknown`
1434
1435 /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
1436 /bin/uname -X = `(/bin/uname -X) 2>/dev/null`
1437
1438 hostinfo = `(hostinfo) 2>/dev/null`
1439 /bin/universe = `(/bin/universe) 2>/dev/null`
1440 /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
1441 /bin/arch = `(/bin/arch) 2>/dev/null`
1442 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
1443 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
1444
1445 UNAME_MACHINE = ${UNAME_MACHINE}
1446 UNAME_RELEASE = ${UNAME_RELEASE}
1447 UNAME_SYSTEM = ${UNAME_SYSTEM}
1448 UNAME_VERSION = ${UNAME_VERSION}
1449 EOF
1450
1451 exit 1
1452
1453 # Local variables:
1454 # eval: (add-hook 'write-file-hooks 'time-stamp)
1455 # time-stamp-start: "timestamp='"
1456 # time-stamp-format: "%:y-%02m-%02d"
1457 # time-stamp-end: "'"
1458 # End:
0 /* config.h.in. Generated from configure.in by autoheader. */
1
2 /* Enable GIF images */
3 #undef ENABLE_GIF
4
5 /* Enable JPEG images */
6 #undef ENABLE_JPEG
7
8 /* Enable PNG images */
9 #undef ENABLE_PNG
10
11 /* Enable SSL support */
12 #undef ENABLE_SSL
13
14 /* Define to 1 if you have the <fcntl.h> header file. */
15 #undef HAVE_FCNTL_H
16
17 /* Define to 1 if you have the `gethostbyname' function. */
18 #undef HAVE_GETHOSTBYNAME
19
20 /* Define to 1 if you have the <inttypes.h> header file. */
21 #undef HAVE_INTTYPES_H
22
23 /* Define to 1 if you have the `nsl' library (-lnsl). */
24 #undef HAVE_LIBNSL
25
26 /* Define to 1 if you have the <libpng/png.h> header file. */
27 #undef HAVE_LIBPNG_PNG_H
28
29 /* Define to 1 if you have the `socket' library (-lsocket). */
30 #undef HAVE_LIBSOCKET
31
32 /* Define to 1 if you have the <memory.h> header file. */
33 #undef HAVE_MEMORY_H
34
35 /* Define to 1 if you have the <png.h> header file. */
36 #undef HAVE_PNG_H
37
38 /* Define to 1 if you have the `setsockopt' function. */
39 #undef HAVE_SETSOCKOPT
40
41 /* Define to 1 if you have the <stdint.h> header file. */
42 #undef HAVE_STDINT_H
43
44 /* Define to 1 if you have the <stdlib.h> header file. */
45 #undef HAVE_STDLIB_H
46
47 /* Define to 1 if you have the <strings.h> header file. */
48 #undef HAVE_STRINGS_H
49
50 /* Define to 1 if you have the <string.h> header file. */
51 #undef HAVE_STRING_H
52
53 /* Define to 1 if you have the <sys/stat.h> header file. */
54 #undef HAVE_SYS_STAT_H
55
56 /* Define to 1 if you have the <sys/types.h> header file. */
57 #undef HAVE_SYS_TYPES_H
58
59 /* Define to 1 if you have the <sys/uio.h> header file. */
60 #undef HAVE_SYS_UIO_H
61
62 /* Define to 1 if you have the <unistd.h> header file. */
63 #undef HAVE_UNISTD_H
64
65 /* Name of package */
66 #undef PACKAGE
67
68 /* Define to the address where bug reports for this package should be sent. */
69 #undef PACKAGE_BUGREPORT
70
71 /* Define to the full name of this package. */
72 #undef PACKAGE_NAME
73
74 /* Define to the full name and version of this package. */
75 #undef PACKAGE_STRING
76
77 /* Define to the one symbol short name of this package. */
78 #undef PACKAGE_TARNAME
79
80 /* Define to the version of this package. */
81 #undef PACKAGE_VERSION
82
83 /* Define to 1 if you have the ANSI C header files. */
84 #undef STDC_HEADERS
85
86 /* Version number of package */
87 #undef VERSION
88
89 /* Define the real type of socklen_t */
90 #undef socklen_t
0 #! /bin/sh
1 # Configuration validation subroutine script.
2 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
3 # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4
5 timestamp='2005-02-10'
6
7 # This file is (in principle) common to ALL GNU software.
8 # The presence of a machine in this file suggests that SOME GNU software
9 # can handle that machine. It does not imply ALL GNU software can.
10 #
11 # This file is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place - Suite 330,
24 # Boston, MA 02111-1307, USA.
25
26 # As a special exception to the GNU General Public License, if you
27 # distribute this file as part of a program that contains a
28 # configuration script generated by Autoconf, you may include it under
29 # the same distribution terms that you use for the rest of that program.
30
31 # Please send patches to <config-patches@gnu.org>. Submit a context
32 # diff and a properly formatted ChangeLog entry.
33 #
34 # Configuration subroutine to validate and canonicalize a configuration type.
35 # Supply the specified configuration type as an argument.
36 # If it is invalid, we print an error message on stderr and exit with code 1.
37 # Otherwise, we print the canonical config type on stdout and succeed.
38
39 # This file is supposed to be the same for all GNU packages
40 # and recognize all the CPU types, system types and aliases
41 # that are meaningful with *any* GNU software.
42 # Each package is responsible for reporting which valid configurations
43 # it does not support. The user should be able to distinguish
44 # a failure to support a valid configuration from a meaningless
45 # configuration.
46
47 # The goal of this file is to map all the various variations of a given
48 # machine specification into a single specification in the form:
49 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
50 # or in some cases, the newer four-part form:
51 # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
52 # It is wrong to echo any other type of specification.
53
54 me=`echo "$0" | sed -e 's,.*/,,'`
55
56 usage="\
57 Usage: $0 [OPTION] CPU-MFR-OPSYS
58 $0 [OPTION] ALIAS
59
60 Canonicalize a configuration name.
61
62 Operation modes:
63 -h, --help print this help, then exit
64 -t, --time-stamp print date of last modification, then exit
65 -v, --version print version number, then exit
66
67 Report bugs and patches to <config-patches@gnu.org>."
68
69 version="\
70 GNU config.sub ($timestamp)
71
72 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
73 Free Software Foundation, Inc.
74
75 This is free software; see the source for copying conditions. There is NO
76 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
77
78 help="
79 Try \`$me --help' for more information."
80
81 # Parse command line
82 while test $# -gt 0 ; do
83 case $1 in
84 --time-stamp | --time* | -t )
85 echo "$timestamp" ; exit 0 ;;
86 --version | -v )
87 echo "$version" ; exit 0 ;;
88 --help | --h* | -h )
89 echo "$usage"; exit 0 ;;
90 -- ) # Stop option processing
91 shift; break ;;
92 - ) # Use stdin as input.
93 break ;;
94 -* )
95 echo "$me: invalid option $1$help"
96 exit 1 ;;
97
98 *local*)
99 # First pass through any local machine types.
100 echo $1
101 exit 0;;
102
103 * )
104 break ;;
105 esac
106 done
107
108 case $# in
109 0) echo "$me: missing argument$help" >&2
110 exit 1;;
111 1) ;;
112 *) echo "$me: too many arguments$help" >&2
113 exit 1;;
114 esac
115
116 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
117 # Here we must recognize all the valid KERNEL-OS combinations.
118 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
119 case $maybe_os in
120 nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
121 kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
122 os=-$maybe_os
123 basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
124 ;;
125 *)
126 basic_machine=`echo $1 | sed 's/-[^-]*$//'`
127 if [ $basic_machine != $1 ]
128 then os=`echo $1 | sed 's/.*-/-/'`
129 else os=; fi
130 ;;
131 esac
132
133 ### Let's recognize common machines as not being operating systems so
134 ### that things like config.sub decstation-3100 work. We also
135 ### recognize some manufacturers as not being operating systems, so we
136 ### can provide default operating systems below.
137 case $os in
138 -sun*os*)
139 # Prevent following clause from handling this invalid input.
140 ;;
141 -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
142 -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
143 -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
144 -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
145 -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
146 -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
147 -apple | -axis | -knuth | -cray)
148 os=
149 basic_machine=$1
150 ;;
151 -sim | -cisco | -oki | -wec | -winbond)
152 os=
153 basic_machine=$1
154 ;;
155 -scout)
156 ;;
157 -wrs)
158 os=-vxworks
159 basic_machine=$1
160 ;;
161 -chorusos*)
162 os=-chorusos
163 basic_machine=$1
164 ;;
165 -chorusrdb)
166 os=-chorusrdb
167 basic_machine=$1
168 ;;
169 -hiux*)
170 os=-hiuxwe2
171 ;;
172 -sco5)
173 os=-sco3.2v5
174 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
175 ;;
176 -sco4)
177 os=-sco3.2v4
178 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
179 ;;
180 -sco3.2.[4-9]*)
181 os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
182 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
183 ;;
184 -sco3.2v[4-9]*)
185 # Don't forget version if it is 3.2v4 or newer.
186 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
187 ;;
188 -sco*)
189 os=-sco3.2v2
190 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
191 ;;
192 -udk*)
193 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
194 ;;
195 -isc)
196 os=-isc2.2
197 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
198 ;;
199 -clix*)
200 basic_machine=clipper-intergraph
201 ;;
202 -isc*)
203 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
204 ;;
205 -lynx*)
206 os=-lynxos
207 ;;
208 -ptx*)
209 basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
210 ;;
211 -windowsnt*)
212 os=`echo $os | sed -e 's/windowsnt/winnt/'`
213 ;;
214 -psos*)
215 os=-psos
216 ;;
217 -mint | -mint[0-9]*)
218 basic_machine=m68k-atari
219 os=-mint
220 ;;
221 esac
222
223 # Decode aliases for certain CPU-COMPANY combinations.
224 case $basic_machine in
225 # Recognize the basic CPU types without company name.
226 # Some are omitted here because they have special meanings below.
227 1750a | 580 \
228 | a29k \
229 | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
230 | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
231 | am33_2.0 \
232 | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
233 | c4x | clipper \
234 | d10v | d30v | dlx | dsp16xx \
235 | fr30 | frv \
236 | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
237 | i370 | i860 | i960 | ia64 \
238 | ip2k | iq2000 \
239 | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
240 | mips | mipsbe | mipseb | mipsel | mipsle \
241 | mips16 \
242 | mips64 | mips64el \
243 | mips64vr | mips64vrel \
244 | mips64orion | mips64orionel \
245 | mips64vr4100 | mips64vr4100el \
246 | mips64vr4300 | mips64vr4300el \
247 | mips64vr5000 | mips64vr5000el \
248 | mipsisa32 | mipsisa32el \
249 | mipsisa32r2 | mipsisa32r2el \
250 | mipsisa64 | mipsisa64el \
251 | mipsisa64r2 | mipsisa64r2el \
252 | mipsisa64sb1 | mipsisa64sb1el \
253 | mipsisa64sr71k | mipsisa64sr71kel \
254 | mipstx39 | mipstx39el \
255 | mn10200 | mn10300 \
256 | msp430 \
257 | ns16k | ns32k \
258 | openrisc | or32 \
259 | pdp10 | pdp11 | pj | pjl \
260 | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
261 | pyramid \
262 | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
263 | sh64 | sh64le \
264 | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
265 | strongarm \
266 | tahoe | thumb | tic4x | tic80 | tron \
267 | v850 | v850e \
268 | we32k \
269 | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
270 | z8k)
271 basic_machine=$basic_machine-unknown
272 ;;
273 m6811 | m68hc11 | m6812 | m68hc12)
274 # Motorola 68HC11/12.
275 basic_machine=$basic_machine-unknown
276 os=-none
277 ;;
278 m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
279 ;;
280
281 # We use `pc' rather than `unknown'
282 # because (1) that's what they normally are, and
283 # (2) the word "unknown" tends to confuse beginning users.
284 i*86 | x86_64)
285 basic_machine=$basic_machine-pc
286 ;;
287 # Object if more than one company name word.
288 *-*-*)
289 echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
290 exit 1
291 ;;
292 # Recognize the basic CPU types with company name.
293 580-* \
294 | a29k-* \
295 | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
296 | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
297 | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
298 | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
299 | avr-* \
300 | bs2000-* \
301 | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
302 | clipper-* | craynv-* | cydra-* \
303 | d10v-* | d30v-* | dlx-* \
304 | elxsi-* \
305 | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
306 | h8300-* | h8500-* \
307 | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
308 | i*86-* | i860-* | i960-* | ia64-* \
309 | ip2k-* | iq2000-* \
310 | m32r-* | m32rle-* \
311 | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
312 | m88110-* | m88k-* | maxq-* | mcore-* \
313 | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
314 | mips16-* \
315 | mips64-* | mips64el-* \
316 | mips64vr-* | mips64vrel-* \
317 | mips64orion-* | mips64orionel-* \
318 | mips64vr4100-* | mips64vr4100el-* \
319 | mips64vr4300-* | mips64vr4300el-* \
320 | mips64vr5000-* | mips64vr5000el-* \
321 | mipsisa32-* | mipsisa32el-* \
322 | mipsisa32r2-* | mipsisa32r2el-* \
323 | mipsisa64-* | mipsisa64el-* \
324 | mipsisa64r2-* | mipsisa64r2el-* \
325 | mipsisa64sb1-* | mipsisa64sb1el-* \
326 | mipsisa64sr71k-* | mipsisa64sr71kel-* \
327 | mipstx39-* | mipstx39el-* \
328 | mmix-* \
329 | msp430-* \
330 | none-* | np1-* | ns16k-* | ns32k-* \
331 | orion-* \
332 | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
333 | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
334 | pyramid-* \
335 | romp-* | rs6000-* \
336 | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
337 | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
338 | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
339 | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
340 | tahoe-* | thumb-* \
341 | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
342 | tron-* \
343 | v850-* | v850e-* | vax-* \
344 | we32k-* \
345 | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
346 | xstormy16-* | xtensa-* \
347 | ymp-* \
348 | z8k-*)
349 ;;
350 # Recognize the various machine names and aliases which stand
351 # for a CPU type and a company and sometimes even an OS.
352 386bsd)
353 basic_machine=i386-unknown
354 os=-bsd
355 ;;
356 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
357 basic_machine=m68000-att
358 ;;
359 3b*)
360 basic_machine=we32k-att
361 ;;
362 a29khif)
363 basic_machine=a29k-amd
364 os=-udi
365 ;;
366 abacus)
367 basic_machine=abacus-unknown
368 ;;
369 adobe68k)
370 basic_machine=m68010-adobe
371 os=-scout
372 ;;
373 alliant | fx80)
374 basic_machine=fx80-alliant
375 ;;
376 altos | altos3068)
377 basic_machine=m68k-altos
378 ;;
379 am29k)
380 basic_machine=a29k-none
381 os=-bsd
382 ;;
383 amd64)
384 basic_machine=x86_64-pc
385 ;;
386 amd64-*)
387 basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
388 ;;
389 amdahl)
390 basic_machine=580-amdahl
391 os=-sysv
392 ;;
393 amiga | amiga-*)
394 basic_machine=m68k-unknown
395 ;;
396 amigaos | amigados)
397 basic_machine=m68k-unknown
398 os=-amigaos
399 ;;
400 amigaunix | amix)
401 basic_machine=m68k-unknown
402 os=-sysv4
403 ;;
404 apollo68)
405 basic_machine=m68k-apollo
406 os=-sysv
407 ;;
408 apollo68bsd)
409 basic_machine=m68k-apollo
410 os=-bsd
411 ;;
412 aux)
413 basic_machine=m68k-apple
414 os=-aux
415 ;;
416 balance)
417 basic_machine=ns32k-sequent
418 os=-dynix
419 ;;
420 c90)
421 basic_machine=c90-cray
422 os=-unicos
423 ;;
424 convex-c1)
425 basic_machine=c1-convex
426 os=-bsd
427 ;;
428 convex-c2)
429 basic_machine=c2-convex
430 os=-bsd
431 ;;
432 convex-c32)
433 basic_machine=c32-convex
434 os=-bsd
435 ;;
436 convex-c34)
437 basic_machine=c34-convex
438 os=-bsd
439 ;;
440 convex-c38)
441 basic_machine=c38-convex
442 os=-bsd
443 ;;
444 cray | j90)
445 basic_machine=j90-cray
446 os=-unicos
447 ;;
448 craynv)
449 basic_machine=craynv-cray
450 os=-unicosmp
451 ;;
452 cr16c)
453 basic_machine=cr16c-unknown
454 os=-elf
455 ;;
456 crds | unos)
457 basic_machine=m68k-crds
458 ;;
459 crisv32 | crisv32-* | etraxfs*)
460 basic_machine=crisv32-axis
461 ;;
462 cris | cris-* | etrax*)
463 basic_machine=cris-axis
464 ;;
465 crx)
466 basic_machine=crx-unknown
467 os=-elf
468 ;;
469 da30 | da30-*)
470 basic_machine=m68k-da30
471 ;;
472 decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
473 basic_machine=mips-dec
474 ;;
475 decsystem10* | dec10*)
476 basic_machine=pdp10-dec
477 os=-tops10
478 ;;
479 decsystem20* | dec20*)
480 basic_machine=pdp10-dec
481 os=-tops20
482 ;;
483 delta | 3300 | motorola-3300 | motorola-delta \
484 | 3300-motorola | delta-motorola)
485 basic_machine=m68k-motorola
486 ;;
487 delta88)
488 basic_machine=m88k-motorola
489 os=-sysv3
490 ;;
491 djgpp)
492 basic_machine=i586-pc
493 os=-msdosdjgpp
494 ;;
495 dpx20 | dpx20-*)
496 basic_machine=rs6000-bull
497 os=-bosx
498 ;;
499 dpx2* | dpx2*-bull)
500 basic_machine=m68k-bull
501 os=-sysv3
502 ;;
503 ebmon29k)
504 basic_machine=a29k-amd
505 os=-ebmon
506 ;;
507 elxsi)
508 basic_machine=elxsi-elxsi
509 os=-bsd
510 ;;
511 encore | umax | mmax)
512 basic_machine=ns32k-encore
513 ;;
514 es1800 | OSE68k | ose68k | ose | OSE)
515 basic_machine=m68k-ericsson
516 os=-ose
517 ;;
518 fx2800)
519 basic_machine=i860-alliant
520 ;;
521 genix)
522 basic_machine=ns32k-ns
523 ;;
524 gmicro)
525 basic_machine=tron-gmicro
526 os=-sysv
527 ;;
528 go32)
529 basic_machine=i386-pc
530 os=-go32
531 ;;
532 h3050r* | hiux*)
533 basic_machine=hppa1.1-hitachi
534 os=-hiuxwe2
535 ;;
536 h8300hms)
537 basic_machine=h8300-hitachi
538 os=-hms
539 ;;
540 h8300xray)
541 basic_machine=h8300-hitachi
542 os=-xray
543 ;;
544 h8500hms)
545 basic_machine=h8500-hitachi
546 os=-hms
547 ;;
548 harris)
549 basic_machine=m88k-harris
550 os=-sysv3
551 ;;
552 hp300-*)
553 basic_machine=m68k-hp
554 ;;
555 hp300bsd)
556 basic_machine=m68k-hp
557 os=-bsd
558 ;;
559 hp300hpux)
560 basic_machine=m68k-hp
561 os=-hpux
562 ;;
563 hp3k9[0-9][0-9] | hp9[0-9][0-9])
564 basic_machine=hppa1.0-hp
565 ;;
566 hp9k2[0-9][0-9] | hp9k31[0-9])
567 basic_machine=m68000-hp
568 ;;
569 hp9k3[2-9][0-9])
570 basic_machine=m68k-hp
571 ;;
572 hp9k6[0-9][0-9] | hp6[0-9][0-9])
573 basic_machine=hppa1.0-hp
574 ;;
575 hp9k7[0-79][0-9] | hp7[0-79][0-9])
576 basic_machine=hppa1.1-hp
577 ;;
578 hp9k78[0-9] | hp78[0-9])
579 # FIXME: really hppa2.0-hp
580 basic_machine=hppa1.1-hp
581 ;;
582 hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
583 # FIXME: really hppa2.0-hp
584 basic_machine=hppa1.1-hp
585 ;;
586 hp9k8[0-9][13679] | hp8[0-9][13679])
587 basic_machine=hppa1.1-hp
588 ;;
589 hp9k8[0-9][0-9] | hp8[0-9][0-9])
590 basic_machine=hppa1.0-hp
591 ;;
592 hppa-next)
593 os=-nextstep3
594 ;;
595 hppaosf)
596 basic_machine=hppa1.1-hp
597 os=-osf
598 ;;
599 hppro)
600 basic_machine=hppa1.1-hp
601 os=-proelf
602 ;;
603 i370-ibm* | ibm*)
604 basic_machine=i370-ibm
605 ;;
606 # I'm not sure what "Sysv32" means. Should this be sysv3.2?
607 i*86v32)
608 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
609 os=-sysv32
610 ;;
611 i*86v4*)
612 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
613 os=-sysv4
614 ;;
615 i*86v)
616 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
617 os=-sysv
618 ;;
619 i*86sol2)
620 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
621 os=-solaris2
622 ;;
623 i386mach)
624 basic_machine=i386-mach
625 os=-mach
626 ;;
627 i386-vsta | vsta)
628 basic_machine=i386-unknown
629 os=-vsta
630 ;;
631 iris | iris4d)
632 basic_machine=mips-sgi
633 case $os in
634 -irix*)
635 ;;
636 *)
637 os=-irix4
638 ;;
639 esac
640 ;;
641 isi68 | isi)
642 basic_machine=m68k-isi
643 os=-sysv
644 ;;
645 m88k-omron*)
646 basic_machine=m88k-omron
647 ;;
648 magnum | m3230)
649 basic_machine=mips-mips
650 os=-sysv
651 ;;
652 merlin)
653 basic_machine=ns32k-utek
654 os=-sysv
655 ;;
656 mingw32)
657 basic_machine=i386-pc
658 os=-mingw32
659 ;;
660 miniframe)
661 basic_machine=m68000-convergent
662 ;;
663 *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
664 basic_machine=m68k-atari
665 os=-mint
666 ;;
667 mips3*-*)
668 basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
669 ;;
670 mips3*)
671 basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
672 ;;
673 monitor)
674 basic_machine=m68k-rom68k
675 os=-coff
676 ;;
677 morphos)
678 basic_machine=powerpc-unknown
679 os=-morphos
680 ;;
681 msdos)
682 basic_machine=i386-pc
683 os=-msdos
684 ;;
685 mvs)
686 basic_machine=i370-ibm
687 os=-mvs
688 ;;
689 ncr3000)
690 basic_machine=i486-ncr
691 os=-sysv4
692 ;;
693 netbsd386)
694 basic_machine=i386-unknown
695 os=-netbsd
696 ;;
697 netwinder)
698 basic_machine=armv4l-rebel
699 os=-linux
700 ;;
701 news | news700 | news800 | news900)
702 basic_machine=m68k-sony
703 os=-newsos
704 ;;
705 news1000)
706 basic_machine=m68030-sony
707 os=-newsos
708 ;;
709 news-3600 | risc-news)
710 basic_machine=mips-sony
711 os=-newsos
712 ;;
713 necv70)
714 basic_machine=v70-nec
715 os=-sysv
716 ;;
717 next | m*-next )
718 basic_machine=m68k-next
719 case $os in
720 -nextstep* )
721 ;;
722 -ns2*)
723 os=-nextstep2
724 ;;
725 *)
726 os=-nextstep3
727 ;;
728 esac
729 ;;
730 nh3000)
731 basic_machine=m68k-harris
732 os=-cxux
733 ;;
734 nh[45]000)
735 basic_machine=m88k-harris
736 os=-cxux
737 ;;
738 nindy960)
739 basic_machine=i960-intel
740 os=-nindy
741 ;;
742 mon960)
743 basic_machine=i960-intel
744 os=-mon960
745 ;;
746 nonstopux)
747 basic_machine=mips-compaq
748 os=-nonstopux
749 ;;
750 np1)
751 basic_machine=np1-gould
752 ;;
753 nsr-tandem)
754 basic_machine=nsr-tandem
755 ;;
756 op50n-* | op60c-*)
757 basic_machine=hppa1.1-oki
758 os=-proelf
759 ;;
760 or32 | or32-*)
761 basic_machine=or32-unknown
762 os=-coff
763 ;;
764 os400)
765 basic_machine=powerpc-ibm
766 os=-os400
767 ;;
768 OSE68000 | ose68000)
769 basic_machine=m68000-ericsson
770 os=-ose
771 ;;
772 os68k)
773 basic_machine=m68k-none
774 os=-os68k
775 ;;
776 pa-hitachi)
777 basic_machine=hppa1.1-hitachi
778 os=-hiuxwe2
779 ;;
780 paragon)
781 basic_machine=i860-intel
782 os=-osf
783 ;;
784 pbd)
785 basic_machine=sparc-tti
786 ;;
787 pbb)
788 basic_machine=m68k-tti
789 ;;
790 pc532 | pc532-*)
791 basic_machine=ns32k-pc532
792 ;;
793 pentium | p5 | k5 | k6 | nexgen | viac3)
794 basic_machine=i586-pc
795 ;;
796 pentiumpro | p6 | 6x86 | athlon | athlon_*)
797 basic_machine=i686-pc
798 ;;
799 pentiumii | pentium2 | pentiumiii | pentium3)
800 basic_machine=i686-pc
801 ;;
802 pentium4)
803 basic_machine=i786-pc
804 ;;
805 pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
806 basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
807 ;;
808 pentiumpro-* | p6-* | 6x86-* | athlon-*)
809 basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
810 ;;
811 pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
812 basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
813 ;;
814 pentium4-*)
815 basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
816 ;;
817 pn)
818 basic_machine=pn-gould
819 ;;
820 power) basic_machine=power-ibm
821 ;;
822 ppc) basic_machine=powerpc-unknown
823 ;;
824 ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
825 ;;
826 ppcle | powerpclittle | ppc-le | powerpc-little)
827 basic_machine=powerpcle-unknown
828 ;;
829 ppcle-* | powerpclittle-*)
830 basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
831 ;;
832 ppc64) basic_machine=powerpc64-unknown
833 ;;
834 ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
835 ;;
836 ppc64le | powerpc64little | ppc64-le | powerpc64-little)
837 basic_machine=powerpc64le-unknown
838 ;;
839 ppc64le-* | powerpc64little-*)
840 basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
841 ;;
842 ps2)
843 basic_machine=i386-ibm
844 ;;
845 pw32)
846 basic_machine=i586-unknown
847 os=-pw32
848 ;;
849 rom68k)
850 basic_machine=m68k-rom68k
851 os=-coff
852 ;;
853 rm[46]00)
854 basic_machine=mips-siemens
855 ;;
856 rtpc | rtpc-*)
857 basic_machine=romp-ibm
858 ;;
859 s390 | s390-*)
860 basic_machine=s390-ibm
861 ;;
862 s390x | s390x-*)
863 basic_machine=s390x-ibm
864 ;;
865 sa29200)
866 basic_machine=a29k-amd
867 os=-udi
868 ;;
869 sb1)
870 basic_machine=mipsisa64sb1-unknown
871 ;;
872 sb1el)
873 basic_machine=mipsisa64sb1el-unknown
874 ;;
875 sei)
876 basic_machine=mips-sei
877 os=-seiux
878 ;;
879 sequent)
880 basic_machine=i386-sequent
881 ;;
882 sh)
883 basic_machine=sh-hitachi
884 os=-hms
885 ;;
886 sh64)
887 basic_machine=sh64-unknown
888 ;;
889 sparclite-wrs | simso-wrs)
890 basic_machine=sparclite-wrs
891 os=-vxworks
892 ;;
893 sps7)
894 basic_machine=m68k-bull
895 os=-sysv2
896 ;;
897 spur)
898 basic_machine=spur-unknown
899 ;;
900 st2000)
901 basic_machine=m68k-tandem
902 ;;
903 stratus)
904 basic_machine=i860-stratus
905 os=-sysv4
906 ;;
907 sun2)
908 basic_machine=m68000-sun
909 ;;
910 sun2os3)
911 basic_machine=m68000-sun
912 os=-sunos3
913 ;;
914 sun2os4)
915 basic_machine=m68000-sun
916 os=-sunos4
917 ;;
918 sun3os3)
919 basic_machine=m68k-sun
920 os=-sunos3
921 ;;
922 sun3os4)
923 basic_machine=m68k-sun
924 os=-sunos4
925 ;;
926 sun4os3)
927 basic_machine=sparc-sun
928 os=-sunos3
929 ;;
930 sun4os4)
931 basic_machine=sparc-sun
932 os=-sunos4
933 ;;
934 sun4sol2)
935 basic_machine=sparc-sun
936 os=-solaris2
937 ;;
938 sun3 | sun3-*)
939 basic_machine=m68k-sun
940 ;;
941 sun4)
942 basic_machine=sparc-sun
943 ;;
944 sun386 | sun386i | roadrunner)
945 basic_machine=i386-sun
946 ;;
947 sv1)
948 basic_machine=sv1-cray
949 os=-unicos
950 ;;
951 symmetry)
952 basic_machine=i386-sequent
953 os=-dynix
954 ;;
955 t3e)
956 basic_machine=alphaev5-cray
957 os=-unicos
958 ;;
959 t90)
960 basic_machine=t90-cray
961 os=-unicos
962 ;;
963 tic54x | c54x*)
964 basic_machine=tic54x-unknown
965 os=-coff
966 ;;
967 tic55x | c55x*)
968 basic_machine=tic55x-unknown
969 os=-coff
970 ;;
971 tic6x | c6x*)
972 basic_machine=tic6x-unknown
973 os=-coff
974 ;;
975 tx39)
976 basic_machine=mipstx39-unknown
977 ;;
978 tx39el)
979 basic_machine=mipstx39el-unknown
980 ;;
981 toad1)
982 basic_machine=pdp10-xkl
983 os=-tops20
984 ;;
985 tower | tower-32)
986 basic_machine=m68k-ncr
987 ;;
988 tpf)
989 basic_machine=s390x-ibm
990 os=-tpf
991 ;;
992 udi29k)
993 basic_machine=a29k-amd
994 os=-udi
995 ;;
996 ultra3)
997 basic_machine=a29k-nyu
998 os=-sym1
999 ;;
1000 v810 | necv810)
1001 basic_machine=v810-nec
1002 os=-none
1003 ;;
1004 vaxv)
1005 basic_machine=vax-dec
1006 os=-sysv
1007 ;;
1008 vms)
1009 basic_machine=vax-dec
1010 os=-vms
1011 ;;
1012 vpp*|vx|vx-*)
1013 basic_machine=f301-fujitsu
1014 ;;
1015 vxworks960)
1016 basic_machine=i960-wrs
1017 os=-vxworks
1018 ;;
1019 vxworks68)
1020 basic_machine=m68k-wrs
1021 os=-vxworks
1022 ;;
1023 vxworks29k)
1024 basic_machine=a29k-wrs
1025 os=-vxworks
1026 ;;
1027 w65*)
1028 basic_machine=w65-wdc
1029 os=-none
1030 ;;
1031 w89k-*)
1032 basic_machine=hppa1.1-winbond
1033 os=-proelf
1034 ;;
1035 xbox)
1036 basic_machine=i686-pc
1037 os=-mingw32
1038 ;;
1039 xps | xps100)
1040 basic_machine=xps100-honeywell
1041 ;;
1042 ymp)
1043 basic_machine=ymp-cray
1044 os=-unicos
1045 ;;
1046 z8k-*-coff)
1047 basic_machine=z8k-unknown
1048 os=-sim
1049 ;;
1050 none)
1051 basic_machine=none-none
1052 os=-none
1053 ;;
1054
1055 # Here we handle the default manufacturer of certain CPU types. It is in
1056 # some cases the only manufacturer, in others, it is the most popular.
1057 w89k)
1058 basic_machine=hppa1.1-winbond
1059 ;;
1060 op50n)
1061 basic_machine=hppa1.1-oki
1062 ;;
1063 op60c)
1064 basic_machine=hppa1.1-oki
1065 ;;
1066 romp)
1067 basic_machine=romp-ibm
1068 ;;
1069 mmix)
1070 basic_machine=mmix-knuth
1071 ;;
1072 rs6000)
1073 basic_machine=rs6000-ibm
1074 ;;
1075 vax)
1076 basic_machine=vax-dec
1077 ;;
1078 pdp10)
1079 # there are many clones, so DEC is not a safe bet
1080 basic_machine=pdp10-unknown
1081 ;;
1082 pdp11)
1083 basic_machine=pdp11-dec
1084 ;;
1085 we32k)
1086 basic_machine=we32k-att
1087 ;;
1088 sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
1089 basic_machine=sh-unknown
1090 ;;
1091 sh64)
1092 basic_machine=sh64-unknown
1093 ;;
1094 sparc | sparcv8 | sparcv9 | sparcv9b)
1095 basic_machine=sparc-sun
1096 ;;
1097 cydra)
1098 basic_machine=cydra-cydrome
1099 ;;
1100 orion)
1101 basic_machine=orion-highlevel
1102 ;;
1103 orion105)
1104 basic_machine=clipper-highlevel
1105 ;;
1106 mac | mpw | mac-mpw)
1107 basic_machine=m68k-apple
1108 ;;
1109 pmac | pmac-mpw)
1110 basic_machine=powerpc-apple
1111 ;;
1112 *-unknown)
1113 # Make sure to match an already-canonicalized machine name.
1114 ;;
1115 *)
1116 echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
1117 exit 1
1118 ;;
1119 esac
1120
1121 # Here we canonicalize certain aliases for manufacturers.
1122 case $basic_machine in
1123 *-digital*)
1124 basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
1125 ;;
1126 *-commodore*)
1127 basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
1128 ;;
1129 *)
1130 ;;
1131 esac
1132
1133 # Decode manufacturer-specific aliases for certain operating systems.
1134
1135 if [ x"$os" != x"" ]
1136 then
1137 case $os in
1138 # First match some system type aliases
1139 # that might get confused with valid system types.
1140 # -solaris* is a basic system type, with this one exception.
1141 -solaris1 | -solaris1.*)
1142 os=`echo $os | sed -e 's|solaris1|sunos4|'`
1143 ;;
1144 -solaris)
1145 os=-solaris2
1146 ;;
1147 -svr4*)
1148 os=-sysv4
1149 ;;
1150 -unixware*)
1151 os=-sysv4.2uw
1152 ;;
1153 -gnu/linux*)
1154 os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
1155 ;;
1156 # First accept the basic system types.
1157 # The portable systems comes first.
1158 # Each alternative MUST END IN A *, to match a version number.
1159 # -sysv* is not here because it comes later, after sysvr4.
1160 -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
1161 | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
1162 | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
1163 | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
1164 | -aos* \
1165 | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
1166 | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
1167 | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
1168 | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
1169 | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
1170 | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
1171 | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
1172 | -chorusos* | -chorusrdb* \
1173 | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
1174 | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
1175 | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
1176 | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
1177 | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
1178 | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
1179 | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
1180 | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
1181 # Remember, each alternative MUST END IN *, to match a version number.
1182 ;;
1183 -qnx*)
1184 case $basic_machine in
1185 x86-* | i*86-*)
1186 ;;
1187 *)
1188 os=-nto$os
1189 ;;
1190 esac
1191 ;;
1192 -nto-qnx*)
1193 ;;
1194 -nto*)
1195 os=`echo $os | sed -e 's|nto|nto-qnx|'`
1196 ;;
1197 -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
1198 | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
1199 | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
1200 ;;
1201 -mac*)
1202 os=`echo $os | sed -e 's|mac|macos|'`
1203 ;;
1204 -linux-dietlibc)
1205 os=-linux-dietlibc
1206 ;;
1207 -linux*)
1208 os=`echo $os | sed -e 's|linux|linux-gnu|'`
1209 ;;
1210 -sunos5*)
1211 os=`echo $os | sed -e 's|sunos5|solaris2|'`
1212 ;;
1213 -sunos6*)
1214 os=`echo $os | sed -e 's|sunos6|solaris3|'`
1215 ;;
1216 -opened*)
1217 os=-openedition
1218 ;;
1219 -os400*)
1220 os=-os400
1221 ;;
1222 -wince*)
1223 os=-wince
1224 ;;
1225 -osfrose*)
1226 os=-osfrose
1227 ;;
1228 -osf*)
1229 os=-osf
1230 ;;
1231 -utek*)
1232 os=-bsd
1233 ;;
1234 -dynix*)
1235 os=-bsd
1236 ;;
1237 -acis*)
1238 os=-aos
1239 ;;
1240 -atheos*)
1241 os=-atheos
1242 ;;
1243 -syllable*)
1244 os=-syllable
1245 ;;
1246 -386bsd)
1247 os=-bsd
1248 ;;
1249 -ctix* | -uts*)
1250 os=-sysv
1251 ;;
1252 -nova*)
1253 os=-rtmk-nova
1254 ;;
1255 -ns2 )
1256 os=-nextstep2
1257 ;;
1258 -nsk*)
1259 os=-nsk
1260 ;;
1261 # Preserve the version number of sinix5.
1262 -sinix5.*)
1263 os=`echo $os | sed -e 's|sinix|sysv|'`
1264 ;;
1265 -sinix*)
1266 os=-sysv4
1267 ;;
1268 -tpf*)
1269 os=-tpf
1270 ;;
1271 -triton*)
1272 os=-sysv3
1273 ;;
1274 -oss*)
1275 os=-sysv3
1276 ;;
1277 -svr4)
1278 os=-sysv4
1279 ;;
1280 -svr3)
1281 os=-sysv3
1282 ;;
1283 -sysvr4)
1284 os=-sysv4
1285 ;;
1286 # This must come after -sysvr4.
1287 -sysv*)
1288 ;;
1289 -ose*)
1290 os=-ose
1291 ;;
1292 -es1800*)
1293 os=-ose
1294 ;;
1295 -xenix)
1296 os=-xenix
1297 ;;
1298 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
1299 os=-mint
1300 ;;
1301 -aros*)
1302 os=-aros
1303 ;;
1304 -kaos*)
1305 os=-kaos
1306 ;;
1307 -zvmoe)
1308 os=-zvmoe
1309 ;;
1310 -none)
1311 ;;
1312 *)
1313 # Get rid of the `-' at the beginning of $os.
1314 os=`echo $os | sed 's/[^-]*-//'`
1315 echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
1316 exit 1
1317 ;;
1318 esac
1319 else
1320
1321 # Here we handle the default operating systems that come with various machines.
1322 # The value should be what the vendor currently ships out the door with their
1323 # machine or put another way, the most popular os provided with the machine.
1324
1325 # Note that if you're going to try to match "-MANUFACTURER" here (say,
1326 # "-sun"), then you have to tell the case statement up towards the top
1327 # that MANUFACTURER isn't an operating system. Otherwise, code above
1328 # will signal an error saying that MANUFACTURER isn't an operating
1329 # system, and we'll never get to this point.
1330
1331 case $basic_machine in
1332 *-acorn)
1333 os=-riscix1.2
1334 ;;
1335 arm*-rebel)
1336 os=-linux
1337 ;;
1338 arm*-semi)
1339 os=-aout
1340 ;;
1341 c4x-* | tic4x-*)
1342 os=-coff
1343 ;;
1344 # This must come before the *-dec entry.
1345 pdp10-*)
1346 os=-tops20
1347 ;;
1348 pdp11-*)
1349 os=-none
1350 ;;
1351 *-dec | vax-*)
1352 os=-ultrix4.2
1353 ;;
1354 m68*-apollo)
1355 os=-domain
1356 ;;
1357 i386-sun)
1358 os=-sunos4.0.2
1359 ;;
1360 m68000-sun)
1361 os=-sunos3
1362 # This also exists in the configure program, but was not the
1363 # default.
1364 # os=-sunos4
1365 ;;
1366 m68*-cisco)
1367 os=-aout
1368 ;;
1369 mips*-cisco)
1370 os=-elf
1371 ;;
1372 mips*-*)
1373 os=-elf
1374 ;;
1375 or32-*)
1376 os=-coff
1377 ;;
1378 *-tti) # must be before sparc entry or we get the wrong os.
1379 os=-sysv3
1380 ;;
1381 sparc-* | *-sun)
1382 os=-sunos4.1.1
1383 ;;
1384 *-be)
1385 os=-beos
1386 ;;
1387 *-ibm)
1388 os=-aix
1389 ;;
1390 *-knuth)
1391 os=-mmixware
1392 ;;
1393 *-wec)
1394 os=-proelf
1395 ;;
1396 *-winbond)
1397 os=-proelf
1398 ;;
1399 *-oki)
1400 os=-proelf
1401 ;;
1402 *-hp)
1403 os=-hpux
1404 ;;
1405 *-hitachi)
1406 os=-hiux
1407 ;;
1408 i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
1409 os=-sysv
1410 ;;
1411 *-cbm)
1412 os=-amigaos
1413 ;;
1414 *-dg)
1415 os=-dgux
1416 ;;
1417 *-dolphin)
1418 os=-sysv3
1419 ;;
1420 m68k-ccur)
1421 os=-rtu
1422 ;;
1423 m88k-omron*)
1424 os=-luna
1425 ;;
1426 *-next )
1427 os=-nextstep
1428 ;;
1429 *-sequent)
1430 os=-ptx
1431 ;;
1432 *-crds)
1433 os=-unos
1434 ;;
1435 *-ns)
1436 os=-genix
1437 ;;
1438 i370-*)
1439 os=-mvs
1440 ;;
1441 *-next)
1442 os=-nextstep3
1443 ;;
1444 *-gould)
1445 os=-sysv
1446 ;;
1447 *-highlevel)
1448 os=-bsd
1449 ;;
1450 *-encore)
1451 os=-bsd
1452 ;;
1453 *-sgi)
1454 os=-irix
1455 ;;
1456 *-siemens)
1457 os=-sysv4
1458 ;;
1459 *-masscomp)
1460 os=-rtu
1461 ;;
1462 f30[01]-fujitsu | f700-fujitsu)
1463 os=-uxpv
1464 ;;
1465 *-rom68k)
1466 os=-coff
1467 ;;
1468 *-*bug)
1469 os=-coff
1470 ;;
1471 *-apple)
1472 os=-macos
1473 ;;
1474 *-atari*)
1475 os=-mint
1476 ;;
1477 *)
1478 os=-none
1479 ;;
1480 esac
1481 fi
1482
1483 # Here we handle the case where we know the os, and the CPU type, but not the
1484 # manufacturer. We pick the logical manufacturer.
1485 vendor=unknown
1486 case $basic_machine in
1487 *-unknown)
1488 case $os in
1489 -riscix*)
1490 vendor=acorn
1491 ;;
1492 -sunos*)
1493 vendor=sun
1494 ;;
1495 -aix*)
1496 vendor=ibm
1497 ;;
1498 -beos*)
1499 vendor=be
1500 ;;
1501 -hpux*)
1502 vendor=hp
1503 ;;
1504 -mpeix*)
1505 vendor=hp
1506 ;;
1507 -hiux*)
1508 vendor=hitachi
1509 ;;
1510 -unos*)
1511 vendor=crds
1512 ;;
1513 -dgux*)
1514 vendor=dg
1515 ;;
1516 -luna*)
1517 vendor=omron
1518 ;;
1519 -genix*)
1520 vendor=ns
1521 ;;
1522 -mvs* | -opened*)
1523 vendor=ibm
1524 ;;
1525 -os400*)
1526 vendor=ibm
1527 ;;
1528 -ptx*)
1529 vendor=sequent
1530 ;;
1531 -tpf*)
1532 vendor=ibm
1533 ;;
1534 -vxsim* | -vxworks* | -windiss*)
1535 vendor=wrs
1536 ;;
1537 -aux*)
1538 vendor=apple
1539 ;;
1540 -hms*)
1541 vendor=hitachi
1542 ;;
1543 -mpw* | -macos*)
1544 vendor=apple
1545 ;;
1546 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
1547 vendor=atari
1548 ;;
1549 -vos*)
1550 vendor=stratus
1551 ;;
1552 esac
1553 basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
1554 ;;
1555 esac
1556
1557 echo $basic_machine$os
1558 exit 0
1559
1560 # Local variables:
1561 # eval: (add-hook 'write-file-hooks 'time-stamp)
1562 # time-stamp-start: "timestamp='"
1563 # time-stamp-format: "%:y-%02m-%02d"
1564 # time-stamp-end: "'"
1565 # End:
0 #! /bin/sh
1 # Guess values for system-dependent variables and create Makefiles.
2 # Generated by GNU Autoconf 2.59.
3 #
4 # Copyright (C) 2003 Free Software Foundation, Inc.
5 # This configure script is free software; the Free Software Foundation
6 # gives unlimited permission to copy, distribute and modify it.
7 ## --------------------- ##
8 ## M4sh Initialization. ##
9 ## --------------------- ##
10
11 # Be Bourne compatible
12 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
13 emulate sh
14 NULLCMD=:
15 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
16 # is contrary to our usage. Disable this feature.
17 alias -g '${1+"$@"}'='"$@"'
18 elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
19 set -o posix
20 fi
21 DUALCASE=1; export DUALCASE # for MKS sh
22
23 # Support unset when possible.
24 if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
25 as_unset=unset
26 else
27 as_unset=false
28 fi
29
30
31 # Work around bugs in pre-3.0 UWIN ksh.
32 $as_unset ENV MAIL MAILPATH
33 PS1='$ '
34 PS2='> '
35 PS4='+ '
36
37 # NLS nuisances.
38 for as_var in \
39 LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
40 LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
41 LC_TELEPHONE LC_TIME
42 do
43 if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
44 eval $as_var=C; export $as_var
45 else
46 $as_unset $as_var
47 fi
48 done
49
50 # Required to use basename.
51 if expr a : '\(a\)' >/dev/null 2>&1; then
52 as_expr=expr
53 else
54 as_expr=false
55 fi
56
57 if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
58 as_basename=basename
59 else
60 as_basename=false
61 fi
62
63
64 # Name of the executable.
65 as_me=`$as_basename "$0" ||
66 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
67 X"$0" : 'X\(//\)$' \| \
68 X"$0" : 'X\(/\)$' \| \
69 . : '\(.\)' 2>/dev/null ||
70 echo X/"$0" |
71 sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
72 /^X\/\(\/\/\)$/{ s//\1/; q; }
73 /^X\/\(\/\).*/{ s//\1/; q; }
74 s/.*/./; q'`
75
76
77 # PATH needs CR, and LINENO needs CR and PATH.
78 # Avoid depending upon Character Ranges.
79 as_cr_letters='abcdefghijklmnopqrstuvwxyz'
80 as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
81 as_cr_Letters=$as_cr_letters$as_cr_LETTERS
82 as_cr_digits='0123456789'
83 as_cr_alnum=$as_cr_Letters$as_cr_digits
84
85 # The user is always right.
86 if test "${PATH_SEPARATOR+set}" != set; then
87 echo "#! /bin/sh" >conf$$.sh
88 echo "exit 0" >>conf$$.sh
89 chmod +x conf$$.sh
90 if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
91 PATH_SEPARATOR=';'
92 else
93 PATH_SEPARATOR=:
94 fi
95 rm -f conf$$.sh
96 fi
97
98
99 as_lineno_1=$LINENO
100 as_lineno_2=$LINENO
101 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
102 test "x$as_lineno_1" != "x$as_lineno_2" &&
103 test "x$as_lineno_3" = "x$as_lineno_2" || {
104 # Find who we are. Look in the path if we contain no path at all
105 # relative or not.
106 case $0 in
107 *[\\/]* ) as_myself=$0 ;;
108 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
109 for as_dir in $PATH
110 do
111 IFS=$as_save_IFS
112 test -z "$as_dir" && as_dir=.
113 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
114 done
115
116 ;;
117 esac
118 # We did not find ourselves, most probably we were run as `sh COMMAND'
119 # in which case we are not to be found in the path.
120 if test "x$as_myself" = x; then
121 as_myself=$0
122 fi
123 if test ! -f "$as_myself"; then
124 { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
125 { (exit 1); exit 1; }; }
126 fi
127 case $CONFIG_SHELL in
128 '')
129 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
130 for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
131 do
132 IFS=$as_save_IFS
133 test -z "$as_dir" && as_dir=.
134 for as_base in sh bash ksh sh5; do
135 case $as_dir in
136 /*)
137 if ("$as_dir/$as_base" -c '
138 as_lineno_1=$LINENO
139 as_lineno_2=$LINENO
140 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
141 test "x$as_lineno_1" != "x$as_lineno_2" &&
142 test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
143 $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
144 $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
145 CONFIG_SHELL=$as_dir/$as_base
146 export CONFIG_SHELL
147 exec "$CONFIG_SHELL" "$0" ${1+"$@"}
148 fi;;
149 esac
150 done
151 done
152 ;;
153 esac
154
155 # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
156 # uniformly replaced by the line number. The first 'sed' inserts a
157 # line-number line before each line; the second 'sed' does the real
158 # work. The second script uses 'N' to pair each line-number line
159 # with the numbered line, and appends trailing '-' during
160 # substitution so that $LINENO is not a special case at line end.
161 # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
162 # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
163 sed '=' <$as_myself |
164 sed '
165 N
166 s,$,-,
167 : loop
168 s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
169 t loop
170 s,-$,,
171 s,^['$as_cr_digits']*\n,,
172 ' >$as_me.lineno &&
173 chmod +x $as_me.lineno ||
174 { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
175 { (exit 1); exit 1; }; }
176
177 # Don't try to exec as it changes $[0], causing all sort of problems
178 # (the dirname of $[0] is not the place where we might find the
179 # original and so on. Autoconf is especially sensible to this).
180 . ./$as_me.lineno
181 # Exit status is that of the last command.
182 exit
183 }
184
185
186 case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
187 *c*,-n*) ECHO_N= ECHO_C='
188 ' ECHO_T=' ' ;;
189 *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
190 *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
191 esac
192
193 if expr a : '\(a\)' >/dev/null 2>&1; then
194 as_expr=expr
195 else
196 as_expr=false
197 fi
198
199 rm -f conf$$ conf$$.exe conf$$.file
200 echo >conf$$.file
201 if ln -s conf$$.file conf$$ 2>/dev/null; then
202 # We could just check for DJGPP; but this test a) works b) is more generic
203 # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
204 if test -f conf$$.exe; then
205 # Don't use ln at all; we don't have any links
206 as_ln_s='cp -p'
207 else
208 as_ln_s='ln -s'
209 fi
210 elif ln conf$$.file conf$$ 2>/dev/null; then
211 as_ln_s=ln
212 else
213 as_ln_s='cp -p'
214 fi
215 rm -f conf$$ conf$$.exe conf$$.file
216
217 if mkdir -p . 2>/dev/null; then
218 as_mkdir_p=:
219 else
220 test -d ./-p && rmdir ./-p
221 as_mkdir_p=false
222 fi
223
224 as_executable_p="test -f"
225
226 # Sed expression to map a string onto a valid CPP name.
227 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
228
229 # Sed expression to map a string onto a valid variable name.
230 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
231
232
233 # IFS
234 # We need space, tab and new line, in precisely that order.
235 as_nl='
236 '
237 IFS=" $as_nl"
238
239 # CDPATH.
240 $as_unset CDPATH
241
242
243 # Name of the host.
244 # hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
245 # so uname gets run too.
246 ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
247
248 exec 6>&1
249
250 #
251 # Initializations.
252 #
253 ac_default_prefix=/usr/local
254 ac_config_libobj_dir=.
255 cross_compiling=no
256 subdirs=
257 MFLAGS=
258 MAKEFLAGS=
259 SHELL=${CONFIG_SHELL-/bin/sh}
260
261 # Maximum number of lines to put in a shell here document.
262 # This variable seems obsolete. It should probably be removed, and
263 # only ac_max_sed_lines should be used.
264 : ${ac_max_here_lines=38}
265
266 # Identity of this package.
267 PACKAGE_NAME=
268 PACKAGE_TARNAME=
269 PACKAGE_VERSION=
270 PACKAGE_STRING=
271 PACKAGE_BUGREPORT=
272
273 ac_unique_file="src/dillo.c"
274 # Factoring default headers for most tests.
275 ac_includes_default="\
276 #include <stdio.h>
277 #if HAVE_SYS_TYPES_H
278 # include <sys/types.h>
279 #endif
280 #if HAVE_SYS_STAT_H
281 # include <sys/stat.h>
282 #endif
283 #if STDC_HEADERS
284 # include <stdlib.h>
285 # include <stddef.h>
286 #else
287 # if HAVE_STDLIB_H
288 # include <stdlib.h>
289 # endif
290 #endif
291 #if HAVE_STRING_H
292 # if !STDC_HEADERS && HAVE_MEMORY_H
293 # include <memory.h>
294 # endif
295 # include <string.h>
296 #endif
297 #if HAVE_STRINGS_H
298 # include <strings.h>
299 #endif
300 #if HAVE_INTTYPES_H
301 # include <inttypes.h>
302 #else
303 # if HAVE_STDINT_H
304 # include <stdint.h>
305 # endif
306 #endif
307 #if HAVE_UNISTD_H
308 # include <unistd.h>
309 #endif"
310
311 ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar DLGUI_TRUE DLGUI_FALSE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE RANLIB ac_ct_RANLIB CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE GLIB_CONFIG GLIB_CFLAGS GLIB_LIBS GTK_CONFIG GTK_CFLAGS GTK_LIBS EGREP LIBJPEG_LIBS LIBJPEG_LDFLAGS LIBJPEG_CPPFLAGS LIBPNG_LIBS LIBPNG_CFLAGS LIBZ_LIBS LIBSSL_LIBS LIBPTHREAD_LIBS LIBPTHREAD_LDFLAGS LIBFLTK_CXXFLAGS LIBFLTK_LIBS src doc bin util lib LIBOBJS LTLIBOBJS'
312 ac_subst_files=''
313
314 # Initialize some variables set by options.
315 ac_init_help=
316 ac_init_version=false
317 # The variables have the same names as the options, with
318 # dashes changed to underlines.
319 cache_file=/dev/null
320 exec_prefix=NONE
321 no_create=
322 no_recursion=
323 prefix=NONE
324 program_prefix=NONE
325 program_suffix=NONE
326 program_transform_name=s,x,x,
327 silent=
328 site=
329 srcdir=
330 verbose=
331 x_includes=NONE
332 x_libraries=NONE
333
334 # Installation directory options.
335 # These are left unexpanded so users can "make install exec_prefix=/foo"
336 # and all the variables that are supposed to be based on exec_prefix
337 # by default will actually change.
338 # Use braces instead of parens because sh, perl, etc. also accept them.
339 bindir='${exec_prefix}/bin'
340 sbindir='${exec_prefix}/sbin'
341 libexecdir='${exec_prefix}/libexec'
342 datadir='${prefix}/share'
343 sysconfdir='${prefix}/etc'
344 sharedstatedir='${prefix}/com'
345 localstatedir='${prefix}/var'
346 libdir='${exec_prefix}/lib'
347 includedir='${prefix}/include'
348 oldincludedir='/usr/include'
349 infodir='${prefix}/info'
350 mandir='${prefix}/man'
351
352 ac_prev=
353 for ac_option
354 do
355 # If the previous option needs an argument, assign it.
356 if test -n "$ac_prev"; then
357 eval "$ac_prev=\$ac_option"
358 ac_prev=
359 continue
360 fi
361
362 ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
363
364 # Accept the important Cygnus configure options, so we can diagnose typos.
365
366 case $ac_option in
367
368 -bindir | --bindir | --bindi | --bind | --bin | --bi)
369 ac_prev=bindir ;;
370 -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
371 bindir=$ac_optarg ;;
372
373 -build | --build | --buil | --bui | --bu)
374 ac_prev=build_alias ;;
375 -build=* | --build=* | --buil=* | --bui=* | --bu=*)
376 build_alias=$ac_optarg ;;
377
378 -cache-file | --cache-file | --cache-fil | --cache-fi \
379 | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
380 ac_prev=cache_file ;;
381 -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
382 | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
383 cache_file=$ac_optarg ;;
384
385 --config-cache | -C)
386 cache_file=config.cache ;;
387
388 -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
389 ac_prev=datadir ;;
390 -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
391 | --da=*)
392 datadir=$ac_optarg ;;
393
394 -disable-* | --disable-*)
395 ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
396 # Reject names that are not valid shell variable names.
397 expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
398 { echo "$as_me: error: invalid feature name: $ac_feature" >&2
399 { (exit 1); exit 1; }; }
400 ac_feature=`echo $ac_feature | sed 's/-/_/g'`
401 eval "enable_$ac_feature=no" ;;
402
403 -enable-* | --enable-*)
404 ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
405 # Reject names that are not valid shell variable names.
406 expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
407 { echo "$as_me: error: invalid feature name: $ac_feature" >&2
408 { (exit 1); exit 1; }; }
409 ac_feature=`echo $ac_feature | sed 's/-/_/g'`
410 case $ac_option in
411 *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
412 *) ac_optarg=yes ;;
413 esac
414 eval "enable_$ac_feature='$ac_optarg'" ;;
415
416 -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
417 | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
418 | --exec | --exe | --ex)
419 ac_prev=exec_prefix ;;
420 -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
421 | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
422 | --exec=* | --exe=* | --ex=*)
423 exec_prefix=$ac_optarg ;;
424
425 -gas | --gas | --ga | --g)
426 # Obsolete; use --with-gas.
427 with_gas=yes ;;
428
429 -help | --help | --hel | --he | -h)
430 ac_init_help=long ;;
431 -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
432 ac_init_help=recursive ;;
433 -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
434 ac_init_help=short ;;
435
436 -host | --host | --hos | --ho)
437 ac_prev=host_alias ;;
438 -host=* | --host=* | --hos=* | --ho=*)
439 host_alias=$ac_optarg ;;
440
441 -includedir | --includedir | --includedi | --included | --include \
442 | --includ | --inclu | --incl | --inc)
443 ac_prev=includedir ;;
444 -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
445 | --includ=* | --inclu=* | --incl=* | --inc=*)
446 includedir=$ac_optarg ;;
447
448 -infodir | --infodir | --infodi | --infod | --info | --inf)
449 ac_prev=infodir ;;
450 -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
451 infodir=$ac_optarg ;;
452
453 -libdir | --libdir | --libdi | --libd)
454 ac_prev=libdir ;;
455 -libdir=* | --libdir=* | --libdi=* | --libd=*)
456 libdir=$ac_optarg ;;
457
458 -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
459 | --libexe | --libex | --libe)
460 ac_prev=libexecdir ;;
461 -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
462 | --libexe=* | --libex=* | --libe=*)
463 libexecdir=$ac_optarg ;;
464
465 -localstatedir | --localstatedir | --localstatedi | --localstated \
466 | --localstate | --localstat | --localsta | --localst \
467 | --locals | --local | --loca | --loc | --lo)
468 ac_prev=localstatedir ;;
469 -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
470 | --localstate=* | --localstat=* | --localsta=* | --localst=* \
471 | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
472 localstatedir=$ac_optarg ;;
473
474 -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
475 ac_prev=mandir ;;
476 -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
477 mandir=$ac_optarg ;;
478
479 -nfp | --nfp | --nf)
480 # Obsolete; use --without-fp.
481 with_fp=no ;;
482
483 -no-create | --no-create | --no-creat | --no-crea | --no-cre \
484 | --no-cr | --no-c | -n)
485 no_create=yes ;;
486
487 -no-recursion | --no-recursion | --no-recursio | --no-recursi \
488 | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
489 no_recursion=yes ;;
490
491 -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
492 | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
493 | --oldin | --oldi | --old | --ol | --o)
494 ac_prev=oldincludedir ;;
495 -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
496 | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
497 | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
498 oldincludedir=$ac_optarg ;;
499
500 -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
501 ac_prev=prefix ;;
502 -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
503 prefix=$ac_optarg ;;
504
505 -program-prefix | --program-prefix | --program-prefi | --program-pref \
506 | --program-pre | --program-pr | --program-p)
507 ac_prev=program_prefix ;;
508 -program-prefix=* | --program-prefix=* | --program-prefi=* \
509 | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
510 program_prefix=$ac_optarg ;;
511
512 -program-suffix | --program-suffix | --program-suffi | --program-suff \
513 | --program-suf | --program-su | --program-s)
514 ac_prev=program_suffix ;;
515 -program-suffix=* | --program-suffix=* | --program-suffi=* \
516 | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
517 program_suffix=$ac_optarg ;;
518
519 -program-transform-name | --program-transform-name \
520 | --program-transform-nam | --program-transform-na \
521 | --program-transform-n | --program-transform- \
522 | --program-transform | --program-transfor \
523 | --program-transfo | --program-transf \
524 | --program-trans | --program-tran \
525 | --progr-tra | --program-tr | --program-t)
526 ac_prev=program_transform_name ;;
527 -program-transform-name=* | --program-transform-name=* \
528 | --program-transform-nam=* | --program-transform-na=* \
529 | --program-transform-n=* | --program-transform-=* \
530 | --program-transform=* | --program-transfor=* \
531 | --program-transfo=* | --program-transf=* \
532 | --program-trans=* | --program-tran=* \
533 | --progr-tra=* | --program-tr=* | --program-t=*)
534 program_transform_name=$ac_optarg ;;
535
536 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
537 | -silent | --silent | --silen | --sile | --sil)
538 silent=yes ;;
539
540 -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
541 ac_prev=sbindir ;;
542 -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
543 | --sbi=* | --sb=*)
544 sbindir=$ac_optarg ;;
545
546 -sharedstatedir | --sharedstatedir | --sharedstatedi \
547 | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
548 | --sharedst | --shareds | --shared | --share | --shar \
549 | --sha | --sh)
550 ac_prev=sharedstatedir ;;
551 -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
552 | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
553 | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
554 | --sha=* | --sh=*)
555 sharedstatedir=$ac_optarg ;;
556
557 -site | --site | --sit)
558 ac_prev=site ;;
559 -site=* | --site=* | --sit=*)
560 site=$ac_optarg ;;
561
562 -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
563 ac_prev=srcdir ;;
564 -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
565 srcdir=$ac_optarg ;;
566
567 -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
568 | --syscon | --sysco | --sysc | --sys | --sy)
569 ac_prev=sysconfdir ;;
570 -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
571 | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
572 sysconfdir=$ac_optarg ;;
573
574 -target | --target | --targe | --targ | --tar | --ta | --t)
575 ac_prev=target_alias ;;
576 -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
577 target_alias=$ac_optarg ;;
578
579 -v | -verbose | --verbose | --verbos | --verbo | --verb)
580 verbose=yes ;;
581
582 -version | --version | --versio | --versi | --vers | -V)
583 ac_init_version=: ;;
584
585 -with-* | --with-*)
586 ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
587 # Reject names that are not valid shell variable names.
588 expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
589 { echo "$as_me: error: invalid package name: $ac_package" >&2
590 { (exit 1); exit 1; }; }
591 ac_package=`echo $ac_package| sed 's/-/_/g'`
592 case $ac_option in
593 *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
594 *) ac_optarg=yes ;;
595 esac
596 eval "with_$ac_package='$ac_optarg'" ;;
597
598 -without-* | --without-*)
599 ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
600 # Reject names that are not valid shell variable names.
601 expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
602 { echo "$as_me: error: invalid package name: $ac_package" >&2
603 { (exit 1); exit 1; }; }
604 ac_package=`echo $ac_package | sed 's/-/_/g'`
605 eval "with_$ac_package=no" ;;
606
607 --x)
608 # Obsolete; use --with-x.
609 with_x=yes ;;
610
611 -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
612 | --x-incl | --x-inc | --x-in | --x-i)
613 ac_prev=x_includes ;;
614 -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
615 | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
616 x_includes=$ac_optarg ;;
617
618 -x-libraries | --x-libraries | --x-librarie | --x-librari \
619 | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
620 ac_prev=x_libraries ;;
621 -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
622 | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
623 x_libraries=$ac_optarg ;;
624
625 -*) { echo "$as_me: error: unrecognized option: $ac_option
626 Try \`$0 --help' for more information." >&2
627 { (exit 1); exit 1; }; }
628 ;;
629
630 *=*)
631 ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
632 # Reject names that are not valid shell variable names.
633 expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
634 { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
635 { (exit 1); exit 1; }; }
636 ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
637 eval "$ac_envvar='$ac_optarg'"
638 export $ac_envvar ;;
639
640 *)
641 # FIXME: should be removed in autoconf 3.0.
642 echo "$as_me: WARNING: you should use --build, --host, --target" >&2
643 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
644 echo "$as_me: WARNING: invalid host type: $ac_option" >&2
645 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
646 ;;
647
648 esac
649 done
650
651 if test -n "$ac_prev"; then
652 ac_option=--`echo $ac_prev | sed 's/_/-/g'`
653 { echo "$as_me: error: missing argument to $ac_option" >&2
654 { (exit 1); exit 1; }; }
655 fi
656
657 # Be sure to have absolute paths.
658 for ac_var in exec_prefix prefix
659 do
660 eval ac_val=$`echo $ac_var`
661 case $ac_val in
662 [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
663 *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
664 { (exit 1); exit 1; }; };;
665 esac
666 done
667
668 # Be sure to have absolute paths.
669 for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
670 localstatedir libdir includedir oldincludedir infodir mandir
671 do
672 eval ac_val=$`echo $ac_var`
673 case $ac_val in
674 [\\/$]* | ?:[\\/]* ) ;;
675 *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
676 { (exit 1); exit 1; }; };;
677 esac
678 done
679
680 # There might be people who depend on the old broken behavior: `$host'
681 # used to hold the argument of --host etc.
682 # FIXME: To remove some day.
683 build=$build_alias
684 host=$host_alias
685 target=$target_alias
686
687 # FIXME: To remove some day.
688 if test "x$host_alias" != x; then
689 if test "x$build_alias" = x; then
690 cross_compiling=maybe
691 echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
692 If a cross compiler is detected then cross compile mode will be used." >&2
693 elif test "x$build_alias" != "x$host_alias"; then
694 cross_compiling=yes
695 fi
696 fi
697
698 ac_tool_prefix=
699 test -n "$host_alias" && ac_tool_prefix=$host_alias-
700
701 test "$silent" = yes && exec 6>/dev/null
702
703
704 # Find the source files, if location was not specified.
705 if test -z "$srcdir"; then
706 ac_srcdir_defaulted=yes
707 # Try the directory containing this script, then its parent.
708 ac_confdir=`(dirname "$0") 2>/dev/null ||
709 $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
710 X"$0" : 'X\(//\)[^/]' \| \
711 X"$0" : 'X\(//\)$' \| \
712 X"$0" : 'X\(/\)' \| \
713 . : '\(.\)' 2>/dev/null ||
714 echo X"$0" |
715 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
716 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
717 /^X\(\/\/\)$/{ s//\1/; q; }
718 /^X\(\/\).*/{ s//\1/; q; }
719 s/.*/./; q'`
720 srcdir=$ac_confdir
721 if test ! -r $srcdir/$ac_unique_file; then
722 srcdir=..
723 fi
724 else
725 ac_srcdir_defaulted=no
726 fi
727 if test ! -r $srcdir/$ac_unique_file; then
728 if test "$ac_srcdir_defaulted" = yes; then
729 { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
730 { (exit 1); exit 1; }; }
731 else
732 { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
733 { (exit 1); exit 1; }; }
734 fi
735 fi
736 (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
737 { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
738 { (exit 1); exit 1; }; }
739 srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
740 ac_env_build_alias_set=${build_alias+set}
741 ac_env_build_alias_value=$build_alias
742 ac_cv_env_build_alias_set=${build_alias+set}
743 ac_cv_env_build_alias_value=$build_alias
744 ac_env_host_alias_set=${host_alias+set}
745 ac_env_host_alias_value=$host_alias
746 ac_cv_env_host_alias_set=${host_alias+set}
747 ac_cv_env_host_alias_value=$host_alias
748 ac_env_target_alias_set=${target_alias+set}
749 ac_env_target_alias_value=$target_alias
750 ac_cv_env_target_alias_set=${target_alias+set}
751 ac_cv_env_target_alias_value=$target_alias
752 ac_env_CC_set=${CC+set}
753 ac_env_CC_value=$CC
754 ac_cv_env_CC_set=${CC+set}
755 ac_cv_env_CC_value=$CC
756 ac_env_CFLAGS_set=${CFLAGS+set}
757 ac_env_CFLAGS_value=$CFLAGS
758 ac_cv_env_CFLAGS_set=${CFLAGS+set}
759 ac_cv_env_CFLAGS_value=$CFLAGS
760 ac_env_LDFLAGS_set=${LDFLAGS+set}
761 ac_env_LDFLAGS_value=$LDFLAGS
762 ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
763 ac_cv_env_LDFLAGS_value=$LDFLAGS
764 ac_env_CPPFLAGS_set=${CPPFLAGS+set}
765 ac_env_CPPFLAGS_value=$CPPFLAGS
766 ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
767 ac_cv_env_CPPFLAGS_value=$CPPFLAGS
768 ac_env_CPP_set=${CPP+set}
769 ac_env_CPP_value=$CPP
770 ac_cv_env_CPP_set=${CPP+set}
771 ac_cv_env_CPP_value=$CPP
772 ac_env_CXX_set=${CXX+set}
773 ac_env_CXX_value=$CXX
774 ac_cv_env_CXX_set=${CXX+set}
775 ac_cv_env_CXX_value=$CXX
776 ac_env_CXXFLAGS_set=${CXXFLAGS+set}
777 ac_env_CXXFLAGS_value=$CXXFLAGS
778 ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
779 ac_cv_env_CXXFLAGS_value=$CXXFLAGS
780
781 #
782 # Report the --help message.
783 #
784 if test "$ac_init_help" = "long"; then
785 # Omit some internal or obsolete options to make the list less imposing.
786 # This message is too long to be a string in the A/UX 3.1 sh.
787 cat <<_ACEOF
788 \`configure' configures this package to adapt to many kinds of systems.
789
790 Usage: $0 [OPTION]... [VAR=VALUE]...
791
792 To assign environment variables (e.g., CC, CFLAGS...), specify them as
793 VAR=VALUE. See below for descriptions of some of the useful variables.
794
795 Defaults for the options are specified in brackets.
796
797 Configuration:
798 -h, --help display this help and exit
799 --help=short display options specific to this package
800 --help=recursive display the short help of all the included packages
801 -V, --version display version information and exit
802 -q, --quiet, --silent do not print \`checking...' messages
803 --cache-file=FILE cache test results in FILE [disabled]
804 -C, --config-cache alias for \`--cache-file=config.cache'
805 -n, --no-create do not create output files
806 --srcdir=DIR find the sources in DIR [configure dir or \`..']
807
808 _ACEOF
809
810 cat <<_ACEOF
811 Installation directories:
812 --prefix=PREFIX install architecture-independent files in PREFIX
813 [$ac_default_prefix]
814 --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
815 [PREFIX]
816
817 By default, \`make install' will install all the files in
818 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
819 an installation prefix other than \`$ac_default_prefix' using \`--prefix',
820 for instance \`--prefix=\$HOME'.
821
822 For better control, use the options below.
823
824 Fine tuning of the installation directories:
825 --bindir=DIR user executables [EPREFIX/bin]
826 --sbindir=DIR system admin executables [EPREFIX/sbin]
827 --libexecdir=DIR program executables [EPREFIX/libexec]
828 --datadir=DIR read-only architecture-independent data [PREFIX/share]
829 --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
830 --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
831 --localstatedir=DIR modifiable single-machine data [PREFIX/var]
832 --libdir=DIR object code libraries [EPREFIX/lib]
833 --includedir=DIR C header files [PREFIX/include]
834 --oldincludedir=DIR C header files for non-gcc [/usr/include]
835 --infodir=DIR info documentation [PREFIX/info]
836 --mandir=DIR man documentation [PREFIX/man]
837 _ACEOF
838
839 cat <<\_ACEOF
840
841 Program names:
842 --program-prefix=PREFIX prepend PREFIX to installed program names
843 --program-suffix=SUFFIX append SUFFIX to installed program names
844 --program-transform-name=PROGRAM run sed PROGRAM on installed program names
845
846 System types:
847 --build=BUILD configure for building on BUILD [guessed]
848 --host=HOST cross-compile to build programs to run on HOST [BUILD]
849 --target=TARGET configure for building compilers for TARGET [HOST]
850 _ACEOF
851 fi
852
853 if test -n "$ac_init_help"; then
854
855 cat <<\_ACEOF
856
857 Optional Features:
858 --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
859 --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
860 --enable-efence Try to compile and run with Electric Fence
861 --enable-gprof Try to compile and run with profiling enabled
862 --enable-insure Try to compile and run with Insure++
863 --enable-ansi Try to compile and run with ANSI flags
864 --enable-ipv6 Build with support for IPv6
865 --enable-rtfl Build with rtfl messages
866 --disable-cookies Don't compile support for cookies
867 --disable-png Disable support for PNG images
868 --disable-jpeg Disable support for JPEG images
869 --disable-gif Disable support for GIF images
870 --disable-ssl Disable ssl features (eg. https)
871 --disable-dlgui Disable FLTK2 GUI for downloads
872 --disable-threaded-dns Disable the advantage of a reentrant resolver library
873 --disable-dependency-tracking speeds up one-time build
874 --enable-dependency-tracking do not reject slow dependency extractors
875 --disable-glibtest Do not try to compile and run a test GLIB program
876 --disable-gtktest Do not try to compile and run a test GTK program
877
878 Optional Packages:
879 --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
880 --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
881 --with-jpeg-lib=DIR Specify where to find libjpeg
882 --with-jpeg-inc=DIR Specify where to find libjpeg's headers
883 --with-glib-prefix=PFX Prefix where GLIB is installed (optional)
884 --with-glib-exec-prefix=PFX Exec prefix where GLIB is installed (optional)
885 --with-gtk-prefix=PFX Prefix where GTK is installed (optional)
886 --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)
887
888 Some influential environment variables:
889 CC C compiler command
890 CFLAGS C compiler flags
891 LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
892 nonstandard directory <lib dir>
893 CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
894 headers in a nonstandard directory <include dir>
895 CPP C preprocessor
896 CXX C++ compiler command
897 CXXFLAGS C++ compiler flags
898
899 Use these variables to override the choices made by `configure' or to help
900 it to find libraries and programs with nonstandard names/locations.
901
902 _ACEOF
903 fi
904
905 if test "$ac_init_help" = "recursive"; then
906 # If there are subdirs, report their specific --help.
907 ac_popdir=`pwd`
908 for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
909 test -d $ac_dir || continue
910 ac_builddir=.
911
912 if test "$ac_dir" != .; then
913 ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
914 # A "../" for each directory in $ac_dir_suffix.
915 ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
916 else
917 ac_dir_suffix= ac_top_builddir=
918 fi
919
920 case $srcdir in
921 .) # No --srcdir option. We are building in place.
922 ac_srcdir=.
923 if test -z "$ac_top_builddir"; then
924 ac_top_srcdir=.
925 else
926 ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
927 fi ;;
928 [\\/]* | ?:[\\/]* ) # Absolute path.
929 ac_srcdir=$srcdir$ac_dir_suffix;
930 ac_top_srcdir=$srcdir ;;
931 *) # Relative path.
932 ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
933 ac_top_srcdir=$ac_top_builddir$srcdir ;;
934 esac
935
936 # Do not use `cd foo && pwd` to compute absolute paths, because
937 # the directories may not exist.
938 case `pwd` in
939 .) ac_abs_builddir="$ac_dir";;
940 *)
941 case "$ac_dir" in
942 .) ac_abs_builddir=`pwd`;;
943 [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
944 *) ac_abs_builddir=`pwd`/"$ac_dir";;
945 esac;;
946 esac
947 case $ac_abs_builddir in
948 .) ac_abs_top_builddir=${ac_top_builddir}.;;
949 *)
950 case ${ac_top_builddir}. in
951 .) ac_abs_top_builddir=$ac_abs_builddir;;
952 [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
953 *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
954 esac;;
955 esac
956 case $ac_abs_builddir in
957 .) ac_abs_srcdir=$ac_srcdir;;
958 *)
959 case $ac_srcdir in
960 .) ac_abs_srcdir=$ac_abs_builddir;;
961 [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
962 *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
963 esac;;
964 esac
965 case $ac_abs_builddir in
966 .) ac_abs_top_srcdir=$ac_top_srcdir;;
967 *)
968 case $ac_top_srcdir in
969 .) ac_abs_top_srcdir=$ac_abs_builddir;;
970 [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
971 *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
972 esac;;
973 esac
974
975 cd $ac_dir
976 # Check for guested configure; otherwise get Cygnus style configure.
977 if test -f $ac_srcdir/configure.gnu; then
978 echo
979 $SHELL $ac_srcdir/configure.gnu --help=recursive
980 elif test -f $ac_srcdir/configure; then
981 echo
982 $SHELL $ac_srcdir/configure --help=recursive
983 elif test -f $ac_srcdir/configure.ac ||
984 test -f $ac_srcdir/configure.in; then
985 echo
986 $ac_configure --help
987 else
988 echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
989 fi
990 cd $ac_popdir
991 done
992 fi
993
994 test -n "$ac_init_help" && exit 0
995 if $ac_init_version; then
996 cat <<\_ACEOF
997
998 Copyright (C) 2003 Free Software Foundation, Inc.
999 This configure script is free software; the Free Software Foundation
1000 gives unlimited permission to copy, distribute and modify it.
1001 _ACEOF
1002 exit 0
1003 fi
1004 exec 5>config.log
1005 cat >&5 <<_ACEOF
1006 This file contains any messages produced by compilers while
1007 running configure, to aid debugging if configure makes a mistake.
1008
1009 It was created by $as_me, which was
1010 generated by GNU Autoconf 2.59. Invocation command line was
1011
1012 $ $0 $@
1013
1014 _ACEOF
1015 {
1016 cat <<_ASUNAME
1017 ## --------- ##
1018 ## Platform. ##
1019 ## --------- ##
1020
1021 hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
1022 uname -m = `(uname -m) 2>/dev/null || echo unknown`
1023 uname -r = `(uname -r) 2>/dev/null || echo unknown`
1024 uname -s = `(uname -s) 2>/dev/null || echo unknown`
1025 uname -v = `(uname -v) 2>/dev/null || echo unknown`
1026
1027 /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
1028 /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
1029
1030 /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
1031 /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
1032 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
1033 hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
1034 /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
1035 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
1036 /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
1037
1038 _ASUNAME
1039
1040 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1041 for as_dir in $PATH
1042 do
1043 IFS=$as_save_IFS
1044 test -z "$as_dir" && as_dir=.
1045 echo "PATH: $as_dir"
1046 done
1047
1048 } >&5
1049
1050 cat >&5 <<_ACEOF
1051
1052
1053 ## ----------- ##
1054 ## Core tests. ##
1055 ## ----------- ##
1056
1057 _ACEOF
1058
1059
1060 # Keep a trace of the command line.
1061 # Strip out --no-create and --no-recursion so they do not pile up.
1062 # Strip out --silent because we don't want to record it for future runs.
1063 # Also quote any args containing shell meta-characters.
1064 # Make two passes to allow for proper duplicate-argument suppression.
1065 ac_configure_args=
1066 ac_configure_args0=
1067 ac_configure_args1=
1068 ac_sep=
1069 ac_must_keep_next=false
1070 for ac_pass in 1 2
1071 do
1072 for ac_arg
1073 do
1074 case $ac_arg in
1075 -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
1076 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
1077 | -silent | --silent | --silen | --sile | --sil)
1078 continue ;;
1079 *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
1080 ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
1081 esac
1082 case $ac_pass in
1083 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
1084 2)
1085 ac_configure_args1="$ac_configure_args1 '$ac_arg'"
1086 if test $ac_must_keep_next = true; then
1087 ac_must_keep_next=false # Got value, back to normal.
1088 else
1089 case $ac_arg in
1090 *=* | --config-cache | -C | -disable-* | --disable-* \
1091 | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
1092 | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
1093 | -with-* | --with-* | -without-* | --without-* | --x)
1094 case "$ac_configure_args0 " in
1095 "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
1096 esac
1097 ;;
1098 -* ) ac_must_keep_next=true ;;
1099 esac
1100 fi
1101 ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
1102 # Get rid of the leading space.
1103 ac_sep=" "
1104 ;;
1105 esac
1106 done
1107 done
1108 $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
1109 $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
1110
1111 # When interrupted or exit'd, cleanup temporary files, and complete
1112 # config.log. We remove comments because anyway the quotes in there
1113 # would cause problems or look ugly.
1114 # WARNING: Be sure not to use single quotes in there, as some shells,
1115 # such as our DU 5.0 friend, will then `close' the trap.
1116 trap 'exit_status=$?
1117 # Save into config.log some information that might help in debugging.
1118 {
1119 echo
1120
1121 cat <<\_ASBOX
1122 ## ---------------- ##
1123 ## Cache variables. ##
1124 ## ---------------- ##
1125 _ASBOX
1126 echo
1127 # The following way of writing the cache mishandles newlines in values,
1128 {
1129 (set) 2>&1 |
1130 case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
1131 *ac_space=\ *)
1132 sed -n \
1133 "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
1134 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
1135 ;;
1136 *)
1137 sed -n \
1138 "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
1139 ;;
1140 esac;
1141 }
1142 echo
1143
1144 cat <<\_ASBOX
1145 ## ----------------- ##
1146 ## Output variables. ##
1147 ## ----------------- ##
1148 _ASBOX
1149 echo
1150 for ac_var in $ac_subst_vars
1151 do
1152 eval ac_val=$`echo $ac_var`
1153 echo "$ac_var='"'"'$ac_val'"'"'"
1154 done | sort
1155 echo
1156
1157 if test -n "$ac_subst_files"; then
1158 cat <<\_ASBOX
1159 ## ------------- ##
1160 ## Output files. ##
1161 ## ------------- ##
1162 _ASBOX
1163 echo
1164 for ac_var in $ac_subst_files
1165 do
1166 eval ac_val=$`echo $ac_var`
1167 echo "$ac_var='"'"'$ac_val'"'"'"
1168 done | sort
1169 echo
1170 fi
1171
1172 if test -s confdefs.h; then
1173 cat <<\_ASBOX
1174 ## ----------- ##
1175 ## confdefs.h. ##
1176 ## ----------- ##
1177 _ASBOX
1178 echo
1179 sed "/^$/d" confdefs.h | sort
1180 echo
1181 fi
1182 test "$ac_signal" != 0 &&
1183 echo "$as_me: caught signal $ac_signal"
1184 echo "$as_me: exit $exit_status"
1185 } >&5
1186 rm -f core *.core &&
1187 rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
1188 exit $exit_status
1189 ' 0
1190 for ac_signal in 1 2 13 15; do
1191 trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
1192 done
1193 ac_signal=0
1194
1195 # confdefs.h avoids OS command line length limits that DEFS can exceed.
1196 rm -rf conftest* confdefs.h
1197 # AIX cpp loses on an empty file, so make sure it contains at least a newline.
1198 echo >confdefs.h
1199
1200 # Predefined preprocessor variables.
1201
1202 cat >>confdefs.h <<_ACEOF
1203 #define PACKAGE_NAME "$PACKAGE_NAME"
1204 _ACEOF
1205
1206
1207 cat >>confdefs.h <<_ACEOF
1208 #define PACKAGE_TARNAME "$PACKAGE_TARNAME"
1209 _ACEOF
1210
1211
1212 cat >>confdefs.h <<_ACEOF
1213 #define PACKAGE_VERSION "$PACKAGE_VERSION"
1214 _ACEOF
1215
1216
1217 cat >>confdefs.h <<_ACEOF
1218 #define PACKAGE_STRING "$PACKAGE_STRING"
1219 _ACEOF
1220
1221
1222 cat >>confdefs.h <<_ACEOF
1223 #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
1224 _ACEOF
1225
1226
1227 # Let the site file select an alternate cache file if it wants to.
1228 # Prefer explicitly selected file to automatically selected ones.
1229 if test -z "$CONFIG_SITE"; then
1230 if test "x$prefix" != xNONE; then
1231 CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
1232 else
1233 CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
1234 fi
1235 fi
1236 for ac_site_file in $CONFIG_SITE; do
1237 if test -r "$ac_site_file"; then
1238 { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
1239 echo "$as_me: loading site script $ac_site_file" >&6;}
1240 sed 's/^/| /' "$ac_site_file" >&5
1241 . "$ac_site_file"
1242 fi
1243 done
1244
1245 if test -r "$cache_file"; then
1246 # Some versions of bash will fail to source /dev/null (special
1247 # files actually), so we avoid doing that.
1248 if test -f "$cache_file"; then
1249 { echo "$as_me:$LINENO: loading cache $cache_file" >&5
1250 echo "$as_me: loading cache $cache_file" >&6;}
1251 case $cache_file in
1252 [\\/]* | ?:[\\/]* ) . $cache_file;;
1253 *) . ./$cache_file;;
1254 esac
1255 fi
1256 else
1257 { echo "$as_me:$LINENO: creating cache $cache_file" >&5
1258 echo "$as_me: creating cache $cache_file" >&6;}
1259 >$cache_file
1260 fi
1261
1262 # Check that the precious variables saved in the cache have kept the same
1263 # value.
1264 ac_cache_corrupted=false
1265 for ac_var in `(set) 2>&1 |
1266 sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
1267 eval ac_old_set=\$ac_cv_env_${ac_var}_set
1268 eval ac_new_set=\$ac_env_${ac_var}_set
1269 eval ac_old_val="\$ac_cv_env_${ac_var}_value"
1270 eval ac_new_val="\$ac_env_${ac_var}_value"
1271 case $ac_old_set,$ac_new_set in
1272 set,)
1273 { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
1274 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
1275 ac_cache_corrupted=: ;;
1276 ,set)
1277 { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
1278 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
1279 ac_cache_corrupted=: ;;
1280 ,);;
1281 *)
1282 if test "x$ac_old_val" != "x$ac_new_val"; then
1283 { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
1284 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
1285 { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
1286 echo "$as_me: former value: $ac_old_val" >&2;}
1287 { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
1288 echo "$as_me: current value: $ac_new_val" >&2;}
1289 ac_cache_corrupted=:
1290 fi;;
1291 esac
1292 # Pass precious variables to config.status.
1293 if test "$ac_new_set" = set; then
1294 case $ac_new_val in
1295 *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
1296 ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
1297 *) ac_arg=$ac_var=$ac_new_val ;;
1298 esac
1299 case " $ac_configure_args " in
1300 *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
1301 *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
1302 esac
1303 fi
1304 done
1305 if $ac_cache_corrupted; then
1306 { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
1307 echo "$as_me: error: changes in the environment can compromise the build" >&2;}
1308 { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
1309 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
1310 { (exit 1); exit 1; }; }
1311 fi
1312
1313 ac_ext=c
1314 ac_cpp='$CPP $CPPFLAGS'
1315 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
1316 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
1317 ac_compiler_gnu=$ac_cv_c_compiler_gnu
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338 ac_aux_dir=
1339 for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
1340 if test -f $ac_dir/install-sh; then
1341 ac_aux_dir=$ac_dir
1342 ac_install_sh="$ac_aux_dir/install-sh -c"
1343 break
1344 elif test -f $ac_dir/install.sh; then
1345 ac_aux_dir=$ac_dir
1346 ac_install_sh="$ac_aux_dir/install.sh -c"
1347 break
1348 elif test -f $ac_dir/shtool; then
1349 ac_aux_dir=$ac_dir
1350 ac_install_sh="$ac_aux_dir/shtool install -c"
1351 break
1352 fi
1353 done
1354 if test -z "$ac_aux_dir"; then
1355 { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
1356 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
1357 { (exit 1); exit 1; }; }
1358 fi
1359 ac_config_guess="$SHELL $ac_aux_dir/config.guess"
1360 ac_config_sub="$SHELL $ac_aux_dir/config.sub"
1361 ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
1362
1363 # Make sure we can run config.sub.
1364 $ac_config_sub sun4 >/dev/null 2>&1 ||
1365 { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
1366 echo "$as_me: error: cannot run $ac_config_sub" >&2;}
1367 { (exit 1); exit 1; }; }
1368
1369 echo "$as_me:$LINENO: checking build system type" >&5
1370 echo $ECHO_N "checking build system type... $ECHO_C" >&6
1371 if test "${ac_cv_build+set}" = set; then
1372 echo $ECHO_N "(cached) $ECHO_C" >&6
1373 else
1374 ac_cv_build_alias=$build_alias
1375 test -z "$ac_cv_build_alias" &&
1376 ac_cv_build_alias=`$ac_config_guess`
1377 test -z "$ac_cv_build_alias" &&
1378 { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
1379 echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
1380 { (exit 1); exit 1; }; }
1381 ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
1382 { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
1383 echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
1384 { (exit 1); exit 1; }; }
1385
1386 fi
1387 echo "$as_me:$LINENO: result: $ac_cv_build" >&5
1388 echo "${ECHO_T}$ac_cv_build" >&6
1389 build=$ac_cv_build
1390 build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
1391 build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
1392 build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
1393
1394
1395 echo "$as_me:$LINENO: checking host system type" >&5
1396 echo $ECHO_N "checking host system type... $ECHO_C" >&6
1397 if test "${ac_cv_host+set}" = set; then
1398 echo $ECHO_N "(cached) $ECHO_C" >&6
1399 else
1400 ac_cv_host_alias=$host_alias
1401 test -z "$ac_cv_host_alias" &&
1402 ac_cv_host_alias=$ac_cv_build_alias
1403 ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
1404 { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
1405 echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
1406 { (exit 1); exit 1; }; }
1407
1408 fi
1409 echo "$as_me:$LINENO: result: $ac_cv_host" >&5
1410 echo "${ECHO_T}$ac_cv_host" >&6
1411 host=$ac_cv_host
1412 host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
1413 host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
1414 host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
1415
1416
1417 echo "$as_me:$LINENO: checking target system type" >&5
1418 echo $ECHO_N "checking target system type... $ECHO_C" >&6
1419 if test "${ac_cv_target+set}" = set; then
1420 echo $ECHO_N "(cached) $ECHO_C" >&6
1421 else
1422 ac_cv_target_alias=$target_alias
1423 test "x$ac_cv_target_alias" = "x" &&
1424 ac_cv_target_alias=$ac_cv_host_alias
1425 ac_cv_target=`$ac_config_sub $ac_cv_target_alias` ||
1426 { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5
1427 echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;}
1428 { (exit 1); exit 1; }; }
1429
1430 fi
1431 echo "$as_me:$LINENO: result: $ac_cv_target" >&5
1432 echo "${ECHO_T}$ac_cv_target" >&6
1433 target=$ac_cv_target
1434 target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
1435 target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
1436 target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
1437
1438
1439 # The aliases save the names the user supplied, while $host etc.
1440 # will get canonicalized.
1441 test -n "$target_alias" &&
1442 test "$program_prefix$program_suffix$program_transform_name" = \
1443 NONENONEs,x,x, &&
1444 program_prefix=${target_alias}-
1445
1446 am__api_version="1.9"
1447 # Find a good install program. We prefer a C program (faster),
1448 # so one script is as good as another. But avoid the broken or
1449 # incompatible versions:
1450 # SysV /etc/install, /usr/sbin/install
1451 # SunOS /usr/etc/install
1452 # IRIX /sbin/install
1453 # AIX /bin/install
1454 # AmigaOS /C/install, which installs bootblocks on floppy discs
1455 # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
1456 # AFS /usr/afsws/bin/install, which mishandles nonexistent args
1457 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
1458 # OS/2's system install, which has a completely different semantic
1459 # ./install, which can be erroneously created by make from ./install.sh.
1460 echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
1461 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
1462 if test -z "$INSTALL"; then
1463 if test "${ac_cv_path_install+set}" = set; then
1464 echo $ECHO_N "(cached) $ECHO_C" >&6
1465 else
1466 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1467 for as_dir in $PATH
1468 do
1469 IFS=$as_save_IFS
1470 test -z "$as_dir" && as_dir=.
1471 # Account for people who put trailing slashes in PATH elements.
1472 case $as_dir/ in
1473 ./ | .// | /cC/* | \
1474 /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
1475 ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
1476 /usr/ucb/* ) ;;
1477 *)
1478 # OSF1 and SCO ODT 3.0 have their own names for install.
1479 # Don't use installbsd from OSF since it installs stuff as root
1480 # by default.
1481 for ac_prog in ginstall scoinst install; do
1482 for ac_exec_ext in '' $ac_executable_extensions; do
1483 if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
1484 if test $ac_prog = install &&
1485 grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
1486 # AIX install. It has an incompatible calling convention.
1487 :
1488 elif test $ac_prog = install &&
1489 grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
1490 # program-specific install script used by HP pwplus--don't use.
1491 :
1492 else
1493 ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
1494 break 3
1495 fi
1496 fi
1497 done
1498 done
1499 ;;
1500 esac
1501 done
1502
1503
1504 fi
1505 if test "${ac_cv_path_install+set}" = set; then
1506 INSTALL=$ac_cv_path_install
1507 else
1508 # As a last resort, use the slow shell script. We don't cache a
1509 # path for INSTALL within a source directory, because that will
1510 # break other packages using the cache if that directory is
1511 # removed, or if the path is relative.
1512 INSTALL=$ac_install_sh
1513 fi
1514 fi
1515 echo "$as_me:$LINENO: result: $INSTALL" >&5
1516 echo "${ECHO_T}$INSTALL" >&6
1517
1518 # Use test -z because SunOS4 sh mishandles braces in ${var-val}.
1519 # It thinks the first close brace ends the variable substitution.
1520 test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
1521
1522 test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
1523
1524 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
1525
1526 echo "$as_me:$LINENO: checking whether build environment is sane" >&5
1527 echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6
1528 # Just in case
1529 sleep 1
1530 echo timestamp > conftest.file
1531 # Do `set' in a subshell so we don't clobber the current shell's
1532 # arguments. Must try -L first in case configure is actually a
1533 # symlink; some systems play weird games with the mod time of symlinks
1534 # (eg FreeBSD returns the mod time of the symlink's containing
1535 # directory).
1536 if (
1537 set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
1538 if test "$*" = "X"; then
1539 # -L didn't work.
1540 set X `ls -t $srcdir/configure conftest.file`
1541 fi
1542 rm -f conftest.file
1543 if test "$*" != "X $srcdir/configure conftest.file" \
1544 && test "$*" != "X conftest.file $srcdir/configure"; then
1545
1546 # If neither matched, then we have a broken ls. This can happen
1547 # if, for instance, CONFIG_SHELL is bash and it inherits a
1548 # broken ls alias from the environment. This has actually
1549 # happened. Such a system could not be considered "sane".
1550 { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken
1551 alias in your environment" >&5
1552 echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken
1553 alias in your environment" >&2;}
1554 { (exit 1); exit 1; }; }
1555 fi
1556
1557 test "$2" = conftest.file
1558 )
1559 then
1560 # Ok.
1561 :
1562 else
1563 { { echo "$as_me:$LINENO: error: newly created file is older than distributed files!
1564 Check your system clock" >&5
1565 echo "$as_me: error: newly created file is older than distributed files!
1566 Check your system clock" >&2;}
1567 { (exit 1); exit 1; }; }
1568 fi
1569 echo "$as_me:$LINENO: result: yes" >&5
1570 echo "${ECHO_T}yes" >&6
1571 test "$program_prefix" != NONE &&
1572 program_transform_name="s,^,$program_prefix,;$program_transform_name"
1573 # Use a double $ so make ignores it.
1574 test "$program_suffix" != NONE &&
1575 program_transform_name="s,\$,$program_suffix,;$program_transform_name"
1576 # Double any \ or $. echo might interpret backslashes.
1577 # By default was `s,x,x', remove it if useless.
1578 cat <<\_ACEOF >conftest.sed
1579 s/[\\$]/&&/g;s/;s,x,x,$//
1580 _ACEOF
1581 program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
1582 rm conftest.sed
1583
1584 # expand $ac_aux_dir to an absolute path
1585 am_aux_dir=`cd $ac_aux_dir && pwd`
1586
1587 test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
1588 # Use eval to expand $SHELL
1589 if eval "$MISSING --run true"; then
1590 am_missing_run="$MISSING --run "
1591 else
1592 am_missing_run=
1593 { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
1594 echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
1595 fi
1596
1597 if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
1598 # We used to keeping the `.' as first argument, in order to
1599 # allow $(mkdir_p) to be used without argument. As in
1600 # $(mkdir_p) $(somedir)
1601 # where $(somedir) is conditionally defined. However this is wrong
1602 # for two reasons:
1603 # 1. if the package is installed by a user who cannot write `.'
1604 # make install will fail,
1605 # 2. the above comment should most certainly read
1606 # $(mkdir_p) $(DESTDIR)$(somedir)
1607 # so it does not work when $(somedir) is undefined and
1608 # $(DESTDIR) is not.
1609 # To support the latter case, we have to write
1610 # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
1611 # so the `.' trick is pointless.
1612 mkdir_p='mkdir -p --'
1613 else
1614 # On NextStep and OpenStep, the `mkdir' command does not
1615 # recognize any option. It will interpret all options as
1616 # directories to create, and then abort because `.' already
1617 # exists.
1618 for d in ./-p ./--version;
1619 do
1620 test -d $d && rmdir $d
1621 done
1622 # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
1623 if test -f "$ac_aux_dir/mkinstalldirs"; then
1624 mkdir_p='$(mkinstalldirs)'
1625 else
1626 mkdir_p='$(install_sh) -d'
1627 fi
1628 fi
1629
1630 for ac_prog in gawk mawk nawk awk
1631 do
1632 # Extract the first word of "$ac_prog", so it can be a program name with args.
1633 set dummy $ac_prog; ac_word=$2
1634 echo "$as_me:$LINENO: checking for $ac_word" >&5
1635 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1636 if test "${ac_cv_prog_AWK+set}" = set; then
1637 echo $ECHO_N "(cached) $ECHO_C" >&6
1638 else
1639 if test -n "$AWK"; then
1640 ac_cv_prog_AWK="$AWK" # Let the user override the test.
1641 else
1642 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1643 for as_dir in $PATH
1644 do
1645 IFS=$as_save_IFS
1646 test -z "$as_dir" && as_dir=.
1647 for ac_exec_ext in '' $ac_executable_extensions; do
1648 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1649 ac_cv_prog_AWK="$ac_prog"
1650 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1651 break 2
1652 fi
1653 done
1654 done
1655
1656 fi
1657 fi
1658 AWK=$ac_cv_prog_AWK
1659 if test -n "$AWK"; then
1660 echo "$as_me:$LINENO: result: $AWK" >&5
1661 echo "${ECHO_T}$AWK" >&6
1662 else
1663 echo "$as_me:$LINENO: result: no" >&5
1664 echo "${ECHO_T}no" >&6
1665 fi
1666
1667 test -n "$AWK" && break
1668 done
1669
1670 echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
1671 echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
1672 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
1673 if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
1674 echo $ECHO_N "(cached) $ECHO_C" >&6
1675 else
1676 cat >conftest.make <<\_ACEOF
1677 all:
1678 @echo 'ac_maketemp="$(MAKE)"'
1679 _ACEOF
1680 # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
1681 eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
1682 if test -n "$ac_maketemp"; then
1683 eval ac_cv_prog_make_${ac_make}_set=yes
1684 else
1685 eval ac_cv_prog_make_${ac_make}_set=no
1686 fi
1687 rm -f conftest.make
1688 fi
1689 if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
1690 echo "$as_me:$LINENO: result: yes" >&5
1691 echo "${ECHO_T}yes" >&6
1692 SET_MAKE=
1693 else
1694 echo "$as_me:$LINENO: result: no" >&5
1695 echo "${ECHO_T}no" >&6
1696 SET_MAKE="MAKE=${MAKE-make}"
1697 fi
1698
1699 rm -rf .tst 2>/dev/null
1700 mkdir .tst 2>/dev/null
1701 if test -d .tst; then
1702 am__leading_dot=.
1703 else
1704 am__leading_dot=_
1705 fi
1706 rmdir .tst 2>/dev/null
1707
1708 # test to see if srcdir already configured
1709 if test "`cd $srcdir && pwd`" != "`pwd`" &&
1710 test -f $srcdir/config.status; then
1711 { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
1712 echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
1713 { (exit 1); exit 1; }; }
1714 fi
1715
1716 # test whether we have cygpath
1717 if test -z "$CYGPATH_W"; then
1718 if (cygpath --version) >/dev/null 2>/dev/null; then
1719 CYGPATH_W='cygpath -w'
1720 else
1721 CYGPATH_W=echo
1722 fi
1723 fi
1724
1725
1726 # Define the identity of the package.
1727 PACKAGE=dillo
1728 VERSION=0.8.6
1729
1730
1731 cat >>confdefs.h <<_ACEOF
1732 #define PACKAGE "$PACKAGE"
1733 _ACEOF
1734
1735
1736 cat >>confdefs.h <<_ACEOF
1737 #define VERSION "$VERSION"
1738 _ACEOF
1739
1740 # Some tools Automake needs.
1741
1742 ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
1743
1744
1745 AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
1746
1747
1748 AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
1749
1750
1751 AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
1752
1753
1754 MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
1755
1756 install_sh=${install_sh-"$am_aux_dir/install-sh"}
1757
1758 # Installed binaries are usually stripped using `strip' when the user
1759 # run `make install-strip'. However `strip' might not be the right
1760 # tool to use in cross-compilation environments, therefore Automake
1761 # will honor the `STRIP' environment variable to overrule this program.
1762 if test "$cross_compiling" != no; then
1763 if test -n "$ac_tool_prefix"; then
1764 # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
1765 set dummy ${ac_tool_prefix}strip; ac_word=$2
1766 echo "$as_me:$LINENO: checking for $ac_word" >&5
1767 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1768 if test "${ac_cv_prog_STRIP+set}" = set; then
1769 echo $ECHO_N "(cached) $ECHO_C" >&6
1770 else
1771 if test -n "$STRIP"; then
1772 ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
1773 else
1774 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1775 for as_dir in $PATH
1776 do
1777 IFS=$as_save_IFS
1778 test -z "$as_dir" && as_dir=.
1779 for ac_exec_ext in '' $ac_executable_extensions; do
1780 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1781 ac_cv_prog_STRIP="${ac_tool_prefix}strip"
1782 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1783 break 2
1784 fi
1785 done
1786 done
1787
1788 fi
1789 fi
1790 STRIP=$ac_cv_prog_STRIP
1791 if test -n "$STRIP"; then
1792 echo "$as_me:$LINENO: result: $STRIP" >&5
1793 echo "${ECHO_T}$STRIP" >&6
1794 else
1795 echo "$as_me:$LINENO: result: no" >&5
1796 echo "${ECHO_T}no" >&6
1797 fi
1798
1799 fi
1800 if test -z "$ac_cv_prog_STRIP"; then
1801 ac_ct_STRIP=$STRIP
1802 # Extract the first word of "strip", so it can be a program name with args.
1803 set dummy strip; ac_word=$2
1804 echo "$as_me:$LINENO: checking for $ac_word" >&5
1805 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1806 if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
1807 echo $ECHO_N "(cached) $ECHO_C" >&6
1808 else
1809 if test -n "$ac_ct_STRIP"; then
1810 ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
1811 else
1812 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1813 for as_dir in $PATH
1814 do
1815 IFS=$as_save_IFS
1816 test -z "$as_dir" && as_dir=.
1817 for ac_exec_ext in '' $ac_executable_extensions; do
1818 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1819 ac_cv_prog_ac_ct_STRIP="strip"
1820 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1821 break 2
1822 fi
1823 done
1824 done
1825
1826 test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
1827 fi
1828 fi
1829 ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
1830 if test -n "$ac_ct_STRIP"; then
1831 echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
1832 echo "${ECHO_T}$ac_ct_STRIP" >&6
1833 else
1834 echo "$as_me:$LINENO: result: no" >&5
1835 echo "${ECHO_T}no" >&6
1836 fi
1837
1838 STRIP=$ac_ct_STRIP
1839 else
1840 STRIP="$ac_cv_prog_STRIP"
1841 fi
1842
1843 fi
1844 INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
1845
1846 # We need awk for the "check" target. The system "awk" is bad on
1847 # some platforms.
1848 # Always define AMTAR for backward compatibility.
1849
1850 AMTAR=${AMTAR-"${am_missing_run}tar"}
1851
1852 am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
1853
1854
1855
1856
1857
1858 ac_config_headers="$ac_config_headers config.h"
1859
1860
1861
1862
1863 # Check whether --with-jpeg-lib or --without-jpeg-lib was given.
1864 if test "${with_jpeg_lib+set}" = set; then
1865 withval="$with_jpeg_lib"
1866 LIBJPEG_LIBDIR=$withval
1867 fi;
1868
1869 # Check whether --with-jpeg-inc or --without-jpeg-inc was given.
1870 if test "${with_jpeg_inc+set}" = set; then
1871 withval="$with_jpeg_inc"
1872 LIBJPEG_INCDIR=$withval
1873 fi;
1874
1875 # Check whether --enable-efence or --disable-efence was given.
1876 if test "${enable_efence+set}" = set; then
1877 enableval="$enable_efence"
1878
1879 else
1880 enable_efence=no
1881 fi;
1882 # Check whether --enable-gprof or --disable-gprof was given.
1883 if test "${enable_gprof+set}" = set; then
1884 enableval="$enable_gprof"
1885
1886 else
1887 enable_gprof=no
1888 fi;
1889 # Check whether --enable-insure or --disable-insure was given.
1890 if test "${enable_insure+set}" = set; then
1891 enableval="$enable_insure"
1892
1893 else
1894 enable_insure=no
1895 fi;
1896 # Check whether --enable-ansi or --disable-ansi was given.
1897 if test "${enable_ansi+set}" = set; then
1898 enableval="$enable_ansi"
1899
1900 else
1901 enable_ansi=no
1902 fi;
1903 # Check whether --enable-ipv6 or --disable-ipv6 was given.
1904 if test "${enable_ipv6+set}" = set; then
1905 enableval="$enable_ipv6"
1906
1907 fi;
1908 # Check whether --enable-rtfl or --disable-rtfl was given.
1909 if test "${enable_rtfl+set}" = set; then
1910 enableval="$enable_rtfl"
1911 enable_rtfl=yes
1912 fi;
1913 # Check whether --enable-cookies or --disable-cookies was given.
1914 if test "${enable_cookies+set}" = set; then
1915 enableval="$enable_cookies"
1916
1917 else
1918 enable_cookies=yes
1919 fi;
1920 # Check whether --enable-png or --disable-png was given.
1921 if test "${enable_png+set}" = set; then
1922 enableval="$enable_png"
1923 enable_png=$enableval
1924 else
1925 enable_png=yes
1926 fi;
1927 # Check whether --enable-jpeg or --disable-jpeg was given.
1928 if test "${enable_jpeg+set}" = set; then
1929 enableval="$enable_jpeg"
1930 enable_jpeg=$enableval
1931 else
1932 enable_jpeg=yes
1933 fi;
1934 # Check whether --enable-gif or --disable-gif was given.
1935 if test "${enable_gif+set}" = set; then
1936 enableval="$enable_gif"
1937 enable_gif=$enableval
1938 else
1939 enable_gif=yes
1940 fi;
1941 # Check whether --enable-ssl or --disable-ssl was given.
1942 if test "${enable_ssl+set}" = set; then
1943 enableval="$enable_ssl"
1944 enable_ssl=$enableval
1945 else
1946 enable_ssl=yes
1947 fi;
1948 # Check whether --enable-dlgui or --disable-dlgui was given.
1949 if test "${enable_dlgui+set}" = set; then
1950 enableval="$enable_dlgui"
1951 enable_dlgui=$enableval
1952 else
1953 enable_dlgui=yes
1954 fi;
1955 # Check whether --enable-threaded-dns or --disable-threaded-dns was given.
1956 if test "${enable_threaded_dns+set}" = set; then
1957 enableval="$enable_threaded_dns"
1958 enable_threaded_dns=$enableval
1959 else
1960 enable_threaded_dns=yes
1961 fi;
1962
1963
1964 if test x$enable_dlgui = xyes; then
1965 DLGUI_TRUE=
1966 DLGUI_FALSE='#'
1967 else
1968 DLGUI_TRUE='#'
1969 DLGUI_FALSE=
1970 fi
1971
1972
1973 ac_ext=c
1974 ac_cpp='$CPP $CPPFLAGS'
1975 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
1976 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
1977 ac_compiler_gnu=$ac_cv_c_compiler_gnu
1978 if test -n "$ac_tool_prefix"; then
1979 # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
1980 set dummy ${ac_tool_prefix}gcc; ac_word=$2
1981 echo "$as_me:$LINENO: checking for $ac_word" >&5
1982 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1983 if test "${ac_cv_prog_CC+set}" = set; then
1984 echo $ECHO_N "(cached) $ECHO_C" >&6
1985 else
1986 if test -n "$CC"; then
1987 ac_cv_prog_CC="$CC" # Let the user override the test.
1988 else
1989 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1990 for as_dir in $PATH
1991 do
1992 IFS=$as_save_IFS
1993 test -z "$as_dir" && as_dir=.
1994 for ac_exec_ext in '' $ac_executable_extensions; do
1995 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1996 ac_cv_prog_CC="${ac_tool_prefix}gcc"
1997 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1998 break 2
1999 fi
2000 done
2001 done
2002
2003 fi
2004 fi
2005 CC=$ac_cv_prog_CC
2006 if test -n "$CC"; then
2007 echo "$as_me:$LINENO: result: $CC" >&5
2008 echo "${ECHO_T}$CC" >&6
2009 else
2010 echo "$as_me:$LINENO: result: no" >&5
2011 echo "${ECHO_T}no" >&6
2012 fi
2013
2014 fi
2015 if test -z "$ac_cv_prog_CC"; then
2016 ac_ct_CC=$CC
2017 # Extract the first word of "gcc", so it can be a program name with args.
2018 set dummy gcc; ac_word=$2
2019 echo "$as_me:$LINENO: checking for $ac_word" >&5
2020 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2021 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
2022 echo $ECHO_N "(cached) $ECHO_C" >&6
2023 else
2024 if test -n "$ac_ct_CC"; then
2025 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2026 else
2027 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2028 for as_dir in $PATH
2029 do
2030 IFS=$as_save_IFS
2031 test -z "$as_dir" && as_dir=.
2032 for ac_exec_ext in '' $ac_executable_extensions; do
2033 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2034 ac_cv_prog_ac_ct_CC="gcc"
2035 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2036 break 2
2037 fi
2038 done
2039 done
2040
2041 fi
2042 fi
2043 ac_ct_CC=$ac_cv_prog_ac_ct_CC
2044 if test -n "$ac_ct_CC"; then
2045 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
2046 echo "${ECHO_T}$ac_ct_CC" >&6
2047 else
2048 echo "$as_me:$LINENO: result: no" >&5
2049 echo "${ECHO_T}no" >&6
2050 fi
2051
2052 CC=$ac_ct_CC
2053 else
2054 CC="$ac_cv_prog_CC"
2055 fi
2056
2057 if test -z "$CC"; then
2058 if test -n "$ac_tool_prefix"; then
2059 # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
2060 set dummy ${ac_tool_prefix}cc; ac_word=$2
2061 echo "$as_me:$LINENO: checking for $ac_word" >&5
2062 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2063 if test "${ac_cv_prog_CC+set}" = set; then
2064 echo $ECHO_N "(cached) $ECHO_C" >&6
2065 else
2066 if test -n "$CC"; then
2067 ac_cv_prog_CC="$CC" # Let the user override the test.
2068 else
2069 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2070 for as_dir in $PATH
2071 do
2072 IFS=$as_save_IFS
2073 test -z "$as_dir" && as_dir=.
2074 for ac_exec_ext in '' $ac_executable_extensions; do
2075 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2076 ac_cv_prog_CC="${ac_tool_prefix}cc"
2077 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2078 break 2
2079 fi
2080 done
2081 done
2082
2083 fi
2084 fi
2085 CC=$ac_cv_prog_CC
2086 if test -n "$CC"; then
2087 echo "$as_me:$LINENO: result: $CC" >&5
2088 echo "${ECHO_T}$CC" >&6
2089 else
2090 echo "$as_me:$LINENO: result: no" >&5
2091 echo "${ECHO_T}no" >&6
2092 fi
2093
2094 fi
2095 if test -z "$ac_cv_prog_CC"; then
2096 ac_ct_CC=$CC
2097 # Extract the first word of "cc", so it can be a program name with args.
2098 set dummy cc; ac_word=$2
2099 echo "$as_me:$LINENO: checking for $ac_word" >&5
2100 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2101 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
2102 echo $ECHO_N "(cached) $ECHO_C" >&6
2103 else
2104 if test -n "$ac_ct_CC"; then
2105 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2106 else
2107 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2108 for as_dir in $PATH
2109 do
2110 IFS=$as_save_IFS
2111 test -z "$as_dir" && as_dir=.
2112 for ac_exec_ext in '' $ac_executable_extensions; do
2113 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2114 ac_cv_prog_ac_ct_CC="cc"
2115 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2116 break 2
2117 fi
2118 done
2119 done
2120
2121 fi
2122 fi
2123 ac_ct_CC=$ac_cv_prog_ac_ct_CC
2124 if test -n "$ac_ct_CC"; then
2125 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
2126 echo "${ECHO_T}$ac_ct_CC" >&6
2127 else
2128 echo "$as_me:$LINENO: result: no" >&5
2129 echo "${ECHO_T}no" >&6
2130 fi
2131
2132 CC=$ac_ct_CC
2133 else
2134 CC="$ac_cv_prog_CC"
2135 fi
2136
2137 fi
2138 if test -z "$CC"; then
2139 # Extract the first word of "cc", so it can be a program name with args.
2140 set dummy cc; ac_word=$2
2141 echo "$as_me:$LINENO: checking for $ac_word" >&5
2142 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2143 if test "${ac_cv_prog_CC+set}" = set; then
2144 echo $ECHO_N "(cached) $ECHO_C" >&6
2145 else
2146 if test -n "$CC"; then
2147 ac_cv_prog_CC="$CC" # Let the user override the test.
2148 else
2149 ac_prog_rejected=no
2150 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2151 for as_dir in $PATH
2152 do
2153 IFS=$as_save_IFS
2154 test -z "$as_dir" && as_dir=.
2155 for ac_exec_ext in '' $ac_executable_extensions; do
2156 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2157 if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
2158 ac_prog_rejected=yes
2159 continue
2160 fi
2161 ac_cv_prog_CC="cc"
2162 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2163 break 2
2164 fi
2165 done
2166 done
2167
2168 if test $ac_prog_rejected = yes; then
2169 # We found a bogon in the path, so make sure we never use it.
2170 set dummy $ac_cv_prog_CC
2171 shift
2172 if test $# != 0; then
2173 # We chose a different compiler from the bogus one.
2174 # However, it has the same basename, so the bogon will be chosen
2175 # first if we set CC to just the basename; use the full file name.
2176 shift
2177 ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
2178 fi
2179 fi
2180 fi
2181 fi
2182 CC=$ac_cv_prog_CC
2183 if test -n "$CC"; then
2184 echo "$as_me:$LINENO: result: $CC" >&5
2185 echo "${ECHO_T}$CC" >&6
2186 else
2187 echo "$as_me:$LINENO: result: no" >&5
2188 echo "${ECHO_T}no" >&6
2189 fi
2190
2191 fi
2192 if test -z "$CC"; then
2193 if test -n "$ac_tool_prefix"; then
2194 for ac_prog in cl
2195 do
2196 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
2197 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
2198 echo "$as_me:$LINENO: checking for $ac_word" >&5
2199 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2200 if test "${ac_cv_prog_CC+set}" = set; then
2201 echo $ECHO_N "(cached) $ECHO_C" >&6
2202 else
2203 if test -n "$CC"; then
2204 ac_cv_prog_CC="$CC" # Let the user override the test.
2205 else
2206 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2207 for as_dir in $PATH
2208 do
2209 IFS=$as_save_IFS
2210 test -z "$as_dir" && as_dir=.
2211 for ac_exec_ext in '' $ac_executable_extensions; do
2212 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2213 ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
2214 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2215 break 2
2216 fi
2217 done
2218 done
2219
2220 fi
2221 fi
2222 CC=$ac_cv_prog_CC
2223 if test -n "$CC"; then
2224 echo "$as_me:$LINENO: result: $CC" >&5
2225 echo "${ECHO_T}$CC" >&6
2226 else
2227 echo "$as_me:$LINENO: result: no" >&5
2228 echo "${ECHO_T}no" >&6
2229 fi
2230
2231 test -n "$CC" && break
2232 done
2233 fi
2234 if test -z "$CC"; then
2235 ac_ct_CC=$CC
2236 for ac_prog in cl
2237 do
2238 # Extract the first word of "$ac_prog", so it can be a program name with args.
2239 set dummy $ac_prog; ac_word=$2
2240 echo "$as_me:$LINENO: checking for $ac_word" >&5
2241 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2242 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
2243 echo $ECHO_N "(cached) $ECHO_C" >&6
2244 else
2245 if test -n "$ac_ct_CC"; then
2246 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2247 else
2248 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2249 for as_dir in $PATH
2250 do
2251 IFS=$as_save_IFS
2252 test -z "$as_dir" && as_dir=.
2253 for ac_exec_ext in '' $ac_executable_extensions; do
2254 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2255 ac_cv_prog_ac_ct_CC="$ac_prog"
2256 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2257 break 2
2258 fi
2259 done
2260 done
2261
2262 fi
2263 fi
2264 ac_ct_CC=$ac_cv_prog_ac_ct_CC
2265 if test -n "$ac_ct_CC"; then
2266 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
2267 echo "${ECHO_T}$ac_ct_CC" >&6
2268 else
2269 echo "$as_me:$LINENO: result: no" >&5
2270 echo "${ECHO_T}no" >&6
2271 fi
2272
2273 test -n "$ac_ct_CC" && break
2274 done
2275
2276 CC=$ac_ct_CC
2277 fi
2278
2279 fi
2280
2281
2282 test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
2283 See \`config.log' for more details." >&5
2284 echo "$as_me: error: no acceptable C compiler found in \$PATH
2285 See \`config.log' for more details." >&2;}
2286 { (exit 1); exit 1; }; }
2287
2288 # Provide some information about the compiler.
2289 echo "$as_me:$LINENO:" \
2290 "checking for C compiler version" >&5
2291 ac_compiler=`set X $ac_compile; echo $2`
2292 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
2293 (eval $ac_compiler --version </dev/null >&5) 2>&5
2294 ac_status=$?
2295 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2296 (exit $ac_status); }
2297 { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
2298 (eval $ac_compiler -v </dev/null >&5) 2>&5
2299 ac_status=$?
2300 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2301 (exit $ac_status); }
2302 { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
2303 (eval $ac_compiler -V </dev/null >&5) 2>&5
2304 ac_status=$?
2305 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2306 (exit $ac_status); }
2307
2308 cat >conftest.$ac_ext <<_ACEOF
2309 /* confdefs.h. */
2310 _ACEOF
2311 cat confdefs.h >>conftest.$ac_ext
2312 cat >>conftest.$ac_ext <<_ACEOF
2313 /* end confdefs.h. */
2314
2315 int
2316 main ()
2317 {
2318
2319 ;
2320 return 0;
2321 }
2322 _ACEOF
2323 ac_clean_files_save=$ac_clean_files
2324 ac_clean_files="$ac_clean_files a.out a.exe b.out"
2325 # Try to create an executable without -o first, disregard a.out.
2326 # It will help us diagnose broken compilers, and finding out an intuition
2327 # of exeext.
2328 echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
2329 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
2330 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
2331 if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
2332 (eval $ac_link_default) 2>&5
2333 ac_status=$?
2334 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2335 (exit $ac_status); }; then
2336 # Find the output, starting from the most likely. This scheme is
2337 # not robust to junk in `.', hence go to wildcards (a.*) only as a last
2338 # resort.
2339
2340 # Be careful to initialize this variable, since it used to be cached.
2341 # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
2342 ac_cv_exeext=
2343 # b.out is created by i960 compilers.
2344 for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
2345 do
2346 test -f "$ac_file" || continue
2347 case $ac_file in
2348 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
2349 ;;
2350 conftest.$ac_ext )
2351 # This is the source file.
2352 ;;
2353 [ab].out )
2354 # We found the default executable, but exeext='' is most
2355 # certainly right.
2356 break;;
2357 *.* )
2358 ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
2359 # FIXME: I believe we export ac_cv_exeext for Libtool,
2360 # but it would be cool to find out if it's true. Does anybody
2361 # maintain Libtool? --akim.
2362 export ac_cv_exeext
2363 break;;
2364 * )
2365 break;;
2366 esac
2367 done
2368 else
2369 echo "$as_me: failed program was:" >&5
2370 sed 's/^/| /' conftest.$ac_ext >&5
2371
2372 { { echo "$as_me:$LINENO: error: C compiler cannot create executables
2373 See \`config.log' for more details." >&5
2374 echo "$as_me: error: C compiler cannot create executables
2375 See \`config.log' for more details." >&2;}
2376 { (exit 77); exit 77; }; }
2377 fi
2378
2379 ac_exeext=$ac_cv_exeext
2380 echo "$as_me:$LINENO: result: $ac_file" >&5
2381 echo "${ECHO_T}$ac_file" >&6
2382
2383 # Check the compiler produces executables we can run. If not, either
2384 # the compiler is broken, or we cross compile.
2385 echo "$as_me:$LINENO: checking whether the C compiler works" >&5
2386 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
2387 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0
2388 # If not cross compiling, check that we can run a simple program.
2389 if test "$cross_compiling" != yes; then
2390 if { ac_try='./$ac_file'
2391 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2392 (eval $ac_try) 2>&5
2393 ac_status=$?
2394 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2395 (exit $ac_status); }; }; then
2396 cross_compiling=no
2397 else
2398 if test "$cross_compiling" = maybe; then
2399 cross_compiling=yes
2400 else
2401 { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
2402 If you meant to cross compile, use \`--host'.
2403 See \`config.log' for more details." >&5
2404 echo "$as_me: error: cannot run C compiled programs.
2405 If you meant to cross compile, use \`--host'.
2406 See \`config.log' for more details." >&2;}
2407 { (exit 1); exit 1; }; }
2408 fi
2409 fi
2410 fi
2411 echo "$as_me:$LINENO: result: yes" >&5
2412 echo "${ECHO_T}yes" >&6
2413
2414 rm -f a.out a.exe conftest$ac_cv_exeext b.out
2415 ac_clean_files=$ac_clean_files_save
2416 # Check the compiler produces executables we can run. If not, either
2417 # the compiler is broken, or we cross compile.
2418 echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
2419 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
2420 echo "$as_me:$LINENO: result: $cross_compiling" >&5
2421 echo "${ECHO_T}$cross_compiling" >&6
2422
2423 echo "$as_me:$LINENO: checking for suffix of executables" >&5
2424 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
2425 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
2426 (eval $ac_link) 2>&5
2427 ac_status=$?
2428 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2429 (exit $ac_status); }; then
2430 # If both `conftest.exe' and `conftest' are `present' (well, observable)
2431 # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
2432 # work properly (i.e., refer to `conftest.exe'), while it won't with
2433 # `rm'.
2434 for ac_file in conftest.exe conftest conftest.*; do
2435 test -f "$ac_file" || continue
2436 case $ac_file in
2437 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
2438 *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
2439 export ac_cv_exeext
2440 break;;
2441 * ) break;;
2442 esac
2443 done
2444 else
2445 { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
2446 See \`config.log' for more details." >&5
2447 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
2448 See \`config.log' for more details." >&2;}
2449 { (exit 1); exit 1; }; }
2450 fi
2451
2452 rm -f conftest$ac_cv_exeext
2453 echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
2454 echo "${ECHO_T}$ac_cv_exeext" >&6
2455
2456 rm -f conftest.$ac_ext
2457 EXEEXT=$ac_cv_exeext
2458 ac_exeext=$EXEEXT
2459 echo "$as_me:$LINENO: checking for suffix of object files" >&5
2460 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
2461 if test "${ac_cv_objext+set}" = set; then
2462 echo $ECHO_N "(cached) $ECHO_C" >&6
2463 else
2464 cat >conftest.$ac_ext <<_ACEOF
2465 /* confdefs.h. */
2466 _ACEOF
2467 cat confdefs.h >>conftest.$ac_ext
2468 cat >>conftest.$ac_ext <<_ACEOF
2469 /* end confdefs.h. */
2470
2471 int
2472 main ()
2473 {
2474
2475 ;
2476 return 0;
2477 }
2478 _ACEOF
2479 rm -f conftest.o conftest.obj
2480 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2481 (eval $ac_compile) 2>&5
2482 ac_status=$?
2483 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2484 (exit $ac_status); }; then
2485 for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
2486 case $ac_file in
2487 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
2488 *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
2489 break;;
2490 esac
2491 done
2492 else
2493 echo "$as_me: failed program was:" >&5
2494 sed 's/^/| /' conftest.$ac_ext >&5
2495
2496 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
2497 See \`config.log' for more details." >&5
2498 echo "$as_me: error: cannot compute suffix of object files: cannot compile
2499 See \`config.log' for more details." >&2;}
2500 { (exit 1); exit 1; }; }
2501 fi
2502
2503 rm -f conftest.$ac_cv_objext conftest.$ac_ext
2504 fi
2505 echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
2506 echo "${ECHO_T}$ac_cv_objext" >&6
2507 OBJEXT=$ac_cv_objext
2508 ac_objext=$OBJEXT
2509 echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
2510 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
2511 if test "${ac_cv_c_compiler_gnu+set}" = set; then
2512 echo $ECHO_N "(cached) $ECHO_C" >&6
2513 else
2514 cat >conftest.$ac_ext <<_ACEOF
2515 /* confdefs.h. */
2516 _ACEOF
2517 cat confdefs.h >>conftest.$ac_ext
2518 cat >>conftest.$ac_ext <<_ACEOF
2519 /* end confdefs.h. */
2520
2521 int
2522 main ()
2523 {
2524 #ifndef __GNUC__
2525 choke me
2526 #endif
2527
2528 ;
2529 return 0;
2530 }
2531 _ACEOF
2532 rm -f conftest.$ac_objext
2533 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2534 (eval $ac_compile) 2>conftest.er1
2535 ac_status=$?
2536 grep -v '^ *+' conftest.er1 >conftest.err
2537 rm -f conftest.er1
2538 cat conftest.err >&5
2539 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2540 (exit $ac_status); } &&
2541 { ac_try='test -z "$ac_c_werror_flag"
2542 || test ! -s conftest.err'
2543 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2544 (eval $ac_try) 2>&5
2545 ac_status=$?
2546 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2547 (exit $ac_status); }; } &&
2548 { ac_try='test -s conftest.$ac_objext'
2549 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2550 (eval $ac_try) 2>&5
2551 ac_status=$?
2552 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2553 (exit $ac_status); }; }; then
2554 ac_compiler_gnu=yes
2555 else
2556 echo "$as_me: failed program was:" >&5
2557 sed 's/^/| /' conftest.$ac_ext >&5
2558
2559 ac_compiler_gnu=no
2560 fi
2561 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2562 ac_cv_c_compiler_gnu=$ac_compiler_gnu
2563
2564 fi
2565 echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
2566 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
2567 GCC=`test $ac_compiler_gnu = yes && echo yes`
2568 ac_test_CFLAGS=${CFLAGS+set}
2569 ac_save_CFLAGS=$CFLAGS
2570 CFLAGS="-g"
2571 echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
2572 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
2573 if test "${ac_cv_prog_cc_g+set}" = set; then
2574 echo $ECHO_N "(cached) $ECHO_C" >&6
2575 else
2576 cat >conftest.$ac_ext <<_ACEOF
2577 /* confdefs.h. */
2578 _ACEOF
2579 cat confdefs.h >>conftest.$ac_ext
2580 cat >>conftest.$ac_ext <<_ACEOF
2581 /* end confdefs.h. */
2582
2583 int
2584 main ()
2585 {
2586
2587 ;
2588 return 0;
2589 }
2590 _ACEOF
2591 rm -f conftest.$ac_objext
2592 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2593 (eval $ac_compile) 2>conftest.er1
2594 ac_status=$?
2595 grep -v '^ *+' conftest.er1 >conftest.err
2596 rm -f conftest.er1
2597 cat conftest.err >&5
2598 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2599 (exit $ac_status); } &&
2600 { ac_try='test -z "$ac_c_werror_flag"
2601 || test ! -s conftest.err'
2602 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2603 (eval $ac_try) 2>&5
2604 ac_status=$?
2605 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2606 (exit $ac_status); }; } &&
2607 { ac_try='test -s conftest.$ac_objext'
2608 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2609 (eval $ac_try) 2>&5
2610 ac_status=$?
2611 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2612 (exit $ac_status); }; }; then
2613 ac_cv_prog_cc_g=yes
2614 else
2615 echo "$as_me: failed program was:" >&5
2616 sed 's/^/| /' conftest.$ac_ext >&5
2617
2618 ac_cv_prog_cc_g=no
2619 fi
2620 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2621 fi
2622 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
2623 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
2624 if test "$ac_test_CFLAGS" = set; then
2625 CFLAGS=$ac_save_CFLAGS
2626 elif test $ac_cv_prog_cc_g = yes; then
2627 if test "$GCC" = yes; then
2628 CFLAGS="-g -O2"
2629 else
2630 CFLAGS="-g"
2631 fi
2632 else
2633 if test "$GCC" = yes; then
2634 CFLAGS="-O2"
2635 else
2636 CFLAGS=
2637 fi
2638 fi
2639 echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
2640 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
2641 if test "${ac_cv_prog_cc_stdc+set}" = set; then
2642 echo $ECHO_N "(cached) $ECHO_C" >&6
2643 else
2644 ac_cv_prog_cc_stdc=no
2645 ac_save_CC=$CC
2646 cat >conftest.$ac_ext <<_ACEOF
2647 /* confdefs.h. */
2648 _ACEOF
2649 cat confdefs.h >>conftest.$ac_ext
2650 cat >>conftest.$ac_ext <<_ACEOF
2651 /* end confdefs.h. */
2652 #include <stdarg.h>
2653 #include <stdio.h>
2654 #include <sys/types.h>
2655 #include <sys/stat.h>
2656 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
2657 struct buf { int x; };
2658 FILE * (*rcsopen) (struct buf *, struct stat *, int);
2659 static char *e (p, i)
2660 char **p;
2661 int i;
2662 {
2663 return p[i];
2664 }
2665 static char *f (char * (*g) (char **, int), char **p, ...)
2666 {
2667 char *s;
2668 va_list v;
2669 va_start (v,p);
2670 s = g (p, va_arg (v,int));
2671 va_end (v);
2672 return s;
2673 }
2674
2675 /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
2676 function prototypes and stuff, but not '\xHH' hex character constants.
2677 These don't provoke an error unfortunately, instead are silently treated
2678 as 'x'. The following induces an error, until -std1 is added to get
2679 proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
2680 array size at least. It's necessary to write '\x00'==0 to get something
2681 that's true only with -std1. */
2682 int osf4_cc_array ['\x00' == 0 ? 1 : -1];
2683
2684 int test (int i, double x);
2685 struct s1 {int (*f) (int a);};
2686 struct s2 {int (*f) (double a);};
2687 int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
2688 int argc;
2689 char **argv;
2690 int
2691 main ()
2692 {
2693 return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
2694 ;
2695 return 0;
2696 }
2697 _ACEOF
2698 # Don't try gcc -ansi; that turns off useful extensions and
2699 # breaks some systems' header files.
2700 # AIX -qlanglvl=ansi
2701 # Ultrix and OSF/1 -std1
2702 # HP-UX 10.20 and later -Ae
2703 # HP-UX older versions -Aa -D_HPUX_SOURCE
2704 # SVR4 -Xc -D__EXTENSIONS__
2705 for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
2706 do
2707 CC="$ac_save_CC $ac_arg"
2708 rm -f conftest.$ac_objext
2709 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2710 (eval $ac_compile) 2>conftest.er1
2711 ac_status=$?
2712 grep -v '^ *+' conftest.er1 >conftest.err
2713 rm -f conftest.er1
2714 cat conftest.err >&5
2715 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2716 (exit $ac_status); } &&
2717 { ac_try='test -z "$ac_c_werror_flag"
2718 || test ! -s conftest.err'
2719 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2720 (eval $ac_try) 2>&5
2721 ac_status=$?
2722 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2723 (exit $ac_status); }; } &&
2724 { ac_try='test -s conftest.$ac_objext'
2725 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2726 (eval $ac_try) 2>&5
2727 ac_status=$?
2728 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2729 (exit $ac_status); }; }; then
2730 ac_cv_prog_cc_stdc=$ac_arg
2731 break
2732 else
2733 echo "$as_me: failed program was:" >&5
2734 sed 's/^/| /' conftest.$ac_ext >&5
2735
2736 fi
2737 rm -f conftest.err conftest.$ac_objext
2738 done
2739 rm -f conftest.$ac_ext conftest.$ac_objext
2740 CC=$ac_save_CC
2741
2742 fi
2743
2744 case "x$ac_cv_prog_cc_stdc" in
2745 x|xno)
2746 echo "$as_me:$LINENO: result: none needed" >&5
2747 echo "${ECHO_T}none needed" >&6 ;;
2748 *)
2749 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
2750 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
2751 CC="$CC $ac_cv_prog_cc_stdc" ;;
2752 esac
2753
2754 # Some people use a C++ compiler to compile C. Since we use `exit',
2755 # in C++ we need to declare it. In case someone uses the same compiler
2756 # for both compiling C and C++ we need to have the C++ compiler decide
2757 # the declaration of exit, since it's the most demanding environment.
2758 cat >conftest.$ac_ext <<_ACEOF
2759 #ifndef __cplusplus
2760 choke me
2761 #endif
2762 _ACEOF
2763 rm -f conftest.$ac_objext
2764 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2765 (eval $ac_compile) 2>conftest.er1
2766 ac_status=$?
2767 grep -v '^ *+' conftest.er1 >conftest.err
2768 rm -f conftest.er1
2769 cat conftest.err >&5
2770 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2771 (exit $ac_status); } &&
2772 { ac_try='test -z "$ac_c_werror_flag"
2773 || test ! -s conftest.err'
2774 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2775 (eval $ac_try) 2>&5
2776 ac_status=$?
2777 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2778 (exit $ac_status); }; } &&
2779 { ac_try='test -s conftest.$ac_objext'
2780 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2781 (eval $ac_try) 2>&5
2782 ac_status=$?
2783 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2784 (exit $ac_status); }; }; then
2785 for ac_declaration in \
2786 '' \
2787 'extern "C" void std::exit (int) throw (); using std::exit;' \
2788 'extern "C" void std::exit (int); using std::exit;' \
2789 'extern "C" void exit (int) throw ();' \
2790 'extern "C" void exit (int);' \
2791 'void exit (int);'
2792 do
2793 cat >conftest.$ac_ext <<_ACEOF
2794 /* confdefs.h. */
2795 _ACEOF
2796 cat confdefs.h >>conftest.$ac_ext
2797 cat >>conftest.$ac_ext <<_ACEOF
2798 /* end confdefs.h. */
2799 $ac_declaration
2800 #include <stdlib.h>
2801 int
2802 main ()
2803 {
2804 exit (42);
2805 ;
2806 return 0;
2807 }
2808 _ACEOF
2809 rm -f conftest.$ac_objext
2810 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2811 (eval $ac_compile) 2>conftest.er1
2812 ac_status=$?
2813 grep -v '^ *+' conftest.er1 >conftest.err
2814 rm -f conftest.er1
2815 cat conftest.err >&5
2816 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2817 (exit $ac_status); } &&
2818 { ac_try='test -z "$ac_c_werror_flag"
2819 || test ! -s conftest.err'
2820 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2821 (eval $ac_try) 2>&5
2822 ac_status=$?
2823 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2824 (exit $ac_status); }; } &&
2825 { ac_try='test -s conftest.$ac_objext'
2826 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2827 (eval $ac_try) 2>&5
2828 ac_status=$?
2829 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2830 (exit $ac_status); }; }; then
2831 :
2832 else
2833 echo "$as_me: failed program was:" >&5
2834 sed 's/^/| /' conftest.$ac_ext >&5
2835
2836 continue
2837 fi
2838 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2839 cat >conftest.$ac_ext <<_ACEOF
2840 /* confdefs.h. */
2841 _ACEOF
2842 cat confdefs.h >>conftest.$ac_ext
2843 cat >>conftest.$ac_ext <<_ACEOF
2844 /* end confdefs.h. */
2845 $ac_declaration
2846 int
2847 main ()
2848 {
2849 exit (42);
2850 ;
2851 return 0;
2852 }
2853 _ACEOF
2854 rm -f conftest.$ac_objext
2855 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2856 (eval $ac_compile) 2>conftest.er1
2857 ac_status=$?
2858 grep -v '^ *+' conftest.er1 >conftest.err
2859 rm -f conftest.er1
2860 cat conftest.err >&5
2861 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2862 (exit $ac_status); } &&
2863 { ac_try='test -z "$ac_c_werror_flag"
2864 || test ! -s conftest.err'
2865 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2866 (eval $ac_try) 2>&5
2867 ac_status=$?
2868 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2869 (exit $ac_status); }; } &&
2870 { ac_try='test -s conftest.$ac_objext'
2871 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2872 (eval $ac_try) 2>&5
2873 ac_status=$?
2874 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2875 (exit $ac_status); }; }; then
2876 break
2877 else
2878 echo "$as_me: failed program was:" >&5
2879 sed 's/^/| /' conftest.$ac_ext >&5
2880
2881 fi
2882 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2883 done
2884 rm -f conftest*
2885 if test -n "$ac_declaration"; then
2886 echo '#ifdef __cplusplus' >>confdefs.h
2887 echo $ac_declaration >>confdefs.h
2888 echo '#endif' >>confdefs.h
2889 fi
2890
2891 else
2892 echo "$as_me: failed program was:" >&5
2893 sed 's/^/| /' conftest.$ac_ext >&5
2894
2895 fi
2896 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2897 ac_ext=c
2898 ac_cpp='$CPP $CPPFLAGS'
2899 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
2900 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
2901 ac_compiler_gnu=$ac_cv_c_compiler_gnu
2902 DEPDIR="${am__leading_dot}deps"
2903
2904 ac_config_commands="$ac_config_commands depfiles"
2905
2906
2907 am_make=${MAKE-make}
2908 cat > confinc << 'END'
2909 am__doit:
2910 @echo done
2911 .PHONY: am__doit
2912 END
2913 # If we don't find an include directive, just comment out the code.
2914 echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
2915 echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6
2916 am__include="#"
2917 am__quote=
2918 _am_result=none
2919 # First try GNU make style include.
2920 echo "include confinc" > confmf
2921 # We grep out `Entering directory' and `Leaving directory'
2922 # messages which can occur if `w' ends up in MAKEFLAGS.
2923 # In particular we don't look at `^make:' because GNU make might
2924 # be invoked under some other name (usually "gmake"), in which
2925 # case it prints its new name instead of `make'.
2926 if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
2927 am__include=include
2928 am__quote=
2929 _am_result=GNU
2930 fi
2931 # Now try BSD make style include.
2932 if test "$am__include" = "#"; then
2933 echo '.include "confinc"' > confmf
2934 if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
2935 am__include=.include
2936 am__quote="\""
2937 _am_result=BSD
2938 fi
2939 fi
2940
2941
2942 echo "$as_me:$LINENO: result: $_am_result" >&5
2943 echo "${ECHO_T}$_am_result" >&6
2944 rm -f confinc confmf
2945
2946 # Check whether --enable-dependency-tracking or --disable-dependency-tracking was given.
2947 if test "${enable_dependency_tracking+set}" = set; then
2948 enableval="$enable_dependency_tracking"
2949
2950 fi;
2951 if test "x$enable_dependency_tracking" != xno; then
2952 am_depcomp="$ac_aux_dir/depcomp"
2953 AMDEPBACKSLASH='\'
2954 fi
2955
2956
2957 if test "x$enable_dependency_tracking" != xno; then
2958 AMDEP_TRUE=
2959 AMDEP_FALSE='#'
2960 else
2961 AMDEP_TRUE='#'
2962 AMDEP_FALSE=
2963 fi
2964
2965
2966
2967
2968 depcc="$CC" am_compiler_list=
2969
2970 echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
2971 echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
2972 if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
2973 echo $ECHO_N "(cached) $ECHO_C" >&6
2974 else
2975 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
2976 # We make a subdir and do the tests there. Otherwise we can end up
2977 # making bogus files that we don't know about and never remove. For
2978 # instance it was reported that on HP-UX the gcc test will end up
2979 # making a dummy file named `D' -- because `-MD' means `put the output
2980 # in D'.
2981 mkdir conftest.dir
2982 # Copy depcomp to subdir because otherwise we won't find it if we're
2983 # using a relative directory.
2984 cp "$am_depcomp" conftest.dir
2985 cd conftest.dir
2986 # We will build objects and dependencies in a subdirectory because
2987 # it helps to detect inapplicable dependency modes. For instance
2988 # both Tru64's cc and ICC support -MD to output dependencies as a
2989 # side effect of compilation, but ICC will put the dependencies in
2990 # the current directory while Tru64 will put them in the object
2991 # directory.
2992 mkdir sub
2993
2994 am_cv_CC_dependencies_compiler_type=none
2995 if test "$am_compiler_list" = ""; then
2996 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
2997 fi
2998 for depmode in $am_compiler_list; do
2999 # Setup a source with many dependencies, because some compilers
3000 # like to wrap large dependency lists on column 80 (with \), and
3001 # we should not choose a depcomp mode which is confused by this.
3002 #
3003 # We need to recreate these files for each test, as the compiler may
3004 # overwrite some of them when testing with obscure command lines.
3005 # This happens at least with the AIX C compiler.
3006 : > sub/conftest.c
3007 for i in 1 2 3 4 5 6; do
3008 echo '#include "conftst'$i'.h"' >> sub/conftest.c
3009 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
3010 # Solaris 8's {/usr,}/bin/sh.
3011 touch sub/conftst$i.h
3012 done
3013 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
3014
3015 case $depmode in
3016 nosideeffect)
3017 # after this tag, mechanisms are not by side-effect, so they'll
3018 # only be used when explicitly requested
3019 if test "x$enable_dependency_tracking" = xyes; then
3020 continue
3021 else
3022 break
3023 fi
3024 ;;
3025 none) break ;;
3026 esac
3027 # We check with `-c' and `-o' for the sake of the "dashmstdout"
3028 # mode. It turns out that the SunPro C++ compiler does not properly
3029 # handle `-M -o', and we need to detect this.
3030 if depmode=$depmode \
3031 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
3032 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
3033 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
3034 >/dev/null 2>conftest.err &&
3035 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
3036 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
3037 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
3038 # icc doesn't choke on unknown options, it will just issue warnings
3039 # or remarks (even with -Werror). So we grep stderr for any message
3040 # that says an option was ignored or not supported.
3041 # When given -MP, icc 7.0 and 7.1 complain thusly:
3042 # icc: Command line warning: ignoring option '-M'; no argument required
3043 # The diagnosis changed in icc 8.0:
3044 # icc: Command line remark: option '-MP' not supported
3045 if (grep 'ignoring option' conftest.err ||
3046 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
3047 am_cv_CC_dependencies_compiler_type=$depmode
3048 break
3049 fi
3050 fi
3051 done
3052
3053 cd ..
3054 rm -rf conftest.dir
3055 else
3056 am_cv_CC_dependencies_compiler_type=none
3057 fi
3058
3059 fi
3060 echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
3061 echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
3062 CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
3063
3064
3065
3066 if
3067 test "x$enable_dependency_tracking" != xno \
3068 && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
3069 am__fastdepCC_TRUE=
3070 am__fastdepCC_FALSE='#'
3071 else
3072 am__fastdepCC_TRUE='#'
3073 am__fastdepCC_FALSE=
3074 fi
3075
3076
3077 ac_ext=c
3078 ac_cpp='$CPP $CPPFLAGS'
3079 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3080 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3081 ac_compiler_gnu=$ac_cv_c_compiler_gnu
3082 if test -n "$ac_tool_prefix"; then
3083 # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
3084 set dummy ${ac_tool_prefix}gcc; ac_word=$2
3085 echo "$as_me:$LINENO: checking for $ac_word" >&5
3086 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3087 if test "${ac_cv_prog_CC+set}" = set; then
3088 echo $ECHO_N "(cached) $ECHO_C" >&6
3089 else
3090 if test -n "$CC"; then
3091 ac_cv_prog_CC="$CC" # Let the user override the test.
3092 else
3093 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3094 for as_dir in $PATH
3095 do
3096 IFS=$as_save_IFS
3097 test -z "$as_dir" && as_dir=.
3098 for ac_exec_ext in '' $ac_executable_extensions; do
3099 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3100 ac_cv_prog_CC="${ac_tool_prefix}gcc"
3101 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3102 break 2
3103 fi
3104 done
3105 done
3106
3107 fi
3108 fi
3109 CC=$ac_cv_prog_CC
3110 if test -n "$CC"; then
3111 echo "$as_me:$LINENO: result: $CC" >&5
3112 echo "${ECHO_T}$CC" >&6
3113 else
3114 echo "$as_me:$LINENO: result: no" >&5
3115 echo "${ECHO_T}no" >&6
3116 fi
3117
3118 fi
3119 if test -z "$ac_cv_prog_CC"; then
3120 ac_ct_CC=$CC
3121 # Extract the first word of "gcc", so it can be a program name with args.
3122 set dummy gcc; ac_word=$2
3123 echo "$as_me:$LINENO: checking for $ac_word" >&5
3124 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3125 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
3126 echo $ECHO_N "(cached) $ECHO_C" >&6
3127 else
3128 if test -n "$ac_ct_CC"; then
3129 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
3130 else
3131 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3132 for as_dir in $PATH
3133 do
3134 IFS=$as_save_IFS
3135 test -z "$as_dir" && as_dir=.
3136 for ac_exec_ext in '' $ac_executable_extensions; do
3137 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3138 ac_cv_prog_ac_ct_CC="gcc"
3139 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3140 break 2
3141 fi
3142 done
3143 done
3144
3145 fi
3146 fi
3147 ac_ct_CC=$ac_cv_prog_ac_ct_CC
3148 if test -n "$ac_ct_CC"; then
3149 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
3150 echo "${ECHO_T}$ac_ct_CC" >&6
3151 else
3152 echo "$as_me:$LINENO: result: no" >&5
3153 echo "${ECHO_T}no" >&6
3154 fi
3155
3156 CC=$ac_ct_CC
3157 else
3158 CC="$ac_cv_prog_CC"
3159 fi
3160
3161 if test -z "$CC"; then
3162 if test -n "$ac_tool_prefix"; then
3163 # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
3164 set dummy ${ac_tool_prefix}cc; ac_word=$2
3165 echo "$as_me:$LINENO: checking for $ac_word" >&5
3166 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3167 if test "${ac_cv_prog_CC+set}" = set; then
3168 echo $ECHO_N "(cached) $ECHO_C" >&6
3169 else
3170 if test -n "$CC"; then
3171 ac_cv_prog_CC="$CC" # Let the user override the test.
3172 else
3173 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3174 for as_dir in $PATH
3175 do
3176 IFS=$as_save_IFS
3177 test -z "$as_dir" && as_dir=.
3178 for ac_exec_ext in '' $ac_executable_extensions; do
3179 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3180 ac_cv_prog_CC="${ac_tool_prefix}cc"
3181 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3182 break 2
3183 fi
3184 done
3185 done
3186
3187 fi
3188 fi
3189 CC=$ac_cv_prog_CC
3190 if test -n "$CC"; then
3191 echo "$as_me:$LINENO: result: $CC" >&5
3192 echo "${ECHO_T}$CC" >&6
3193 else
3194 echo "$as_me:$LINENO: result: no" >&5
3195 echo "${ECHO_T}no" >&6
3196 fi
3197
3198 fi
3199 if test -z "$ac_cv_prog_CC"; then
3200 ac_ct_CC=$CC
3201 # Extract the first word of "cc", so it can be a program name with args.
3202 set dummy cc; ac_word=$2
3203 echo "$as_me:$LINENO: checking for $ac_word" >&5
3204 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3205 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
3206 echo $ECHO_N "(cached) $ECHO_C" >&6
3207 else
3208 if test -n "$ac_ct_CC"; then
3209 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
3210 else
3211 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3212 for as_dir in $PATH
3213 do
3214 IFS=$as_save_IFS
3215 test -z "$as_dir" && as_dir=.
3216 for ac_exec_ext in '' $ac_executable_extensions; do
3217 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3218 ac_cv_prog_ac_ct_CC="cc"
3219 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3220 break 2
3221 fi
3222 done
3223 done
3224
3225 fi
3226 fi
3227 ac_ct_CC=$ac_cv_prog_ac_ct_CC
3228 if test -n "$ac_ct_CC"; then
3229 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
3230 echo "${ECHO_T}$ac_ct_CC" >&6
3231 else
3232 echo "$as_me:$LINENO: result: no" >&5
3233 echo "${ECHO_T}no" >&6
3234 fi
3235
3236 CC=$ac_ct_CC
3237 else
3238 CC="$ac_cv_prog_CC"
3239 fi
3240
3241 fi
3242 if test -z "$CC"; then
3243 # Extract the first word of "cc", so it can be a program name with args.
3244 set dummy cc; ac_word=$2
3245 echo "$as_me:$LINENO: checking for $ac_word" >&5
3246 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3247 if test "${ac_cv_prog_CC+set}" = set; then
3248 echo $ECHO_N "(cached) $ECHO_C" >&6
3249 else
3250 if test -n "$CC"; then
3251 ac_cv_prog_CC="$CC" # Let the user override the test.
3252 else
3253 ac_prog_rejected=no
3254 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3255 for as_dir in $PATH
3256 do
3257 IFS=$as_save_IFS
3258 test -z "$as_dir" && as_dir=.
3259 for ac_exec_ext in '' $ac_executable_extensions; do
3260 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3261 if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
3262 ac_prog_rejected=yes
3263 continue
3264 fi
3265 ac_cv_prog_CC="cc"
3266 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3267 break 2
3268 fi
3269 done
3270 done
3271
3272 if test $ac_prog_rejected = yes; then
3273 # We found a bogon in the path, so make sure we never use it.
3274 set dummy $ac_cv_prog_CC
3275 shift
3276 if test $# != 0; then
3277 # We chose a different compiler from the bogus one.
3278 # However, it has the same basename, so the bogon will be chosen
3279 # first if we set CC to just the basename; use the full file name.
3280 shift
3281 ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
3282 fi
3283 fi
3284 fi
3285 fi
3286 CC=$ac_cv_prog_CC
3287 if test -n "$CC"; then
3288 echo "$as_me:$LINENO: result: $CC" >&5
3289 echo "${ECHO_T}$CC" >&6
3290 else
3291 echo "$as_me:$LINENO: result: no" >&5
3292 echo "${ECHO_T}no" >&6
3293 fi
3294
3295 fi
3296 if test -z "$CC"; then
3297 if test -n "$ac_tool_prefix"; then
3298 for ac_prog in cl
3299 do
3300 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
3301 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
3302 echo "$as_me:$LINENO: checking for $ac_word" >&5
3303 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3304 if test "${ac_cv_prog_CC+set}" = set; then
3305 echo $ECHO_N "(cached) $ECHO_C" >&6
3306 else
3307 if test -n "$CC"; then
3308 ac_cv_prog_CC="$CC" # Let the user override the test.
3309 else
3310 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3311 for as_dir in $PATH
3312 do
3313 IFS=$as_save_IFS
3314 test -z "$as_dir" && as_dir=.
3315 for ac_exec_ext in '' $ac_executable_extensions; do
3316 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3317 ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
3318 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3319 break 2
3320 fi
3321 done
3322 done
3323
3324 fi
3325 fi
3326 CC=$ac_cv_prog_CC
3327 if test -n "$CC"; then
3328 echo "$as_me:$LINENO: result: $CC" >&5
3329 echo "${ECHO_T}$CC" >&6
3330 else
3331 echo "$as_me:$LINENO: result: no" >&5
3332 echo "${ECHO_T}no" >&6
3333 fi
3334
3335 test -n "$CC" && break
3336 done
3337 fi
3338 if test -z "$CC"; then
3339 ac_ct_CC=$CC
3340 for ac_prog in cl
3341 do
3342 # Extract the first word of "$ac_prog", so it can be a program name with args.
3343 set dummy $ac_prog; ac_word=$2
3344 echo "$as_me:$LINENO: checking for $ac_word" >&5
3345 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3346 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
3347 echo $ECHO_N "(cached) $ECHO_C" >&6
3348 else
3349 if test -n "$ac_ct_CC"; then
3350 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
3351 else
3352 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3353 for as_dir in $PATH
3354 do
3355 IFS=$as_save_IFS
3356 test -z "$as_dir" && as_dir=.
3357 for ac_exec_ext in '' $ac_executable_extensions; do
3358 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3359 ac_cv_prog_ac_ct_CC="$ac_prog"
3360 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3361 break 2
3362 fi
3363 done
3364 done
3365
3366 fi
3367 fi
3368 ac_ct_CC=$ac_cv_prog_ac_ct_CC
3369 if test -n "$ac_ct_CC"; then
3370 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
3371 echo "${ECHO_T}$ac_ct_CC" >&6
3372 else
3373 echo "$as_me:$LINENO: result: no" >&5
3374 echo "${ECHO_T}no" >&6
3375 fi
3376
3377 test -n "$ac_ct_CC" && break
3378 done
3379
3380 CC=$ac_ct_CC
3381 fi
3382
3383 fi
3384
3385
3386 test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
3387 See \`config.log' for more details." >&5
3388 echo "$as_me: error: no acceptable C compiler found in \$PATH
3389 See \`config.log' for more details." >&2;}
3390 { (exit 1); exit 1; }; }
3391
3392 # Provide some information about the compiler.
3393 echo "$as_me:$LINENO:" \
3394 "checking for C compiler version" >&5
3395 ac_compiler=`set X $ac_compile; echo $2`
3396 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
3397 (eval $ac_compiler --version </dev/null >&5) 2>&5
3398 ac_status=$?
3399 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3400 (exit $ac_status); }
3401 { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
3402 (eval $ac_compiler -v </dev/null >&5) 2>&5
3403 ac_status=$?
3404 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3405 (exit $ac_status); }
3406 { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
3407 (eval $ac_compiler -V </dev/null >&5) 2>&5
3408 ac_status=$?
3409 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3410 (exit $ac_status); }
3411
3412 echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
3413 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
3414 if test "${ac_cv_c_compiler_gnu+set}" = set; then
3415 echo $ECHO_N "(cached) $ECHO_C" >&6
3416 else
3417 cat >conftest.$ac_ext <<_ACEOF
3418 /* confdefs.h. */
3419 _ACEOF
3420 cat confdefs.h >>conftest.$ac_ext
3421 cat >>conftest.$ac_ext <<_ACEOF
3422 /* end confdefs.h. */
3423
3424 int
3425 main ()
3426 {
3427 #ifndef __GNUC__
3428 choke me
3429 #endif
3430
3431 ;
3432 return 0;
3433 }
3434 _ACEOF
3435 rm -f conftest.$ac_objext
3436 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3437 (eval $ac_compile) 2>conftest.er1
3438 ac_status=$?
3439 grep -v '^ *+' conftest.er1 >conftest.err
3440 rm -f conftest.er1
3441 cat conftest.err >&5
3442 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3443 (exit $ac_status); } &&
3444 { ac_try='test -z "$ac_c_werror_flag"
3445 || test ! -s conftest.err'
3446 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3447 (eval $ac_try) 2>&5
3448 ac_status=$?
3449 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3450 (exit $ac_status); }; } &&
3451 { ac_try='test -s conftest.$ac_objext'
3452 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3453 (eval $ac_try) 2>&5
3454 ac_status=$?
3455 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3456 (exit $ac_status); }; }; then
3457 ac_compiler_gnu=yes
3458 else
3459 echo "$as_me: failed program was:" >&5
3460 sed 's/^/| /' conftest.$ac_ext >&5
3461
3462 ac_compiler_gnu=no
3463 fi
3464 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3465 ac_cv_c_compiler_gnu=$ac_compiler_gnu
3466
3467 fi
3468 echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
3469 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
3470 GCC=`test $ac_compiler_gnu = yes && echo yes`
3471 ac_test_CFLAGS=${CFLAGS+set}
3472 ac_save_CFLAGS=$CFLAGS
3473 CFLAGS="-g"
3474 echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
3475 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
3476 if test "${ac_cv_prog_cc_g+set}" = set; then
3477 echo $ECHO_N "(cached) $ECHO_C" >&6
3478 else
3479 cat >conftest.$ac_ext <<_ACEOF
3480 /* confdefs.h. */
3481 _ACEOF
3482 cat confdefs.h >>conftest.$ac_ext
3483 cat >>conftest.$ac_ext <<_ACEOF
3484 /* end confdefs.h. */
3485
3486 int
3487 main ()
3488 {
3489
3490 ;
3491 return 0;
3492 }
3493 _ACEOF
3494 rm -f conftest.$ac_objext
3495 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3496 (eval $ac_compile) 2>conftest.er1
3497 ac_status=$?
3498 grep -v '^ *+' conftest.er1 >conftest.err
3499 rm -f conftest.er1
3500 cat conftest.err >&5
3501 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3502 (exit $ac_status); } &&
3503 { ac_try='test -z "$ac_c_werror_flag"
3504 || test ! -s conftest.err'
3505 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3506 (eval $ac_try) 2>&5
3507 ac_status=$?
3508 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3509 (exit $ac_status); }; } &&
3510 { ac_try='test -s conftest.$ac_objext'
3511 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3512 (eval $ac_try) 2>&5
3513 ac_status=$?
3514 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3515 (exit $ac_status); }; }; then
3516 ac_cv_prog_cc_g=yes
3517 else
3518 echo "$as_me: failed program was:" >&5
3519 sed 's/^/| /' conftest.$ac_ext >&5
3520
3521 ac_cv_prog_cc_g=no
3522 fi
3523 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3524 fi
3525 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
3526 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
3527 if test "$ac_test_CFLAGS" = set; then
3528 CFLAGS=$ac_save_CFLAGS
3529 elif test $ac_cv_prog_cc_g = yes; then
3530 if test "$GCC" = yes; then
3531 CFLAGS="-g -O2"
3532 else
3533 CFLAGS="-g"
3534 fi
3535 else
3536 if test "$GCC" = yes; then
3537 CFLAGS="-O2"
3538 else
3539 CFLAGS=
3540 fi
3541 fi
3542 echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
3543 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
3544 if test "${ac_cv_prog_cc_stdc+set}" = set; then
3545 echo $ECHO_N "(cached) $ECHO_C" >&6
3546 else
3547 ac_cv_prog_cc_stdc=no
3548 ac_save_CC=$CC
3549 cat >conftest.$ac_ext <<_ACEOF
3550 /* confdefs.h. */
3551 _ACEOF
3552 cat confdefs.h >>conftest.$ac_ext
3553 cat >>conftest.$ac_ext <<_ACEOF
3554 /* end confdefs.h. */
3555 #include <stdarg.h>
3556 #include <stdio.h>
3557 #include <sys/types.h>
3558 #include <sys/stat.h>
3559 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
3560 struct buf { int x; };
3561 FILE * (*rcsopen) (struct buf *, struct stat *, int);
3562 static char *e (p, i)
3563 char **p;
3564 int i;
3565 {
3566 return p[i];
3567 }
3568 static char *f (char * (*g) (char **, int), char **p, ...)
3569 {
3570 char *s;
3571 va_list v;
3572 va_start (v,p);
3573 s = g (p, va_arg (v,int));
3574 va_end (v);
3575 return s;
3576 }
3577
3578 /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
3579 function prototypes and stuff, but not '\xHH' hex character constants.
3580 These don't provoke an error unfortunately, instead are silently treated
3581 as 'x'. The following induces an error, until -std1 is added to get
3582 proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
3583 array size at least. It's necessary to write '\x00'==0 to get something
3584 that's true only with -std1. */
3585 int osf4_cc_array ['\x00' == 0 ? 1 : -1];
3586
3587 int test (int i, double x);
3588 struct s1 {int (*f) (int a);};
3589 struct s2 {int (*f) (double a);};
3590 int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
3591 int argc;
3592 char **argv;
3593 int
3594 main ()
3595 {
3596 return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
3597 ;
3598 return 0;
3599 }
3600 _ACEOF
3601 # Don't try gcc -ansi; that turns off useful extensions and
3602 # breaks some systems' header files.
3603 # AIX -qlanglvl=ansi
3604 # Ultrix and OSF/1 -std1
3605 # HP-UX 10.20 and later -Ae
3606 # HP-UX older versions -Aa -D_HPUX_SOURCE
3607 # SVR4 -Xc -D__EXTENSIONS__
3608 for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
3609 do
3610 CC="$ac_save_CC $ac_arg"
3611 rm -f conftest.$ac_objext
3612 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3613 (eval $ac_compile) 2>conftest.er1
3614 ac_status=$?
3615 grep -v '^ *+' conftest.er1 >conftest.err
3616 rm -f conftest.er1
3617 cat conftest.err >&5
3618 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3619 (exit $ac_status); } &&
3620 { ac_try='test -z "$ac_c_werror_flag"
3621 || test ! -s conftest.err'
3622 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3623 (eval $ac_try) 2>&5
3624 ac_status=$?
3625 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3626 (exit $ac_status); }; } &&
3627 { ac_try='test -s conftest.$ac_objext'
3628 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3629 (eval $ac_try) 2>&5
3630 ac_status=$?
3631 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3632 (exit $ac_status); }; }; then
3633 ac_cv_prog_cc_stdc=$ac_arg
3634 break
3635 else
3636 echo "$as_me: failed program was:" >&5
3637 sed 's/^/| /' conftest.$ac_ext >&5
3638
3639 fi
3640 rm -f conftest.err conftest.$ac_objext
3641 done
3642 rm -f conftest.$ac_ext conftest.$ac_objext
3643 CC=$ac_save_CC
3644
3645 fi
3646
3647 case "x$ac_cv_prog_cc_stdc" in
3648 x|xno)
3649 echo "$as_me:$LINENO: result: none needed" >&5
3650 echo "${ECHO_T}none needed" >&6 ;;
3651 *)
3652 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
3653 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
3654 CC="$CC $ac_cv_prog_cc_stdc" ;;
3655 esac
3656
3657 # Some people use a C++ compiler to compile C. Since we use `exit',
3658 # in C++ we need to declare it. In case someone uses the same compiler
3659 # for both compiling C and C++ we need to have the C++ compiler decide
3660 # the declaration of exit, since it's the most demanding environment.
3661 cat >conftest.$ac_ext <<_ACEOF
3662 #ifndef __cplusplus
3663 choke me
3664 #endif
3665 _ACEOF
3666 rm -f conftest.$ac_objext
3667 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3668 (eval $ac_compile) 2>conftest.er1
3669 ac_status=$?
3670 grep -v '^ *+' conftest.er1 >conftest.err
3671 rm -f conftest.er1
3672 cat conftest.err >&5
3673 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3674 (exit $ac_status); } &&
3675 { ac_try='test -z "$ac_c_werror_flag"
3676 || test ! -s conftest.err'
3677 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3678 (eval $ac_try) 2>&5
3679 ac_status=$?
3680 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3681 (exit $ac_status); }; } &&
3682 { ac_try='test -s conftest.$ac_objext'
3683 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3684 (eval $ac_try) 2>&5
3685 ac_status=$?
3686 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3687 (exit $ac_status); }; }; then
3688 for ac_declaration in \
3689 '' \
3690 'extern "C" void std::exit (int) throw (); using std::exit;' \
3691 'extern "C" void std::exit (int); using std::exit;' \
3692 'extern "C" void exit (int) throw ();' \
3693 'extern "C" void exit (int);' \
3694 'void exit (int);'
3695 do
3696 cat >conftest.$ac_ext <<_ACEOF
3697 /* confdefs.h. */
3698 _ACEOF
3699 cat confdefs.h >>conftest.$ac_ext
3700 cat >>conftest.$ac_ext <<_ACEOF
3701 /* end confdefs.h. */
3702 $ac_declaration
3703 #include <stdlib.h>
3704 int
3705 main ()
3706 {
3707 exit (42);
3708 ;
3709 return 0;
3710 }
3711 _ACEOF
3712 rm -f conftest.$ac_objext
3713 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3714 (eval $ac_compile) 2>conftest.er1
3715 ac_status=$?
3716 grep -v '^ *+' conftest.er1 >conftest.err
3717 rm -f conftest.er1
3718 cat conftest.err >&5
3719 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3720 (exit $ac_status); } &&
3721 { ac_try='test -z "$ac_c_werror_flag"
3722 || test ! -s conftest.err'
3723 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3724 (eval $ac_try) 2>&5
3725 ac_status=$?
3726 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3727 (exit $ac_status); }; } &&
3728 { ac_try='test -s conftest.$ac_objext'
3729 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3730 (eval $ac_try) 2>&5
3731 ac_status=$?
3732 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3733 (exit $ac_status); }; }; then
3734 :
3735 else
3736 echo "$as_me: failed program was:" >&5
3737 sed 's/^/| /' conftest.$ac_ext >&5
3738
3739 continue
3740 fi
3741 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3742 cat >conftest.$ac_ext <<_ACEOF
3743 /* confdefs.h. */
3744 _ACEOF
3745 cat confdefs.h >>conftest.$ac_ext
3746 cat >>conftest.$ac_ext <<_ACEOF
3747 /* end confdefs.h. */
3748 $ac_declaration
3749 int
3750 main ()
3751 {
3752 exit (42);
3753 ;
3754 return 0;
3755 }
3756 _ACEOF
3757 rm -f conftest.$ac_objext
3758 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3759 (eval $ac_compile) 2>conftest.er1
3760 ac_status=$?
3761 grep -v '^ *+' conftest.er1 >conftest.err
3762 rm -f conftest.er1
3763 cat conftest.err >&5
3764 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3765 (exit $ac_status); } &&
3766 { ac_try='test -z "$ac_c_werror_flag"
3767 || test ! -s conftest.err'
3768 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3769 (eval $ac_try) 2>&5
3770 ac_status=$?
3771 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3772 (exit $ac_status); }; } &&
3773 { ac_try='test -s conftest.$ac_objext'
3774 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3775 (eval $ac_try) 2>&5
3776 ac_status=$?
3777 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3778 (exit $ac_status); }; }; then
3779 break
3780 else
3781 echo "$as_me: failed program was:" >&5
3782 sed 's/^/| /' conftest.$ac_ext >&5
3783
3784 fi
3785 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3786 done
3787 rm -f conftest*
3788 if test -n "$ac_declaration"; then
3789 echo '#ifdef __cplusplus' >>confdefs.h
3790 echo $ac_declaration >>confdefs.h
3791 echo '#endif' >>confdefs.h
3792 fi
3793
3794 else
3795 echo "$as_me: failed program was:" >&5
3796 sed 's/^/| /' conftest.$ac_ext >&5
3797
3798 fi
3799 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3800 ac_ext=c
3801 ac_cpp='$CPP $CPPFLAGS'
3802 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3803 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3804 ac_compiler_gnu=$ac_cv_c_compiler_gnu
3805
3806 depcc="$CC" am_compiler_list=
3807
3808 echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
3809 echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
3810 if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
3811 echo $ECHO_N "(cached) $ECHO_C" >&6
3812 else
3813 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
3814 # We make a subdir and do the tests there. Otherwise we can end up
3815 # making bogus files that we don't know about and never remove. For
3816 # instance it was reported that on HP-UX the gcc test will end up
3817 # making a dummy file named `D' -- because `-MD' means `put the output
3818 # in D'.
3819 mkdir conftest.dir
3820 # Copy depcomp to subdir because otherwise we won't find it if we're
3821 # using a relative directory.
3822 cp "$am_depcomp" conftest.dir
3823 cd conftest.dir
3824 # We will build objects and dependencies in a subdirectory because
3825 # it helps to detect inapplicable dependency modes. For instance
3826 # both Tru64's cc and ICC support -MD to output dependencies as a
3827 # side effect of compilation, but ICC will put the dependencies in
3828 # the current directory while Tru64 will put them in the object
3829 # directory.
3830 mkdir sub
3831
3832 am_cv_CC_dependencies_compiler_type=none
3833 if test "$am_compiler_list" = ""; then
3834 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
3835 fi
3836 for depmode in $am_compiler_list; do
3837 # Setup a source with many dependencies, because some compilers
3838 # like to wrap large dependency lists on column 80 (with \), and
3839 # we should not choose a depcomp mode which is confused by this.
3840 #
3841 # We need to recreate these files for each test, as the compiler may
3842 # overwrite some of them when testing with obscure command lines.
3843 # This happens at least with the AIX C compiler.
3844 : > sub/conftest.c
3845 for i in 1 2 3 4 5 6; do
3846 echo '#include "conftst'$i'.h"' >> sub/conftest.c
3847 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
3848 # Solaris 8's {/usr,}/bin/sh.
3849 touch sub/conftst$i.h
3850 done
3851 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
3852
3853 case $depmode in
3854 nosideeffect)
3855 # after this tag, mechanisms are not by side-effect, so they'll
3856 # only be used when explicitly requested
3857 if test "x$enable_dependency_tracking" = xyes; then
3858 continue
3859 else
3860 break
3861 fi
3862 ;;
3863 none) break ;;
3864 esac
3865 # We check with `-c' and `-o' for the sake of the "dashmstdout"
3866 # mode. It turns out that the SunPro C++ compiler does not properly
3867 # handle `-M -o', and we need to detect this.
3868 if depmode=$depmode \
3869 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
3870 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
3871 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
3872 >/dev/null 2>conftest.err &&
3873 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
3874 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
3875 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
3876 # icc doesn't choke on unknown options, it will just issue warnings
3877 # or remarks (even with -Werror). So we grep stderr for any message
3878 # that says an option was ignored or not supported.
3879 # When given -MP, icc 7.0 and 7.1 complain thusly:
3880 # icc: Command line warning: ignoring option '-M'; no argument required
3881 # The diagnosis changed in icc 8.0:
3882 # icc: Command line remark: option '-MP' not supported
3883 if (grep 'ignoring option' conftest.err ||
3884 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
3885 am_cv_CC_dependencies_compiler_type=$depmode
3886 break
3887 fi
3888 fi
3889 done
3890
3891 cd ..
3892 rm -rf conftest.dir
3893 else
3894 am_cv_CC_dependencies_compiler_type=none
3895 fi
3896
3897 fi
3898 echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
3899 echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
3900 CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
3901
3902
3903
3904 if
3905 test "x$enable_dependency_tracking" != xno \
3906 && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
3907 am__fastdepCC_TRUE=
3908 am__fastdepCC_FALSE='#'
3909 else
3910 am__fastdepCC_TRUE='#'
3911 am__fastdepCC_FALSE=
3912 fi
3913
3914
3915
3916 am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
3917
3918 if test -n "$ac_tool_prefix"; then
3919 # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
3920 set dummy ${ac_tool_prefix}ranlib; ac_word=$2
3921 echo "$as_me:$LINENO: checking for $ac_word" >&5
3922 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3923 if test "${ac_cv_prog_RANLIB+set}" = set; then
3924 echo $ECHO_N "(cached) $ECHO_C" >&6
3925 else
3926 if test -n "$RANLIB"; then
3927 ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
3928 else
3929 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3930 for as_dir in $PATH
3931 do
3932 IFS=$as_save_IFS
3933 test -z "$as_dir" && as_dir=.
3934 for ac_exec_ext in '' $ac_executable_extensions; do
3935 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3936 ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
3937 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3938 break 2
3939 fi
3940 done
3941 done
3942
3943 fi
3944 fi
3945 RANLIB=$ac_cv_prog_RANLIB
3946 if test -n "$RANLIB"; then
3947 echo "$as_me:$LINENO: result: $RANLIB" >&5
3948 echo "${ECHO_T}$RANLIB" >&6
3949 else
3950 echo "$as_me:$LINENO: result: no" >&5
3951 echo "${ECHO_T}no" >&6
3952 fi
3953
3954 fi
3955 if test -z "$ac_cv_prog_RANLIB"; then
3956 ac_ct_RANLIB=$RANLIB
3957 # Extract the first word of "ranlib", so it can be a program name with args.
3958 set dummy ranlib; ac_word=$2
3959 echo "$as_me:$LINENO: checking for $ac_word" >&5
3960 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3961 if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
3962 echo $ECHO_N "(cached) $ECHO_C" >&6
3963 else
3964 if test -n "$ac_ct_RANLIB"; then
3965 ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
3966 else
3967 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3968 for as_dir in $PATH
3969 do
3970 IFS=$as_save_IFS
3971 test -z "$as_dir" && as_dir=.
3972 for ac_exec_ext in '' $ac_executable_extensions; do
3973 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3974 ac_cv_prog_ac_ct_RANLIB="ranlib"
3975 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3976 break 2
3977 fi
3978 done
3979 done
3980
3981 test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
3982 fi
3983 fi
3984 ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
3985 if test -n "$ac_ct_RANLIB"; then
3986 echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
3987 echo "${ECHO_T}$ac_ct_RANLIB" >&6
3988 else
3989 echo "$as_me:$LINENO: result: no" >&5
3990 echo "${ECHO_T}no" >&6
3991 fi
3992
3993 RANLIB=$ac_ct_RANLIB
3994 else
3995 RANLIB="$ac_cv_prog_RANLIB"
3996 fi
3997
3998 ac_ext=c
3999 ac_cpp='$CPP $CPPFLAGS'
4000 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4001 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4002 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4003 echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
4004 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
4005 # On Suns, sometimes $CPP names a directory.
4006 if test -n "$CPP" && test -d "$CPP"; then
4007 CPP=
4008 fi
4009 if test -z "$CPP"; then
4010 if test "${ac_cv_prog_CPP+set}" = set; then
4011 echo $ECHO_N "(cached) $ECHO_C" >&6
4012 else
4013 # Double quotes because CPP needs to be expanded
4014 for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
4015 do
4016 ac_preproc_ok=false
4017 for ac_c_preproc_warn_flag in '' yes
4018 do
4019 # Use a header file that comes with gcc, so configuring glibc
4020 # with a fresh cross-compiler works.
4021 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4022 # <limits.h> exists even on freestanding compilers.
4023 # On the NeXT, cc -E runs the code through the compiler's parser,
4024 # not just through cpp. "Syntax error" is here to catch this case.
4025 cat >conftest.$ac_ext <<_ACEOF
4026 /* confdefs.h. */
4027 _ACEOF
4028 cat confdefs.h >>conftest.$ac_ext
4029 cat >>conftest.$ac_ext <<_ACEOF
4030 /* end confdefs.h. */
4031 #ifdef __STDC__
4032 # include <limits.h>
4033 #else
4034 # include <assert.h>
4035 #endif
4036 Syntax error
4037 _ACEOF
4038 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4039 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4040 ac_status=$?
4041 grep -v '^ *+' conftest.er1 >conftest.err
4042 rm -f conftest.er1
4043 cat conftest.err >&5
4044 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4045 (exit $ac_status); } >/dev/null; then
4046 if test -s conftest.err; then
4047 ac_cpp_err=$ac_c_preproc_warn_flag
4048 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4049 else
4050 ac_cpp_err=
4051 fi
4052 else
4053 ac_cpp_err=yes
4054 fi
4055 if test -z "$ac_cpp_err"; then
4056 :
4057 else
4058 echo "$as_me: failed program was:" >&5
4059 sed 's/^/| /' conftest.$ac_ext >&5
4060
4061 # Broken: fails on valid input.
4062 continue
4063 fi
4064 rm -f conftest.err conftest.$ac_ext
4065
4066 # OK, works on sane cases. Now check whether non-existent headers
4067 # can be detected and how.
4068 cat >conftest.$ac_ext <<_ACEOF
4069 /* confdefs.h. */
4070 _ACEOF
4071 cat confdefs.h >>conftest.$ac_ext
4072 cat >>conftest.$ac_ext <<_ACEOF
4073 /* end confdefs.h. */
4074 #include <ac_nonexistent.h>
4075 _ACEOF
4076 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4077 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4078 ac_status=$?
4079 grep -v '^ *+' conftest.er1 >conftest.err
4080 rm -f conftest.er1
4081 cat conftest.err >&5
4082 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4083 (exit $ac_status); } >/dev/null; then
4084 if test -s conftest.err; then
4085 ac_cpp_err=$ac_c_preproc_warn_flag
4086 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4087 else
4088 ac_cpp_err=
4089 fi
4090 else
4091 ac_cpp_err=yes
4092 fi
4093 if test -z "$ac_cpp_err"; then
4094 # Broken: success on invalid input.
4095 continue
4096 else
4097 echo "$as_me: failed program was:" >&5
4098 sed 's/^/| /' conftest.$ac_ext >&5
4099
4100 # Passes both tests.
4101 ac_preproc_ok=:
4102 break
4103 fi
4104 rm -f conftest.err conftest.$ac_ext
4105
4106 done
4107 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
4108 rm -f conftest.err conftest.$ac_ext
4109 if $ac_preproc_ok; then
4110 break
4111 fi
4112
4113 done
4114 ac_cv_prog_CPP=$CPP
4115
4116 fi
4117 CPP=$ac_cv_prog_CPP
4118 else
4119 ac_cv_prog_CPP=$CPP
4120 fi
4121 echo "$as_me:$LINENO: result: $CPP" >&5
4122 echo "${ECHO_T}$CPP" >&6
4123 ac_preproc_ok=false
4124 for ac_c_preproc_warn_flag in '' yes
4125 do
4126 # Use a header file that comes with gcc, so configuring glibc
4127 # with a fresh cross-compiler works.
4128 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4129 # <limits.h> exists even on freestanding compilers.
4130 # On the NeXT, cc -E runs the code through the compiler's parser,
4131 # not just through cpp. "Syntax error" is here to catch this case.
4132 cat >conftest.$ac_ext <<_ACEOF
4133 /* confdefs.h. */
4134 _ACEOF
4135 cat confdefs.h >>conftest.$ac_ext
4136 cat >>conftest.$ac_ext <<_ACEOF
4137 /* end confdefs.h. */
4138 #ifdef __STDC__
4139 # include <limits.h>
4140 #else
4141 # include <assert.h>
4142 #endif
4143 Syntax error
4144 _ACEOF
4145 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4146 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4147 ac_status=$?
4148 grep -v '^ *+' conftest.er1 >conftest.err
4149 rm -f conftest.er1
4150 cat conftest.err >&5
4151 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4152 (exit $ac_status); } >/dev/null; then
4153 if test -s conftest.err; then
4154 ac_cpp_err=$ac_c_preproc_warn_flag
4155 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4156 else
4157 ac_cpp_err=
4158 fi
4159 else
4160 ac_cpp_err=yes
4161 fi
4162 if test -z "$ac_cpp_err"; then
4163 :
4164 else
4165 echo "$as_me: failed program was:" >&5
4166 sed 's/^/| /' conftest.$ac_ext >&5
4167
4168 # Broken: fails on valid input.
4169 continue
4170 fi
4171 rm -f conftest.err conftest.$ac_ext
4172
4173 # OK, works on sane cases. Now check whether non-existent headers
4174 # can be detected and how.
4175 cat >conftest.$ac_ext <<_ACEOF
4176 /* confdefs.h. */
4177 _ACEOF
4178 cat confdefs.h >>conftest.$ac_ext
4179 cat >>conftest.$ac_ext <<_ACEOF
4180 /* end confdefs.h. */
4181 #include <ac_nonexistent.h>
4182 _ACEOF
4183 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4184 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4185 ac_status=$?
4186 grep -v '^ *+' conftest.er1 >conftest.err
4187 rm -f conftest.er1
4188 cat conftest.err >&5
4189 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4190 (exit $ac_status); } >/dev/null; then
4191 if test -s conftest.err; then
4192 ac_cpp_err=$ac_c_preproc_warn_flag
4193 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4194 else
4195 ac_cpp_err=
4196 fi
4197 else
4198 ac_cpp_err=yes
4199 fi
4200 if test -z "$ac_cpp_err"; then
4201 # Broken: success on invalid input.
4202 continue
4203 else
4204 echo "$as_me: failed program was:" >&5
4205 sed 's/^/| /' conftest.$ac_ext >&5
4206
4207 # Passes both tests.
4208 ac_preproc_ok=:
4209 break
4210 fi
4211 rm -f conftest.err conftest.$ac_ext
4212
4213 done
4214 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
4215 rm -f conftest.err conftest.$ac_ext
4216 if $ac_preproc_ok; then
4217 :
4218 else
4219 { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
4220 See \`config.log' for more details." >&5
4221 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
4222 See \`config.log' for more details." >&2;}
4223 { (exit 1); exit 1; }; }
4224 fi
4225
4226 ac_ext=c
4227 ac_cpp='$CPP $CPPFLAGS'
4228 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4229 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4230 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4231
4232 ac_ext=cc
4233 ac_cpp='$CXXCPP $CPPFLAGS'
4234 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4235 ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4236 ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
4237 if test -n "$ac_tool_prefix"; then
4238 for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
4239 do
4240 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
4241 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
4242 echo "$as_me:$LINENO: checking for $ac_word" >&5
4243 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
4244 if test "${ac_cv_prog_CXX+set}" = set; then
4245 echo $ECHO_N "(cached) $ECHO_C" >&6
4246 else
4247 if test -n "$CXX"; then
4248 ac_cv_prog_CXX="$CXX" # Let the user override the test.
4249 else
4250 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4251 for as_dir in $PATH
4252 do
4253 IFS=$as_save_IFS
4254 test -z "$as_dir" && as_dir=.
4255 for ac_exec_ext in '' $ac_executable_extensions; do
4256 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4257 ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
4258 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
4259 break 2
4260 fi
4261 done
4262 done
4263
4264 fi
4265 fi
4266 CXX=$ac_cv_prog_CXX
4267 if test -n "$CXX"; then
4268 echo "$as_me:$LINENO: result: $CXX" >&5
4269 echo "${ECHO_T}$CXX" >&6
4270 else
4271 echo "$as_me:$LINENO: result: no" >&5
4272 echo "${ECHO_T}no" >&6
4273 fi
4274
4275 test -n "$CXX" && break
4276 done
4277 fi
4278 if test -z "$CXX"; then
4279 ac_ct_CXX=$CXX
4280 for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
4281 do
4282 # Extract the first word of "$ac_prog", so it can be a program name with args.
4283 set dummy $ac_prog; ac_word=$2
4284 echo "$as_me:$LINENO: checking for $ac_word" >&5
4285 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
4286 if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
4287 echo $ECHO_N "(cached) $ECHO_C" >&6
4288 else
4289 if test -n "$ac_ct_CXX"; then
4290 ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
4291 else
4292 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4293 for as_dir in $PATH
4294 do
4295 IFS=$as_save_IFS
4296 test -z "$as_dir" && as_dir=.
4297 for ac_exec_ext in '' $ac_executable_extensions; do
4298 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4299 ac_cv_prog_ac_ct_CXX="$ac_prog"
4300 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
4301 break 2
4302 fi
4303 done
4304 done
4305
4306 fi
4307 fi
4308 ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
4309 if test -n "$ac_ct_CXX"; then
4310 echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
4311 echo "${ECHO_T}$ac_ct_CXX" >&6
4312 else
4313 echo "$as_me:$LINENO: result: no" >&5
4314 echo "${ECHO_T}no" >&6
4315 fi
4316
4317 test -n "$ac_ct_CXX" && break
4318 done
4319 test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
4320
4321 CXX=$ac_ct_CXX
4322 fi
4323
4324
4325 # Provide some information about the compiler.
4326 echo "$as_me:$LINENO:" \
4327 "checking for C++ compiler version" >&5
4328 ac_compiler=`set X $ac_compile; echo $2`
4329 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
4330 (eval $ac_compiler --version </dev/null >&5) 2>&5
4331 ac_status=$?
4332 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4333 (exit $ac_status); }
4334 { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
4335 (eval $ac_compiler -v </dev/null >&5) 2>&5
4336 ac_status=$?
4337 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4338 (exit $ac_status); }
4339 { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
4340 (eval $ac_compiler -V </dev/null >&5) 2>&5
4341 ac_status=$?
4342 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4343 (exit $ac_status); }
4344
4345 echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
4346 echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
4347 if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
4348 echo $ECHO_N "(cached) $ECHO_C" >&6
4349 else
4350 cat >conftest.$ac_ext <<_ACEOF
4351 /* confdefs.h. */
4352 _ACEOF
4353 cat confdefs.h >>conftest.$ac_ext
4354 cat >>conftest.$ac_ext <<_ACEOF
4355 /* end confdefs.h. */
4356
4357 int
4358 main ()
4359 {
4360 #ifndef __GNUC__
4361 choke me
4362 #endif
4363
4364 ;
4365 return 0;
4366 }
4367 _ACEOF
4368 rm -f conftest.$ac_objext
4369 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4370 (eval $ac_compile) 2>conftest.er1
4371 ac_status=$?
4372 grep -v '^ *+' conftest.er1 >conftest.err
4373 rm -f conftest.er1
4374 cat conftest.err >&5
4375 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4376 (exit $ac_status); } &&
4377 { ac_try='test -z "$ac_cxx_werror_flag"
4378 || test ! -s conftest.err'
4379 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4380 (eval $ac_try) 2>&5
4381 ac_status=$?
4382 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4383 (exit $ac_status); }; } &&
4384 { ac_try='test -s conftest.$ac_objext'
4385 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4386 (eval $ac_try) 2>&5
4387 ac_status=$?
4388 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4389 (exit $ac_status); }; }; then
4390 ac_compiler_gnu=yes
4391 else
4392 echo "$as_me: failed program was:" >&5
4393 sed 's/^/| /' conftest.$ac_ext >&5
4394
4395 ac_compiler_gnu=no
4396 fi
4397 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4398 ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
4399
4400 fi
4401 echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
4402 echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
4403 GXX=`test $ac_compiler_gnu = yes && echo yes`
4404 ac_test_CXXFLAGS=${CXXFLAGS+set}
4405 ac_save_CXXFLAGS=$CXXFLAGS
4406 CXXFLAGS="-g"
4407 echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
4408 echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
4409 if test "${ac_cv_prog_cxx_g+set}" = set; then
4410 echo $ECHO_N "(cached) $ECHO_C" >&6
4411 else
4412 cat >conftest.$ac_ext <<_ACEOF
4413 /* confdefs.h. */
4414 _ACEOF
4415 cat confdefs.h >>conftest.$ac_ext
4416 cat >>conftest.$ac_ext <<_ACEOF
4417 /* end confdefs.h. */
4418
4419 int
4420 main ()
4421 {
4422
4423 ;
4424 return 0;
4425 }
4426 _ACEOF
4427 rm -f conftest.$ac_objext
4428 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4429 (eval $ac_compile) 2>conftest.er1
4430 ac_status=$?
4431 grep -v '^ *+' conftest.er1 >conftest.err
4432 rm -f conftest.er1
4433 cat conftest.err >&5
4434 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4435 (exit $ac_status); } &&
4436 { ac_try='test -z "$ac_cxx_werror_flag"
4437 || test ! -s conftest.err'
4438 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4439 (eval $ac_try) 2>&5
4440 ac_status=$?
4441 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4442 (exit $ac_status); }; } &&
4443 { ac_try='test -s conftest.$ac_objext'
4444 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4445 (eval $ac_try) 2>&5
4446 ac_status=$?
4447 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4448 (exit $ac_status); }; }; then
4449 ac_cv_prog_cxx_g=yes
4450 else
4451 echo "$as_me: failed program was:" >&5
4452 sed 's/^/| /' conftest.$ac_ext >&5
4453
4454 ac_cv_prog_cxx_g=no
4455 fi
4456 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4457 fi
4458 echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
4459 echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
4460 if test "$ac_test_CXXFLAGS" = set; then
4461 CXXFLAGS=$ac_save_CXXFLAGS
4462 elif test $ac_cv_prog_cxx_g = yes; then
4463 if test "$GXX" = yes; then
4464 CXXFLAGS="-g -O2"
4465 else
4466 CXXFLAGS="-g"
4467 fi
4468 else
4469 if test "$GXX" = yes; then
4470 CXXFLAGS="-O2"
4471 else
4472 CXXFLAGS=
4473 fi
4474 fi
4475 for ac_declaration in \
4476 '' \
4477 'extern "C" void std::exit (int) throw (); using std::exit;' \
4478 'extern "C" void std::exit (int); using std::exit;' \
4479 'extern "C" void exit (int) throw ();' \
4480 'extern "C" void exit (int);' \
4481 'void exit (int);'
4482 do
4483 cat >conftest.$ac_ext <<_ACEOF
4484 /* confdefs.h. */
4485 _ACEOF
4486 cat confdefs.h >>conftest.$ac_ext
4487 cat >>conftest.$ac_ext <<_ACEOF
4488 /* end confdefs.h. */
4489 $ac_declaration
4490 #include <stdlib.h>
4491 int
4492 main ()
4493 {
4494 exit (42);
4495 ;
4496 return 0;
4497 }
4498 _ACEOF
4499 rm -f conftest.$ac_objext
4500 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4501 (eval $ac_compile) 2>conftest.er1
4502 ac_status=$?
4503 grep -v '^ *+' conftest.er1 >conftest.err
4504 rm -f conftest.er1
4505 cat conftest.err >&5
4506 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4507 (exit $ac_status); } &&
4508 { ac_try='test -z "$ac_cxx_werror_flag"
4509 || test ! -s conftest.err'
4510 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4511 (eval $ac_try) 2>&5
4512 ac_status=$?
4513 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4514 (exit $ac_status); }; } &&
4515 { ac_try='test -s conftest.$ac_objext'
4516 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4517 (eval $ac_try) 2>&5
4518 ac_status=$?
4519 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4520 (exit $ac_status); }; }; then
4521 :
4522 else
4523 echo "$as_me: failed program was:" >&5
4524 sed 's/^/| /' conftest.$ac_ext >&5
4525
4526 continue
4527 fi
4528 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4529 cat >conftest.$ac_ext <<_ACEOF
4530 /* confdefs.h. */
4531 _ACEOF
4532 cat confdefs.h >>conftest.$ac_ext
4533 cat >>conftest.$ac_ext <<_ACEOF
4534 /* end confdefs.h. */
4535 $ac_declaration
4536 int
4537 main ()
4538 {
4539 exit (42);
4540 ;
4541 return 0;
4542 }
4543 _ACEOF
4544 rm -f conftest.$ac_objext
4545 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4546 (eval $ac_compile) 2>conftest.er1
4547 ac_status=$?
4548 grep -v '^ *+' conftest.er1 >conftest.err
4549 rm -f conftest.er1
4550 cat conftest.err >&5
4551 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4552 (exit $ac_status); } &&
4553 { ac_try='test -z "$ac_cxx_werror_flag"
4554 || test ! -s conftest.err'
4555 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4556 (eval $ac_try) 2>&5
4557 ac_status=$?
4558 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4559 (exit $ac_status); }; } &&
4560 { ac_try='test -s conftest.$ac_objext'
4561 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4562 (eval $ac_try) 2>&5
4563 ac_status=$?
4564 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4565 (exit $ac_status); }; }; then
4566 break
4567 else
4568 echo "$as_me: failed program was:" >&5
4569 sed 's/^/| /' conftest.$ac_ext >&5
4570
4571 fi
4572 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4573 done
4574 rm -f conftest*
4575 if test -n "$ac_declaration"; then
4576 echo '#ifdef __cplusplus' >>confdefs.h
4577 echo $ac_declaration >>confdefs.h
4578 echo '#endif' >>confdefs.h
4579 fi
4580
4581 ac_ext=c
4582 ac_cpp='$CPP $CPPFLAGS'
4583 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4584 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4585 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4586
4587 depcc="$CXX" am_compiler_list=
4588
4589 echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
4590 echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
4591 if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then
4592 echo $ECHO_N "(cached) $ECHO_C" >&6
4593 else
4594 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
4595 # We make a subdir and do the tests there. Otherwise we can end up
4596 # making bogus files that we don't know about and never remove. For
4597 # instance it was reported that on HP-UX the gcc test will end up
4598 # making a dummy file named `D' -- because `-MD' means `put the output
4599 # in D'.
4600 mkdir conftest.dir
4601 # Copy depcomp to subdir because otherwise we won't find it if we're
4602 # using a relative directory.
4603 cp "$am_depcomp" conftest.dir
4604 cd conftest.dir
4605 # We will build objects and dependencies in a subdirectory because
4606 # it helps to detect inapplicable dependency modes. For instance
4607 # both Tru64's cc and ICC support -MD to output dependencies as a
4608 # side effect of compilation, but ICC will put the dependencies in
4609 # the current directory while Tru64 will put them in the object
4610 # directory.
4611 mkdir sub
4612
4613 am_cv_CXX_dependencies_compiler_type=none
4614 if test "$am_compiler_list" = ""; then
4615 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
4616 fi
4617 for depmode in $am_compiler_list; do
4618 # Setup a source with many dependencies, because some compilers
4619 # like to wrap large dependency lists on column 80 (with \), and
4620 # we should not choose a depcomp mode which is confused by this.
4621 #
4622 # We need to recreate these files for each test, as the compiler may
4623 # overwrite some of them when testing with obscure command lines.
4624 # This happens at least with the AIX C compiler.
4625 : > sub/conftest.c
4626 for i in 1 2 3 4 5 6; do
4627 echo '#include "conftst'$i'.h"' >> sub/conftest.c
4628 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
4629 # Solaris 8's {/usr,}/bin/sh.
4630 touch sub/conftst$i.h
4631 done
4632 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
4633
4634 case $depmode in
4635 nosideeffect)
4636 # after this tag, mechanisms are not by side-effect, so they'll
4637 # only be used when explicitly requested
4638 if test "x$enable_dependency_tracking" = xyes; then
4639 continue
4640 else
4641 break
4642 fi
4643 ;;
4644 none) break ;;
4645 esac
4646 # We check with `-c' and `-o' for the sake of the "dashmstdout"
4647 # mode. It turns out that the SunPro C++ compiler does not properly
4648 # handle `-M -o', and we need to detect this.
4649 if depmode=$depmode \
4650 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
4651 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
4652 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
4653 >/dev/null 2>conftest.err &&
4654 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
4655 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
4656 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
4657 # icc doesn't choke on unknown options, it will just issue warnings
4658 # or remarks (even with -Werror). So we grep stderr for any message
4659 # that says an option was ignored or not supported.
4660 # When given -MP, icc 7.0 and 7.1 complain thusly:
4661 # icc: Command line warning: ignoring option '-M'; no argument required
4662 # The diagnosis changed in icc 8.0:
4663 # icc: Command line remark: option '-MP' not supported
4664 if (grep 'ignoring option' conftest.err ||
4665 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
4666 am_cv_CXX_dependencies_compiler_type=$depmode
4667 break
4668 fi
4669 fi
4670 done
4671
4672 cd ..
4673 rm -rf conftest.dir
4674 else
4675 am_cv_CXX_dependencies_compiler_type=none
4676 fi
4677
4678 fi
4679 echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5
4680 echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6
4681 CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type
4682
4683
4684
4685 if
4686 test "x$enable_dependency_tracking" != xno \
4687 && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then
4688 am__fastdepCXX_TRUE=
4689 am__fastdepCXX_FALSE='#'
4690 else
4691 am__fastdepCXX_TRUE='#'
4692 am__fastdepCXX_FALSE=
4693 fi
4694
4695
4696
4697 if test "`$CPP -v < /dev/null 2>&1 | grep '/usr/local/include' 2>&1`" = ""; then
4698 CPPFLAGS="$CPPFLAGS -I/usr/local/include"
4699 LDFLAGS="$LDFLAGS -L/usr/local/lib"
4700 fi
4701
4702
4703
4704 for ac_func in gethostbyname
4705 do
4706 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
4707 echo "$as_me:$LINENO: checking for $ac_func" >&5
4708 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
4709 if eval "test \"\${$as_ac_var+set}\" = set"; then
4710 echo $ECHO_N "(cached) $ECHO_C" >&6
4711 else
4712 cat >conftest.$ac_ext <<_ACEOF
4713 /* confdefs.h. */
4714 _ACEOF
4715 cat confdefs.h >>conftest.$ac_ext
4716 cat >>conftest.$ac_ext <<_ACEOF
4717 /* end confdefs.h. */
4718 /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
4719 For example, HP-UX 11i <limits.h> declares gettimeofday. */
4720 #define $ac_func innocuous_$ac_func
4721
4722 /* System header to define __stub macros and hopefully few prototypes,
4723 which can conflict with char $ac_func (); below.
4724 Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4725 <limits.h> exists even on freestanding compilers. */
4726
4727 #ifdef __STDC__
4728 # include <limits.h>
4729 #else
4730 # include <assert.h>
4731 #endif
4732
4733 #undef $ac_func
4734
4735 /* Override any gcc2 internal prototype to avoid an error. */
4736 #ifdef __cplusplus
4737 extern "C"
4738 {
4739 #endif
4740 /* We use char because int might match the return type of a gcc2
4741 builtin and then its argument prototype would still apply. */
4742 char $ac_func ();
4743 /* The GNU C library defines this for functions which it implements
4744 to always fail with ENOSYS. Some functions are actually named
4745 something starting with __ and the normal name is an alias. */
4746 #if defined (__stub_$ac_func) || defined (__stub___$ac_func)
4747 choke me
4748 #else
4749 char (*f) () = $ac_func;
4750 #endif
4751 #ifdef __cplusplus
4752 }
4753 #endif
4754
4755 int
4756 main ()
4757 {
4758 return f != $ac_func;
4759 ;
4760 return 0;
4761 }
4762 _ACEOF
4763 rm -f conftest.$ac_objext conftest$ac_exeext
4764 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
4765 (eval $ac_link) 2>conftest.er1
4766 ac_status=$?
4767 grep -v '^ *+' conftest.er1 >conftest.err
4768 rm -f conftest.er1
4769 cat conftest.err >&5
4770 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4771 (exit $ac_status); } &&
4772 { ac_try='test -z "$ac_c_werror_flag"
4773 || test ! -s conftest.err'
4774 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4775 (eval $ac_try) 2>&5
4776 ac_status=$?
4777 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4778 (exit $ac_status); }; } &&
4779 { ac_try='test -s conftest$ac_exeext'
4780 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4781 (eval $ac_try) 2>&5
4782 ac_status=$?
4783 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4784 (exit $ac_status); }; }; then
4785 eval "$as_ac_var=yes"
4786 else
4787 echo "$as_me: failed program was:" >&5
4788 sed 's/^/| /' conftest.$ac_ext >&5
4789
4790 eval "$as_ac_var=no"
4791 fi
4792 rm -f conftest.err conftest.$ac_objext \
4793 conftest$ac_exeext conftest.$ac_ext
4794 fi
4795 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
4796 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
4797 if test `eval echo '${'$as_ac_var'}'` = yes; then
4798 cat >>confdefs.h <<_ACEOF
4799 #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
4800 _ACEOF
4801
4802 else
4803
4804 echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5
4805 echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6
4806 if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then
4807 echo $ECHO_N "(cached) $ECHO_C" >&6
4808 else
4809 ac_check_lib_save_LIBS=$LIBS
4810 LIBS="-lnsl $LIBS"
4811 cat >conftest.$ac_ext <<_ACEOF
4812 /* confdefs.h. */
4813 _ACEOF
4814 cat confdefs.h >>conftest.$ac_ext
4815 cat >>conftest.$ac_ext <<_ACEOF
4816 /* end confdefs.h. */
4817
4818 /* Override any gcc2 internal prototype to avoid an error. */
4819 #ifdef __cplusplus
4820 extern "C"
4821 #endif
4822 /* We use char because int might match the return type of a gcc2
4823 builtin and then its argument prototype would still apply. */
4824 char gethostbyname ();
4825 int
4826 main ()
4827 {
4828 gethostbyname ();
4829 ;
4830 return 0;
4831 }
4832 _ACEOF
4833 rm -f conftest.$ac_objext conftest$ac_exeext
4834 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
4835 (eval $ac_link) 2>conftest.er1
4836 ac_status=$?
4837 grep -v '^ *+' conftest.er1 >conftest.err
4838 rm -f conftest.er1
4839 cat conftest.err >&5
4840 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4841 (exit $ac_status); } &&
4842 { ac_try='test -z "$ac_c_werror_flag"
4843 || test ! -s conftest.err'
4844 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4845 (eval $ac_try) 2>&5
4846 ac_status=$?
4847 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4848 (exit $ac_status); }; } &&
4849 { ac_try='test -s conftest$ac_exeext'
4850 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4851 (eval $ac_try) 2>&5
4852 ac_status=$?
4853 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4854 (exit $ac_status); }; }; then
4855 ac_cv_lib_nsl_gethostbyname=yes
4856 else
4857 echo "$as_me: failed program was:" >&5
4858 sed 's/^/| /' conftest.$ac_ext >&5
4859
4860 ac_cv_lib_nsl_gethostbyname=no
4861 fi
4862 rm -f conftest.err conftest.$ac_objext \
4863 conftest$ac_exeext conftest.$ac_ext
4864 LIBS=$ac_check_lib_save_LIBS
4865 fi
4866 echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5
4867 echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6
4868 if test $ac_cv_lib_nsl_gethostbyname = yes; then
4869 cat >>confdefs.h <<_ACEOF
4870 #define HAVE_LIBNSL 1
4871 _ACEOF
4872
4873 LIBS="-lnsl $LIBS"
4874
4875 else
4876
4877 echo "$as_me:$LINENO: checking for gethostbyname in -lsocket" >&5
4878 echo $ECHO_N "checking for gethostbyname in -lsocket... $ECHO_C" >&6
4879 if test "${ac_cv_lib_socket_gethostbyname+set}" = set; then
4880 echo $ECHO_N "(cached) $ECHO_C" >&6
4881 else
4882 ac_check_lib_save_LIBS=$LIBS
4883 LIBS="-lsocket $LIBS"
4884 cat >conftest.$ac_ext <<_ACEOF
4885 /* confdefs.h. */
4886 _ACEOF
4887 cat confdefs.h >>conftest.$ac_ext
4888 cat >>conftest.$ac_ext <<_ACEOF
4889 /* end confdefs.h. */
4890
4891 /* Override any gcc2 internal prototype to avoid an error. */
4892 #ifdef __cplusplus
4893 extern "C"
4894 #endif
4895 /* We use char because int might match the return type of a gcc2
4896 builtin and then its argument prototype would still apply. */
4897 char gethostbyname ();
4898 int
4899 main ()
4900 {
4901 gethostbyname ();
4902 ;
4903 return 0;
4904 }
4905 _ACEOF
4906 rm -f conftest.$ac_objext conftest$ac_exeext
4907 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
4908 (eval $ac_link) 2>conftest.er1
4909 ac_status=$?
4910 grep -v '^ *+' conftest.er1 >conftest.err
4911 rm -f conftest.er1
4912 cat conftest.err >&5
4913 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4914 (exit $ac_status); } &&
4915 { ac_try='test -z "$ac_c_werror_flag"
4916 || test ! -s conftest.err'
4917 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4918 (eval $ac_try) 2>&5
4919 ac_status=$?
4920 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4921 (exit $ac_status); }; } &&
4922 { ac_try='test -s conftest$ac_exeext'
4923 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4924 (eval $ac_try) 2>&5
4925 ac_status=$?
4926 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4927 (exit $ac_status); }; }; then
4928 ac_cv_lib_socket_gethostbyname=yes
4929 else
4930 echo "$as_me: failed program was:" >&5
4931 sed 's/^/| /' conftest.$ac_ext >&5
4932
4933 ac_cv_lib_socket_gethostbyname=no
4934 fi
4935 rm -f conftest.err conftest.$ac_objext \
4936 conftest$ac_exeext conftest.$ac_ext
4937 LIBS=$ac_check_lib_save_LIBS
4938 fi
4939 echo "$as_me:$LINENO: result: $ac_cv_lib_socket_gethostbyname" >&5
4940 echo "${ECHO_T}$ac_cv_lib_socket_gethostbyname" >&6
4941 if test $ac_cv_lib_socket_gethostbyname = yes; then
4942 cat >>confdefs.h <<_ACEOF
4943 #define HAVE_LIBSOCKET 1
4944 _ACEOF
4945
4946 LIBS="-lsocket $LIBS"
4947
4948 fi
4949
4950 fi
4951
4952 fi
4953 done
4954
4955
4956 for ac_func in setsockopt
4957 do
4958 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
4959 echo "$as_me:$LINENO: checking for $ac_func" >&5
4960 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
4961 if eval "test \"\${$as_ac_var+set}\" = set"; then
4962 echo $ECHO_N "(cached) $ECHO_C" >&6
4963 else
4964 cat >conftest.$ac_ext <<_ACEOF
4965 /* confdefs.h. */
4966 _ACEOF
4967 cat confdefs.h >>conftest.$ac_ext
4968 cat >>conftest.$ac_ext <<_ACEOF
4969 /* end confdefs.h. */
4970 /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
4971 For example, HP-UX 11i <limits.h> declares gettimeofday. */
4972 #define $ac_func innocuous_$ac_func
4973
4974 /* System header to define __stub macros and hopefully few prototypes,
4975 which can conflict with char $ac_func (); below.
4976 Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4977 <limits.h> exists even on freestanding compilers. */
4978
4979 #ifdef __STDC__
4980 # include <limits.h>
4981 #else
4982 # include <assert.h>
4983 #endif
4984
4985 #undef $ac_func
4986
4987 /* Override any gcc2 internal prototype to avoid an error. */
4988 #ifdef __cplusplus
4989 extern "C"
4990 {
4991 #endif
4992 /* We use char because int might match the return type of a gcc2
4993 builtin and then its argument prototype would still apply. */
4994 char $ac_func ();
4995 /* The GNU C library defines this for functions which it implements
4996 to always fail with ENOSYS. Some functions are actually named
4997 something starting with __ and the normal name is an alias. */
4998 #if defined (__stub_$ac_func) || defined (__stub___$ac_func)
4999 choke me
5000 #else
5001 char (*f) () = $ac_func;
5002 #endif
5003 #ifdef __cplusplus
5004 }
5005 #endif
5006
5007 int
5008 main ()
5009 {
5010 return f != $ac_func;
5011 ;
5012 return 0;
5013 }
5014 _ACEOF
5015 rm -f conftest.$ac_objext conftest$ac_exeext
5016 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5017 (eval $ac_link) 2>conftest.er1
5018 ac_status=$?
5019 grep -v '^ *+' conftest.er1 >conftest.err
5020 rm -f conftest.er1
5021 cat conftest.err >&5
5022 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5023 (exit $ac_status); } &&
5024 { ac_try='test -z "$ac_c_werror_flag"
5025 || test ! -s conftest.err'
5026 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5027 (eval $ac_try) 2>&5
5028 ac_status=$?
5029 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5030 (exit $ac_status); }; } &&
5031 { ac_try='test -s conftest$ac_exeext'
5032 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5033 (eval $ac_try) 2>&5
5034 ac_status=$?
5035 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5036 (exit $ac_status); }; }; then
5037 eval "$as_ac_var=yes"
5038 else
5039 echo "$as_me: failed program was:" >&5
5040 sed 's/^/| /' conftest.$ac_ext >&5
5041
5042 eval "$as_ac_var=no"
5043 fi
5044 rm -f conftest.err conftest.$ac_objext \
5045 conftest$ac_exeext conftest.$ac_ext
5046 fi
5047 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
5048 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
5049 if test `eval echo '${'$as_ac_var'}'` = yes; then
5050 cat >>confdefs.h <<_ACEOF
5051 #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
5052 _ACEOF
5053
5054 else
5055
5056 echo "$as_me:$LINENO: checking for setsockopt in -lsocket" >&5
5057 echo $ECHO_N "checking for setsockopt in -lsocket... $ECHO_C" >&6
5058 if test "${ac_cv_lib_socket_setsockopt+set}" = set; then
5059 echo $ECHO_N "(cached) $ECHO_C" >&6
5060 else
5061 ac_check_lib_save_LIBS=$LIBS
5062 LIBS="-lsocket $LIBS"
5063 cat >conftest.$ac_ext <<_ACEOF
5064 /* confdefs.h. */
5065 _ACEOF
5066 cat confdefs.h >>conftest.$ac_ext
5067 cat >>conftest.$ac_ext <<_ACEOF
5068 /* end confdefs.h. */
5069
5070 /* Override any gcc2 internal prototype to avoid an error. */
5071 #ifdef __cplusplus
5072 extern "C"
5073 #endif
5074 /* We use char because int might match the return type of a gcc2
5075 builtin and then its argument prototype would still apply. */
5076 char setsockopt ();
5077 int
5078 main ()
5079 {
5080 setsockopt ();
5081 ;
5082 return 0;
5083 }
5084 _ACEOF
5085 rm -f conftest.$ac_objext conftest$ac_exeext
5086 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5087 (eval $ac_link) 2>conftest.er1
5088 ac_status=$?
5089 grep -v '^ *+' conftest.er1 >conftest.err
5090 rm -f conftest.er1
5091 cat conftest.err >&5
5092 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5093 (exit $ac_status); } &&
5094 { ac_try='test -z "$ac_c_werror_flag"
5095 || test ! -s conftest.err'
5096 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5097 (eval $ac_try) 2>&5
5098 ac_status=$?
5099 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5100 (exit $ac_status); }; } &&
5101 { ac_try='test -s conftest$ac_exeext'
5102 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5103 (eval $ac_try) 2>&5
5104 ac_status=$?
5105 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5106 (exit $ac_status); }; }; then
5107 ac_cv_lib_socket_setsockopt=yes
5108 else
5109 echo "$as_me: failed program was:" >&5
5110 sed 's/^/| /' conftest.$ac_ext >&5
5111
5112 ac_cv_lib_socket_setsockopt=no
5113 fi
5114 rm -f conftest.err conftest.$ac_objext \
5115 conftest$ac_exeext conftest.$ac_ext
5116 LIBS=$ac_check_lib_save_LIBS
5117 fi
5118 echo "$as_me:$LINENO: result: $ac_cv_lib_socket_setsockopt" >&5
5119 echo "${ECHO_T}$ac_cv_lib_socket_setsockopt" >&6
5120 if test $ac_cv_lib_socket_setsockopt = yes; then
5121 cat >>confdefs.h <<_ACEOF
5122 #define HAVE_LIBSOCKET 1
5123 _ACEOF
5124
5125 LIBS="-lsocket $LIBS"
5126
5127 fi
5128
5129 fi
5130 done
5131
5132
5133 echo "$as_me:$LINENO: checking for socklen_t" >&5
5134 echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6
5135 ac_cv_socklen_t=""
5136 cat >conftest.$ac_ext <<_ACEOF
5137 /* confdefs.h. */
5138 _ACEOF
5139 cat confdefs.h >>conftest.$ac_ext
5140 cat >>conftest.$ac_ext <<_ACEOF
5141 /* end confdefs.h. */
5142
5143 #include <sys/types.h>
5144 #include <sys/socket.h>
5145
5146 int
5147 main ()
5148 {
5149
5150 socklen_t a=0;
5151 getsockname(0,(struct sockaddr*)0, &a);
5152
5153 ;
5154 return 0;
5155 }
5156 _ACEOF
5157 rm -f conftest.$ac_objext
5158 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
5159 (eval $ac_compile) 2>conftest.er1
5160 ac_status=$?
5161 grep -v '^ *+' conftest.er1 >conftest.err
5162 rm -f conftest.er1
5163 cat conftest.err >&5
5164 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5165 (exit $ac_status); } &&
5166 { ac_try='test -z "$ac_c_werror_flag"
5167 || test ! -s conftest.err'
5168 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5169 (eval $ac_try) 2>&5
5170 ac_status=$?
5171 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5172 (exit $ac_status); }; } &&
5173 { ac_try='test -s conftest.$ac_objext'
5174 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5175 (eval $ac_try) 2>&5
5176 ac_status=$?
5177 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5178 (exit $ac_status); }; }; then
5179 ac_cv_socklen_t="socklen_t"
5180 else
5181 echo "$as_me: failed program was:" >&5
5182 sed 's/^/| /' conftest.$ac_ext >&5
5183
5184 cat >conftest.$ac_ext <<_ACEOF
5185 /* confdefs.h. */
5186 _ACEOF
5187 cat confdefs.h >>conftest.$ac_ext
5188 cat >>conftest.$ac_ext <<_ACEOF
5189 /* end confdefs.h. */
5190
5191 #include <sys/types.h>
5192 #include <sys/socket.h>
5193
5194 int
5195 main ()
5196 {
5197
5198 int a=0;
5199 getsockname(0,(struct sockaddr*)0, &a);
5200
5201 ;
5202 return 0;
5203 }
5204 _ACEOF
5205 rm -f conftest.$ac_objext
5206 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
5207 (eval $ac_compile) 2>conftest.er1
5208 ac_status=$?
5209 grep -v '^ *+' conftest.er1 >conftest.err
5210 rm -f conftest.er1
5211 cat conftest.err >&5
5212 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5213 (exit $ac_status); } &&
5214 { ac_try='test -z "$ac_c_werror_flag"
5215 || test ! -s conftest.err'
5216 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5217 (eval $ac_try) 2>&5
5218 ac_status=$?
5219 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5220 (exit $ac_status); }; } &&
5221 { ac_try='test -s conftest.$ac_objext'
5222 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5223 (eval $ac_try) 2>&5
5224 ac_status=$?
5225 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5226 (exit $ac_status); }; }; then
5227 ac_cv_socklen_t="int"
5228 else
5229 echo "$as_me: failed program was:" >&5
5230 sed 's/^/| /' conftest.$ac_ext >&5
5231
5232 ac_cv_socklen_t="size_t"
5233
5234 fi
5235 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
5236
5237 fi
5238 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
5239 echo "$as_me:$LINENO: result: $ac_cv_socklen_t" >&5
5240 echo "${ECHO_T}$ac_cv_socklen_t" >&6
5241 if test "$ac_cv_socklen_t" != "socklen_t"; then
5242
5243 cat >>confdefs.h <<_ACEOF
5244 #define socklen_t $ac_cv_socklen_t
5245 _ACEOF
5246
5247 fi
5248
5249
5250
5251 # Check whether --with-glib-prefix or --without-glib-prefix was given.
5252 if test "${with_glib_prefix+set}" = set; then
5253 withval="$with_glib_prefix"
5254 glib_config_prefix="$withval"
5255 else
5256 glib_config_prefix=""
5257 fi;
5258
5259 # Check whether --with-glib-exec-prefix or --without-glib-exec-prefix was given.
5260 if test "${with_glib_exec_prefix+set}" = set; then
5261 withval="$with_glib_exec_prefix"
5262 glib_config_exec_prefix="$withval"
5263 else
5264 glib_config_exec_prefix=""
5265 fi;
5266 # Check whether --enable-glibtest or --disable-glibtest was given.
5267 if test "${enable_glibtest+set}" = set; then
5268 enableval="$enable_glibtest"
5269
5270 else
5271 enable_glibtest=yes
5272 fi;
5273
5274 if test x$glib_config_exec_prefix != x ; then
5275 glib_config_args="$glib_config_args --exec-prefix=$glib_config_exec_prefix"
5276 if test x${GLIB_CONFIG+set} != xset ; then
5277 GLIB_CONFIG=$glib_config_exec_prefix/bin/glib-config
5278 fi
5279 fi
5280 if test x$glib_config_prefix != x ; then
5281 glib_config_args="$glib_config_args --prefix=$glib_config_prefix"
5282 if test x${GLIB_CONFIG+set} != xset ; then
5283 GLIB_CONFIG=$glib_config_prefix/bin/glib-config
5284 fi
5285 fi
5286
5287 for module in .
5288 do
5289 case "$module" in
5290 gmodule)
5291 glib_config_args="$glib_config_args gmodule"
5292 ;;
5293 gthread)
5294 glib_config_args="$glib_config_args gthread"
5295 ;;
5296 esac
5297 done
5298
5299 # Extract the first word of "glib-config", so it can be a program name with args.
5300 set dummy glib-config; ac_word=$2
5301 echo "$as_me:$LINENO: checking for $ac_word" >&5
5302 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
5303 if test "${ac_cv_path_GLIB_CONFIG+set}" = set; then
5304 echo $ECHO_N "(cached) $ECHO_C" >&6
5305 else
5306 case $GLIB_CONFIG in
5307 [\\/]* | ?:[\\/]*)
5308 ac_cv_path_GLIB_CONFIG="$GLIB_CONFIG" # Let the user override the test with a path.
5309 ;;
5310 *)
5311 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
5312 for as_dir in $PATH
5313 do
5314 IFS=$as_save_IFS
5315 test -z "$as_dir" && as_dir=.
5316 for ac_exec_ext in '' $ac_executable_extensions; do
5317 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
5318 ac_cv_path_GLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext"
5319 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
5320 break 2
5321 fi
5322 done
5323 done
5324
5325 test -z "$ac_cv_path_GLIB_CONFIG" && ac_cv_path_GLIB_CONFIG="no"
5326 ;;
5327 esac
5328 fi
5329 GLIB_CONFIG=$ac_cv_path_GLIB_CONFIG
5330
5331 if test -n "$GLIB_CONFIG"; then
5332 echo "$as_me:$LINENO: result: $GLIB_CONFIG" >&5
5333 echo "${ECHO_T}$GLIB_CONFIG" >&6
5334 else
5335 echo "$as_me:$LINENO: result: no" >&5
5336 echo "${ECHO_T}no" >&6
5337 fi
5338
5339 min_glib_version=1.2.0
5340 echo "$as_me:$LINENO: checking for GLIB - version >= $min_glib_version" >&5
5341 echo $ECHO_N "checking for GLIB - version >= $min_glib_version... $ECHO_C" >&6
5342 no_glib=""
5343 if test "$GLIB_CONFIG" = "no" ; then
5344 no_glib=yes
5345 else
5346 GLIB_CFLAGS=`$GLIB_CONFIG $glib_config_args --cflags`
5347 GLIB_LIBS=`$GLIB_CONFIG $glib_config_args --libs`
5348 glib_config_major_version=`$GLIB_CONFIG $glib_config_args --version | \
5349 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
5350 glib_config_minor_version=`$GLIB_CONFIG $glib_config_args --version | \
5351 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
5352 glib_config_micro_version=`$GLIB_CONFIG $glib_config_args --version | \
5353 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
5354 if test "x$enable_glibtest" = "xyes" ; then
5355 ac_save_CFLAGS="$CFLAGS"
5356 ac_save_LIBS="$LIBS"
5357 CFLAGS="$CFLAGS $GLIB_CFLAGS"
5358 LIBS="$GLIB_LIBS $LIBS"
5359 rm -f conf.glibtest
5360 if test "$cross_compiling" = yes; then
5361 echo $ac_n "cross compiling; assumed OK... $ac_c"
5362 else
5363 cat >conftest.$ac_ext <<_ACEOF
5364 /* confdefs.h. */
5365 _ACEOF
5366 cat confdefs.h >>conftest.$ac_ext
5367 cat >>conftest.$ac_ext <<_ACEOF
5368 /* end confdefs.h. */
5369
5370 #include <glib.h>
5371 #include <stdio.h>
5372 #include <stdlib.h>
5373
5374 int
5375 main ()
5376 {
5377 int major, minor, micro;
5378 char *tmp_version;
5379
5380 system ("touch conf.glibtest");
5381
5382 /* HP/UX 9 (%@#!) writes to sscanf strings */
5383 tmp_version = g_strdup("$min_glib_version");
5384 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
5385 printf("%s, bad version string\n", "$min_glib_version");
5386 exit(1);
5387 }
5388
5389 if ((glib_major_version != $glib_config_major_version) ||
5390 (glib_minor_version != $glib_config_minor_version) ||
5391 (glib_micro_version != $glib_config_micro_version))
5392 {
5393 printf("\n*** 'glib-config --version' returned %d.%d.%d, but GLIB (%d.%d.%d)\n",
5394 $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
5395 glib_major_version, glib_minor_version, glib_micro_version);
5396 printf ("*** was found! If glib-config was correct, then it is best\n");
5397 printf ("*** to remove the old version of GLIB. You may also be able to fix the error\n");
5398 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
5399 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
5400 printf("*** required on your system.\n");
5401 printf("*** If glib-config was wrong, set the environment variable GLIB_CONFIG\n");
5402 printf("*** to point to the correct copy of glib-config, and remove the file config.cache\n");
5403 printf("*** before re-running configure\n");
5404 }
5405 else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
5406 (glib_minor_version != GLIB_MINOR_VERSION) ||
5407 (glib_micro_version != GLIB_MICRO_VERSION))
5408 {
5409 printf("*** GLIB header files (version %d.%d.%d) do not match\n",
5410 GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
5411 printf("*** library (version %d.%d.%d)\n",
5412 glib_major_version, glib_minor_version, glib_micro_version);
5413 }
5414 else
5415 {
5416 if ((glib_major_version > major) ||
5417 ((glib_major_version == major) && (glib_minor_version > minor)) ||
5418 ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
5419 {
5420 return 0;
5421 }
5422 else
5423 {
5424 printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n",
5425 glib_major_version, glib_minor_version, glib_micro_version);
5426 printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n",
5427 major, minor, micro);
5428 printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
5429 printf("***\n");
5430 printf("*** If you have already installed a sufficiently new version, this error\n");
5431 printf("*** probably means that the wrong copy of the glib-config shell script is\n");
5432 printf("*** being found. The easiest way to fix this is to remove the old version\n");
5433 printf("*** of GLIB, but you can also set the GLIB_CONFIG environment to point to the\n");
5434 printf("*** correct copy of glib-config. (In this case, you will have to\n");
5435 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
5436 printf("*** so that the correct libraries are found at run-time))\n");
5437 }
5438 }
5439 return 1;
5440 }
5441
5442 _ACEOF
5443 rm -f conftest$ac_exeext
5444 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5445 (eval $ac_link) 2>&5
5446 ac_status=$?
5447 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5448 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
5449 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5450 (eval $ac_try) 2>&5
5451 ac_status=$?
5452 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5453 (exit $ac_status); }; }; then
5454 :
5455 else
5456 echo "$as_me: program exited with status $ac_status" >&5
5457 echo "$as_me: failed program was:" >&5
5458 sed 's/^/| /' conftest.$ac_ext >&5
5459
5460 ( exit $ac_status )
5461 no_glib=yes
5462 fi
5463 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
5464 fi
5465 CFLAGS="$ac_save_CFLAGS"
5466 LIBS="$ac_save_LIBS"
5467 fi
5468 fi
5469 if test "x$no_glib" = x ; then
5470 echo "$as_me:$LINENO: result: yes" >&5
5471 echo "${ECHO_T}yes" >&6
5472 :
5473 else
5474 echo "$as_me:$LINENO: result: no" >&5
5475 echo "${ECHO_T}no" >&6
5476 if test "$GLIB_CONFIG" = "no" ; then
5477 echo "*** The glib-config script installed by GLIB could not be found"
5478 echo "*** If GLIB was installed in PREFIX, make sure PREFIX/bin is in"
5479 echo "*** your path, or set the GLIB_CONFIG environment variable to the"
5480 echo "*** full path to glib-config."
5481 else
5482 if test -f conf.glibtest ; then
5483 :
5484 else
5485 echo "*** Could not run GLIB test program, checking why..."
5486 CFLAGS="$CFLAGS $GLIB_CFLAGS"
5487 LIBS="$LIBS $GLIB_LIBS"
5488 cat >conftest.$ac_ext <<_ACEOF
5489 /* confdefs.h. */
5490 _ACEOF
5491 cat confdefs.h >>conftest.$ac_ext
5492 cat >>conftest.$ac_ext <<_ACEOF
5493 /* end confdefs.h. */
5494
5495 #include <glib.h>
5496 #include <stdio.h>
5497
5498 int
5499 main ()
5500 {
5501 return ((glib_major_version) || (glib_minor_version) || (glib_micro_version));
5502 ;
5503 return 0;
5504 }
5505 _ACEOF
5506 rm -f conftest.$ac_objext conftest$ac_exeext
5507 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5508 (eval $ac_link) 2>conftest.er1
5509 ac_status=$?
5510 grep -v '^ *+' conftest.er1 >conftest.err
5511 rm -f conftest.er1
5512 cat conftest.err >&5
5513 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5514 (exit $ac_status); } &&
5515 { ac_try='test -z "$ac_c_werror_flag"
5516 || test ! -s conftest.err'
5517 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5518 (eval $ac_try) 2>&5
5519 ac_status=$?
5520 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5521 (exit $ac_status); }; } &&
5522 { ac_try='test -s conftest$ac_exeext'
5523 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5524 (eval $ac_try) 2>&5
5525 ac_status=$?
5526 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5527 (exit $ac_status); }; }; then
5528 echo "*** The test program compiled, but did not run. This usually means"
5529 echo "*** that the run-time linker is not finding GLIB or finding the wrong"
5530 echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
5531 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
5532 echo "*** to the installed location Also, make sure you have run ldconfig if that"
5533 echo "*** is required on your system"
5534 echo "***"
5535 echo "*** If you have an old version installed, it is best to remove it, although"
5536 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
5537 echo "***"
5538 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
5539 echo "*** came with the system with the command"
5540 echo "***"
5541 echo "*** rpm --erase --nodeps gtk gtk-devel"
5542 else
5543 echo "$as_me: failed program was:" >&5
5544 sed 's/^/| /' conftest.$ac_ext >&5
5545
5546 echo "*** The test program failed to compile or link. See the file config.log for the"
5547 echo "*** exact error that occured. This usually means GLIB was incorrectly installed"
5548 echo "*** or that you have moved GLIB since it was installed. In the latter case, you"
5549 echo "*** may want to edit the glib-config script: $GLIB_CONFIG"
5550 fi
5551 rm -f conftest.err conftest.$ac_objext \
5552 conftest$ac_exeext conftest.$ac_ext
5553 CFLAGS="$ac_save_CFLAGS"
5554 LIBS="$ac_save_LIBS"
5555 fi
5556 fi
5557 GLIB_CFLAGS=""
5558 GLIB_LIBS=""
5559 { { echo "$as_me:$LINENO: error: Unable to find glib with a version >= 1.2.0. Dillo NEEDS glib" >&5
5560 echo "$as_me: error: Unable to find glib with a version >= 1.2.0. Dillo NEEDS glib" >&2;}
5561 { (exit 1); exit 1; }; }
5562 fi
5563
5564
5565 rm -f conf.glibtest
5566
5567
5568
5569
5570 # Check whether --with-gtk-prefix or --without-gtk-prefix was given.
5571 if test "${with_gtk_prefix+set}" = set; then
5572 withval="$with_gtk_prefix"
5573 gtk_config_prefix="$withval"
5574 else
5575 gtk_config_prefix=""
5576 fi;
5577
5578 # Check whether --with-gtk-exec-prefix or --without-gtk-exec-prefix was given.
5579 if test "${with_gtk_exec_prefix+set}" = set; then
5580 withval="$with_gtk_exec_prefix"
5581 gtk_config_exec_prefix="$withval"
5582 else
5583 gtk_config_exec_prefix=""
5584 fi;
5585 # Check whether --enable-gtktest or --disable-gtktest was given.
5586 if test "${enable_gtktest+set}" = set; then
5587 enableval="$enable_gtktest"
5588
5589 else
5590 enable_gtktest=yes
5591 fi;
5592
5593 for module in .
5594 do
5595 case "$module" in
5596 gthread)
5597 gtk_config_args="$gtk_config_args gthread"
5598 ;;
5599 esac
5600 done
5601
5602 if test x$gtk_config_exec_prefix != x ; then
5603 gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
5604 if test x${GTK_CONFIG+set} != xset ; then
5605 GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
5606 fi
5607 fi
5608 if test x$gtk_config_prefix != x ; then
5609 gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
5610 if test x${GTK_CONFIG+set} != xset ; then
5611 GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
5612 fi
5613 fi
5614
5615 # Extract the first word of "gtk-config", so it can be a program name with args.
5616 set dummy gtk-config; ac_word=$2
5617 echo "$as_me:$LINENO: checking for $ac_word" >&5
5618 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
5619 if test "${ac_cv_path_GTK_CONFIG+set}" = set; then
5620 echo $ECHO_N "(cached) $ECHO_C" >&6
5621 else
5622 case $GTK_CONFIG in
5623 [\\/]* | ?:[\\/]*)
5624 ac_cv_path_GTK_CONFIG="$GTK_CONFIG" # Let the user override the test with a path.
5625 ;;
5626 *)
5627 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
5628 for as_dir in $PATH
5629 do
5630 IFS=$as_save_IFS
5631 test -z "$as_dir" && as_dir=.
5632 for ac_exec_ext in '' $ac_executable_extensions; do
5633 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
5634 ac_cv_path_GTK_CONFIG="$as_dir/$ac_word$ac_exec_ext"
5635 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
5636 break 2
5637 fi
5638 done
5639 done
5640
5641 test -z "$ac_cv_path_GTK_CONFIG" && ac_cv_path_GTK_CONFIG="no"
5642 ;;
5643 esac
5644 fi
5645 GTK_CONFIG=$ac_cv_path_GTK_CONFIG
5646
5647 if test -n "$GTK_CONFIG"; then
5648 echo "$as_me:$LINENO: result: $GTK_CONFIG" >&5
5649 echo "${ECHO_T}$GTK_CONFIG" >&6
5650 else
5651 echo "$as_me:$LINENO: result: no" >&5
5652 echo "${ECHO_T}no" >&6
5653 fi
5654
5655 min_gtk_version=1.2.0
5656 echo "$as_me:$LINENO: checking for GTK - version >= $min_gtk_version" >&5
5657 echo $ECHO_N "checking for GTK - version >= $min_gtk_version... $ECHO_C" >&6
5658 no_gtk=""
5659 if test "$GTK_CONFIG" = "no" ; then
5660 no_gtk=yes
5661 else
5662 GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
5663 GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
5664 gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
5665 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
5666 gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
5667 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
5668 gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
5669 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
5670 if test "x$enable_gtktest" = "xyes" ; then
5671 ac_save_CFLAGS="$CFLAGS"
5672 ac_save_LIBS="$LIBS"
5673 CFLAGS="$CFLAGS $GTK_CFLAGS"
5674 LIBS="$GTK_LIBS $LIBS"
5675 rm -f conf.gtktest
5676 if test "$cross_compiling" = yes; then
5677 echo $ac_n "cross compiling; assumed OK... $ac_c"
5678 else
5679 cat >conftest.$ac_ext <<_ACEOF
5680 /* confdefs.h. */
5681 _ACEOF
5682 cat confdefs.h >>conftest.$ac_ext
5683 cat >>conftest.$ac_ext <<_ACEOF
5684 /* end confdefs.h. */
5685
5686 #include <gtk/gtk.h>
5687 #include <stdio.h>
5688 #include <stdlib.h>
5689
5690 int
5691 main ()
5692 {
5693 int major, minor, micro;
5694 char *tmp_version;
5695
5696 system ("touch conf.gtktest");
5697
5698 /* HP/UX 9 (%@#!) writes to sscanf strings */
5699 tmp_version = g_strdup("$min_gtk_version");
5700 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
5701 printf("%s, bad version string\n", "$min_gtk_version");
5702 exit(1);
5703 }
5704
5705 if ((gtk_major_version != $gtk_config_major_version) ||
5706 (gtk_minor_version != $gtk_config_minor_version) ||
5707 (gtk_micro_version != $gtk_config_micro_version))
5708 {
5709 printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
5710 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
5711 gtk_major_version, gtk_minor_version, gtk_micro_version);
5712 printf ("*** was found! If gtk-config was correct, then it is best\n");
5713 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
5714 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
5715 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
5716 printf("*** required on your system.\n");
5717 printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
5718 printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
5719 printf("*** before re-running configure\n");
5720 }
5721 #if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
5722 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
5723 (gtk_minor_version != GTK_MINOR_VERSION) ||
5724 (gtk_micro_version != GTK_MICRO_VERSION))
5725 {
5726 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
5727 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
5728 printf("*** library (version %d.%d.%d)\n",
5729 gtk_major_version, gtk_minor_version, gtk_micro_version);
5730 }
5731 #endif /* defined (GTK_MAJOR_VERSION) ... */
5732 else
5733 {
5734 if ((gtk_major_version > major) ||
5735 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
5736 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
5737 {
5738 return 0;
5739 }
5740 else
5741 {
5742 printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
5743 gtk_major_version, gtk_minor_version, gtk_micro_version);
5744 printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
5745 major, minor, micro);
5746 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
5747 printf("***\n");
5748 printf("*** If you have already installed a sufficiently new version, this error\n");
5749 printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
5750 printf("*** being found. The easiest way to fix this is to remove the old version\n");
5751 printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
5752 printf("*** correct copy of gtk-config. (In this case, you will have to\n");
5753 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
5754 printf("*** so that the correct libraries are found at run-time))\n");
5755 }
5756 }
5757 return 1;
5758 }
5759
5760 _ACEOF
5761 rm -f conftest$ac_exeext
5762 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5763 (eval $ac_link) 2>&5
5764 ac_status=$?
5765 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5766 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
5767 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5768 (eval $ac_try) 2>&5
5769 ac_status=$?
5770 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5771 (exit $ac_status); }; }; then
5772 :
5773 else
5774 echo "$as_me: program exited with status $ac_status" >&5
5775 echo "$as_me: failed program was:" >&5
5776 sed 's/^/| /' conftest.$ac_ext >&5
5777
5778 ( exit $ac_status )
5779 no_gtk=yes
5780 fi
5781 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
5782 fi
5783 CFLAGS="$ac_save_CFLAGS"
5784 LIBS="$ac_save_LIBS"
5785 fi
5786 fi
5787 if test "x$no_gtk" = x ; then
5788 echo "$as_me:$LINENO: result: yes" >&5
5789 echo "${ECHO_T}yes" >&6
5790 :
5791 else
5792 echo "$as_me:$LINENO: result: no" >&5
5793 echo "${ECHO_T}no" >&6
5794 if test "$GTK_CONFIG" = "no" ; then
5795 echo "*** The gtk-config script installed by GTK could not be found"
5796 echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
5797 echo "*** your path, or set the GTK_CONFIG environment variable to the"
5798 echo "*** full path to gtk-config."
5799 else
5800 if test -f conf.gtktest ; then
5801 :
5802 else
5803 echo "*** Could not run GTK test program, checking why..."
5804 CFLAGS="$CFLAGS $GTK_CFLAGS"
5805 LIBS="$LIBS $GTK_LIBS"
5806 cat >conftest.$ac_ext <<_ACEOF
5807 /* confdefs.h. */
5808 _ACEOF
5809 cat confdefs.h >>conftest.$ac_ext
5810 cat >>conftest.$ac_ext <<_ACEOF
5811 /* end confdefs.h. */
5812
5813 #include <gtk/gtk.h>
5814 #include <stdio.h>
5815
5816 int
5817 main ()
5818 {
5819 return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version));
5820 ;
5821 return 0;
5822 }
5823 _ACEOF
5824 rm -f conftest.$ac_objext conftest$ac_exeext
5825 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5826 (eval $ac_link) 2>conftest.er1
5827 ac_status=$?
5828 grep -v '^ *+' conftest.er1 >conftest.err
5829 rm -f conftest.er1
5830 cat conftest.err >&5
5831 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5832 (exit $ac_status); } &&
5833 { ac_try='test -z "$ac_c_werror_flag"
5834 || test ! -s conftest.err'
5835 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5836 (eval $ac_try) 2>&5
5837 ac_status=$?
5838 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5839 (exit $ac_status); }; } &&
5840 { ac_try='test -s conftest$ac_exeext'
5841 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5842 (eval $ac_try) 2>&5
5843 ac_status=$?
5844 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5845 (exit $ac_status); }; }; then
5846 echo "*** The test program compiled, but did not run. This usually means"
5847 echo "*** that the run-time linker is not finding GTK or finding the wrong"
5848 echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
5849 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
5850 echo "*** to the installed location Also, make sure you have run ldconfig if that"
5851 echo "*** is required on your system"
5852 echo "***"
5853 echo "*** If you have an old version installed, it is best to remove it, although"
5854 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
5855 echo "***"
5856 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
5857 echo "*** came with the system with the command"
5858 echo "***"
5859 echo "*** rpm --erase --nodeps gtk gtk-devel"
5860 else
5861 echo "$as_me: failed program was:" >&5
5862 sed 's/^/| /' conftest.$ac_ext >&5
5863
5864 echo "*** The test program failed to compile or link. See the file config.log for the"
5865 echo "*** exact error that occured. This usually means GTK was incorrectly installed"
5866 echo "*** or that you have moved GTK since it was installed. In the latter case, you"
5867 echo "*** may want to edit the gtk-config script: $GTK_CONFIG"
5868 fi
5869 rm -f conftest.err conftest.$ac_objext \
5870 conftest$ac_exeext conftest.$ac_ext
5871 CFLAGS="$ac_save_CFLAGS"
5872 LIBS="$ac_save_LIBS"
5873 fi
5874 fi
5875 GTK_CFLAGS=""
5876 GTK_LIBS=""
5877 { { echo "$as_me:$LINENO: error: Unable to find Gtk+ with a version >= 1.2.0. Dillo NEEDS Gtk+" >&5
5878 echo "$as_me: error: Unable to find Gtk+ with a version >= 1.2.0. Dillo NEEDS Gtk+" >&2;}
5879 { (exit 1); exit 1; }; }
5880 fi
5881
5882
5883 rm -f conf.gtktest
5884
5885
5886 echo "$as_me:$LINENO: checking Hackish check for FLTK" >&5
5887 echo $ECHO_N "checking Hackish check for FLTK... $ECHO_C" >&6
5888 LIBFLTK_CXXFLAGS=`fltk-config --cxxflags`
5889 LIBFLTK_LIBS=`fltk-config --ldflags`
5890
5891
5892 if test "x$enable_jpeg" = "xyes"; then
5893
5894 echo "$as_me:$LINENO: checking for egrep" >&5
5895 echo $ECHO_N "checking for egrep... $ECHO_C" >&6
5896 if test "${ac_cv_prog_egrep+set}" = set; then
5897 echo $ECHO_N "(cached) $ECHO_C" >&6
5898 else
5899 if echo a | (grep -E '(a|b)') >/dev/null 2>&1
5900 then ac_cv_prog_egrep='grep -E'
5901 else ac_cv_prog_egrep='egrep'
5902 fi
5903 fi
5904 echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
5905 echo "${ECHO_T}$ac_cv_prog_egrep" >&6
5906 EGREP=$ac_cv_prog_egrep
5907
5908
5909 echo "$as_me:$LINENO: checking for ANSI C header files" >&5
5910 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
5911 if test "${ac_cv_header_stdc+set}" = set; then
5912 echo $ECHO_N "(cached) $ECHO_C" >&6
5913 else
5914 cat >conftest.$ac_ext <<_ACEOF
5915 /* confdefs.h. */
5916 _ACEOF
5917 cat confdefs.h >>conftest.$ac_ext
5918 cat >>conftest.$ac_ext <<_ACEOF
5919 /* end confdefs.h. */
5920 #include <stdlib.h>
5921 #include <stdarg.h>
5922 #include <string.h>
5923 #include <float.h>
5924
5925 int
5926 main ()
5927 {
5928
5929 ;
5930 return 0;
5931 }
5932 _ACEOF
5933 rm -f conftest.$ac_objext
5934 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
5935 (eval $ac_compile) 2>conftest.er1
5936 ac_status=$?
5937 grep -v '^ *+' conftest.er1 >conftest.err
5938 rm -f conftest.er1
5939 cat conftest.err >&5
5940 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5941 (exit $ac_status); } &&
5942 { ac_try='test -z "$ac_c_werror_flag"
5943 || test ! -s conftest.err'
5944 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5945 (eval $ac_try) 2>&5
5946 ac_status=$?
5947 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5948 (exit $ac_status); }; } &&
5949 { ac_try='test -s conftest.$ac_objext'
5950 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5951 (eval $ac_try) 2>&5
5952 ac_status=$?
5953 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5954 (exit $ac_status); }; }; then
5955 ac_cv_header_stdc=yes
5956 else
5957 echo "$as_me: failed program was:" >&5
5958 sed 's/^/| /' conftest.$ac_ext >&5
5959
5960 ac_cv_header_stdc=no
5961 fi
5962 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
5963
5964 if test $ac_cv_header_stdc = yes; then
5965 # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
5966 cat >conftest.$ac_ext <<_ACEOF
5967 /* confdefs.h. */
5968 _ACEOF
5969 cat confdefs.h >>conftest.$ac_ext
5970 cat >>conftest.$ac_ext <<_ACEOF
5971 /* end confdefs.h. */
5972 #include <string.h>
5973
5974 _ACEOF
5975 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
5976 $EGREP "memchr" >/dev/null 2>&1; then
5977 :
5978 else
5979 ac_cv_header_stdc=no
5980 fi
5981 rm -f conftest*
5982
5983 fi
5984
5985 if test $ac_cv_header_stdc = yes; then
5986 # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
5987 cat >conftest.$ac_ext <<_ACEOF
5988 /* confdefs.h. */
5989 _ACEOF
5990 cat confdefs.h >>conftest.$ac_ext
5991 cat >>conftest.$ac_ext <<_ACEOF
5992 /* end confdefs.h. */
5993 #include <stdlib.h>
5994
5995 _ACEOF
5996 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
5997 $EGREP "free" >/dev/null 2>&1; then
5998 :
5999 else
6000 ac_cv_header_stdc=no
6001 fi
6002 rm -f conftest*
6003
6004 fi
6005
6006 if test $ac_cv_header_stdc = yes; then
6007 # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
6008 if test "$cross_compiling" = yes; then
6009 :
6010 else
6011 cat >conftest.$ac_ext <<_ACEOF
6012 /* confdefs.h. */
6013 _ACEOF
6014 cat confdefs.h >>conftest.$ac_ext
6015 cat >>conftest.$ac_ext <<_ACEOF
6016 /* end confdefs.h. */
6017 #include <ctype.h>
6018 #if ((' ' & 0x0FF) == 0x020)
6019 # define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
6020 # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
6021 #else
6022 # define ISLOWER(c) \
6023 (('a' <= (c) && (c) <= 'i') \
6024 || ('j' <= (c) && (c) <= 'r') \
6025 || ('s' <= (c) && (c) <= 'z'))
6026 # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
6027 #endif
6028
6029 #define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
6030 int
6031 main ()
6032 {
6033 int i;
6034 for (i = 0; i < 256; i++)
6035 if (XOR (islower (i), ISLOWER (i))
6036 || toupper (i) != TOUPPER (i))
6037 exit(2);
6038 exit (0);
6039 }
6040 _ACEOF
6041 rm -f conftest$ac_exeext
6042 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6043 (eval $ac_link) 2>&5
6044 ac_status=$?
6045 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6046 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
6047 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6048 (eval $ac_try) 2>&5
6049 ac_status=$?
6050 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6051 (exit $ac_status); }; }; then
6052 :
6053 else
6054 echo "$as_me: program exited with status $ac_status" >&5
6055 echo "$as_me: failed program was:" >&5
6056 sed 's/^/| /' conftest.$ac_ext >&5
6057
6058 ( exit $ac_status )
6059 ac_cv_header_stdc=no
6060 fi
6061 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
6062 fi
6063 fi
6064 fi
6065 echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
6066 echo "${ECHO_T}$ac_cv_header_stdc" >&6
6067 if test $ac_cv_header_stdc = yes; then
6068
6069 cat >>confdefs.h <<\_ACEOF
6070 #define STDC_HEADERS 1
6071 _ACEOF
6072
6073 fi
6074
6075 # On IRIX 5.3, sys/types and inttypes.h are conflicting.
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085 for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
6086 inttypes.h stdint.h unistd.h
6087 do
6088 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
6089 echo "$as_me:$LINENO: checking for $ac_header" >&5
6090 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
6091 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6092 echo $ECHO_N "(cached) $ECHO_C" >&6
6093 else
6094 cat >conftest.$ac_ext <<_ACEOF
6095 /* confdefs.h. */
6096 _ACEOF
6097 cat confdefs.h >>conftest.$ac_ext
6098 cat >>conftest.$ac_ext <<_ACEOF
6099 /* end confdefs.h. */
6100 $ac_includes_default
6101
6102 #include <$ac_header>
6103 _ACEOF
6104 rm -f conftest.$ac_objext
6105 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6106 (eval $ac_compile) 2>conftest.er1
6107 ac_status=$?
6108 grep -v '^ *+' conftest.er1 >conftest.err
6109 rm -f conftest.er1
6110 cat conftest.err >&5
6111 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6112 (exit $ac_status); } &&
6113 { ac_try='test -z "$ac_c_werror_flag"
6114 || test ! -s conftest.err'
6115 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6116 (eval $ac_try) 2>&5
6117 ac_status=$?
6118 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6119 (exit $ac_status); }; } &&
6120 { ac_try='test -s conftest.$ac_objext'
6121 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6122 (eval $ac_try) 2>&5
6123 ac_status=$?
6124 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6125 (exit $ac_status); }; }; then
6126 eval "$as_ac_Header=yes"
6127 else
6128 echo "$as_me: failed program was:" >&5
6129 sed 's/^/| /' conftest.$ac_ext >&5
6130
6131 eval "$as_ac_Header=no"
6132 fi
6133 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6134 fi
6135 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
6136 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
6137 if test `eval echo '${'$as_ac_Header'}'` = yes; then
6138 cat >>confdefs.h <<_ACEOF
6139 #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
6140 _ACEOF
6141
6142 fi
6143
6144 done
6145
6146
6147 if test "${ac_cv_header_jpeglib_h+set}" = set; then
6148 echo "$as_me:$LINENO: checking for jpeglib.h" >&5
6149 echo $ECHO_N "checking for jpeglib.h... $ECHO_C" >&6
6150 if test "${ac_cv_header_jpeglib_h+set}" = set; then
6151 echo $ECHO_N "(cached) $ECHO_C" >&6
6152 fi
6153 echo "$as_me:$LINENO: result: $ac_cv_header_jpeglib_h" >&5
6154 echo "${ECHO_T}$ac_cv_header_jpeglib_h" >&6
6155 else
6156 # Is the header compilable?
6157 echo "$as_me:$LINENO: checking jpeglib.h usability" >&5
6158 echo $ECHO_N "checking jpeglib.h usability... $ECHO_C" >&6
6159 cat >conftest.$ac_ext <<_ACEOF
6160 /* confdefs.h. */
6161 _ACEOF
6162 cat confdefs.h >>conftest.$ac_ext
6163 cat >>conftest.$ac_ext <<_ACEOF
6164 /* end confdefs.h. */
6165 $ac_includes_default
6166 #include <jpeglib.h>
6167 _ACEOF
6168 rm -f conftest.$ac_objext
6169 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6170 (eval $ac_compile) 2>conftest.er1
6171 ac_status=$?
6172 grep -v '^ *+' conftest.er1 >conftest.err
6173 rm -f conftest.er1
6174 cat conftest.err >&5
6175 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6176 (exit $ac_status); } &&
6177 { ac_try='test -z "$ac_c_werror_flag"
6178 || test ! -s conftest.err'
6179 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6180 (eval $ac_try) 2>&5
6181 ac_status=$?
6182 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6183 (exit $ac_status); }; } &&
6184 { ac_try='test -s conftest.$ac_objext'
6185 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6186 (eval $ac_try) 2>&5
6187 ac_status=$?
6188 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6189 (exit $ac_status); }; }; then
6190 ac_header_compiler=yes
6191 else
6192 echo "$as_me: failed program was:" >&5
6193 sed 's/^/| /' conftest.$ac_ext >&5
6194
6195 ac_header_compiler=no
6196 fi
6197 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6198 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6199 echo "${ECHO_T}$ac_header_compiler" >&6
6200
6201 # Is the header present?
6202 echo "$as_me:$LINENO: checking jpeglib.h presence" >&5
6203 echo $ECHO_N "checking jpeglib.h presence... $ECHO_C" >&6
6204 cat >conftest.$ac_ext <<_ACEOF
6205 /* confdefs.h. */
6206 _ACEOF
6207 cat confdefs.h >>conftest.$ac_ext
6208 cat >>conftest.$ac_ext <<_ACEOF
6209 /* end confdefs.h. */
6210 #include <jpeglib.h>
6211 _ACEOF
6212 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6213 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6214 ac_status=$?
6215 grep -v '^ *+' conftest.er1 >conftest.err
6216 rm -f conftest.er1
6217 cat conftest.err >&5
6218 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6219 (exit $ac_status); } >/dev/null; then
6220 if test -s conftest.err; then
6221 ac_cpp_err=$ac_c_preproc_warn_flag
6222 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6223 else
6224 ac_cpp_err=
6225 fi
6226 else
6227 ac_cpp_err=yes
6228 fi
6229 if test -z "$ac_cpp_err"; then
6230 ac_header_preproc=yes
6231 else
6232 echo "$as_me: failed program was:" >&5
6233 sed 's/^/| /' conftest.$ac_ext >&5
6234
6235 ac_header_preproc=no
6236 fi
6237 rm -f conftest.err conftest.$ac_ext
6238 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
6239 echo "${ECHO_T}$ac_header_preproc" >&6
6240
6241 # So? What about this header?
6242 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
6243 yes:no: )
6244 { echo "$as_me:$LINENO: WARNING: jpeglib.h: accepted by the compiler, rejected by the preprocessor!" >&5
6245 echo "$as_me: WARNING: jpeglib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
6246 { echo "$as_me:$LINENO: WARNING: jpeglib.h: proceeding with the compiler's result" >&5
6247 echo "$as_me: WARNING: jpeglib.h: proceeding with the compiler's result" >&2;}
6248 ac_header_preproc=yes
6249 ;;
6250 no:yes:* )
6251 { echo "$as_me:$LINENO: WARNING: jpeglib.h: present but cannot be compiled" >&5
6252 echo "$as_me: WARNING: jpeglib.h: present but cannot be compiled" >&2;}
6253 { echo "$as_me:$LINENO: WARNING: jpeglib.h: check for missing prerequisite headers?" >&5
6254 echo "$as_me: WARNING: jpeglib.h: check for missing prerequisite headers?" >&2;}
6255 { echo "$as_me:$LINENO: WARNING: jpeglib.h: see the Autoconf documentation" >&5
6256 echo "$as_me: WARNING: jpeglib.h: see the Autoconf documentation" >&2;}
6257 { echo "$as_me:$LINENO: WARNING: jpeglib.h: section \"Present But Cannot Be Compiled\"" >&5
6258 echo "$as_me: WARNING: jpeglib.h: section \"Present But Cannot Be Compiled\"" >&2;}
6259 { echo "$as_me:$LINENO: WARNING: jpeglib.h: proceeding with the preprocessor's result" >&5
6260 echo "$as_me: WARNING: jpeglib.h: proceeding with the preprocessor's result" >&2;}
6261 { echo "$as_me:$LINENO: WARNING: jpeglib.h: in the future, the compiler will take precedence" >&5
6262 echo "$as_me: WARNING: jpeglib.h: in the future, the compiler will take precedence" >&2;}
6263 (
6264 cat <<\_ASBOX
6265 ## ------------------------------------------ ##
6266 ## Report this to the AC_PACKAGE_NAME lists. ##
6267 ## ------------------------------------------ ##
6268 _ASBOX
6269 ) |
6270 sed "s/^/$as_me: WARNING: /" >&2
6271 ;;
6272 esac
6273 echo "$as_me:$LINENO: checking for jpeglib.h" >&5
6274 echo $ECHO_N "checking for jpeglib.h... $ECHO_C" >&6
6275 if test "${ac_cv_header_jpeglib_h+set}" = set; then
6276 echo $ECHO_N "(cached) $ECHO_C" >&6
6277 else
6278 ac_cv_header_jpeglib_h=$ac_header_preproc
6279 fi
6280 echo "$as_me:$LINENO: result: $ac_cv_header_jpeglib_h" >&5
6281 echo "${ECHO_T}$ac_cv_header_jpeglib_h" >&6
6282
6283 fi
6284 if test $ac_cv_header_jpeglib_h = yes; then
6285 jpeg_ok=yes
6286 else
6287 jpeg_ok=no
6288 fi
6289
6290
6291
6292 if test "x$jpeg_ok" = "xyes"; then
6293 old_libs="$LIBS"
6294 echo "$as_me:$LINENO: checking for jpeg_destroy_decompress in -ljpeg" >&5
6295 echo $ECHO_N "checking for jpeg_destroy_decompress in -ljpeg... $ECHO_C" >&6
6296 if test "${ac_cv_lib_jpeg_jpeg_destroy_decompress+set}" = set; then
6297 echo $ECHO_N "(cached) $ECHO_C" >&6
6298 else
6299 ac_check_lib_save_LIBS=$LIBS
6300 LIBS="-ljpeg $LIBS"
6301 cat >conftest.$ac_ext <<_ACEOF
6302 /* confdefs.h. */
6303 _ACEOF
6304 cat confdefs.h >>conftest.$ac_ext
6305 cat >>conftest.$ac_ext <<_ACEOF
6306 /* end confdefs.h. */
6307
6308 /* Override any gcc2 internal prototype to avoid an error. */
6309 #ifdef __cplusplus
6310 extern "C"
6311 #endif
6312 /* We use char because int might match the return type of a gcc2
6313 builtin and then its argument prototype would still apply. */
6314 char jpeg_destroy_decompress ();
6315 int
6316 main ()
6317 {
6318 jpeg_destroy_decompress ();
6319 ;
6320 return 0;
6321 }
6322 _ACEOF
6323 rm -f conftest.$ac_objext conftest$ac_exeext
6324 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6325 (eval $ac_link) 2>conftest.er1
6326 ac_status=$?
6327 grep -v '^ *+' conftest.er1 >conftest.err
6328 rm -f conftest.er1
6329 cat conftest.err >&5
6330 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6331 (exit $ac_status); } &&
6332 { ac_try='test -z "$ac_c_werror_flag"
6333 || test ! -s conftest.err'
6334 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6335 (eval $ac_try) 2>&5
6336 ac_status=$?
6337 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6338 (exit $ac_status); }; } &&
6339 { ac_try='test -s conftest$ac_exeext'
6340 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6341 (eval $ac_try) 2>&5
6342 ac_status=$?
6343 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6344 (exit $ac_status); }; }; then
6345 ac_cv_lib_jpeg_jpeg_destroy_decompress=yes
6346 else
6347 echo "$as_me: failed program was:" >&5
6348 sed 's/^/| /' conftest.$ac_ext >&5
6349
6350 ac_cv_lib_jpeg_jpeg_destroy_decompress=no
6351 fi
6352 rm -f conftest.err conftest.$ac_objext \
6353 conftest$ac_exeext conftest.$ac_ext
6354 LIBS=$ac_check_lib_save_LIBS
6355 fi
6356 echo "$as_me:$LINENO: result: $ac_cv_lib_jpeg_jpeg_destroy_decompress" >&5
6357 echo "${ECHO_T}$ac_cv_lib_jpeg_jpeg_destroy_decompress" >&6
6358 if test $ac_cv_lib_jpeg_jpeg_destroy_decompress = yes; then
6359 jpeg_ok=yes
6360 else
6361 jpeg_ok=no
6362 fi
6363
6364 LIBS="$old_libs"
6365 fi
6366
6367 if test "x$jpeg_ok" = "xyes"; then
6368 LIBJPEG_LIBS="-ljpeg"
6369 if test -n "$LIBJPEG_LIBDIR"; then
6370 LIBJPEG_LDFLAGS="-L$LIBJPEG_LIBDIR"
6371 fi
6372 if test -n "$LIBJPEG_INCDIR"; then
6373 LIBJPEG_CPPFLAGS="-I$LIBJPEG_INCDIR"
6374 fi
6375 else
6376 { echo "$as_me:$LINENO: WARNING: *** No libjpeg found. Disabling jpeg images.***" >&5
6377 echo "$as_me: WARNING: *** No libjpeg found. Disabling jpeg images.***" >&2;}
6378 fi
6379 fi
6380
6381 if test "x$jpeg_ok" = "xyes"; then
6382
6383 cat >>confdefs.h <<\_ACEOF
6384 #define ENABLE_JPEG
6385 _ACEOF
6386
6387 fi
6388
6389 if test "x$enable_png" = "xyes"; then
6390 if test "${ac_cv_header_zlib_h+set}" = set; then
6391 echo "$as_me:$LINENO: checking for zlib.h" >&5
6392 echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6
6393 if test "${ac_cv_header_zlib_h+set}" = set; then
6394 echo $ECHO_N "(cached) $ECHO_C" >&6
6395 fi
6396 echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
6397 echo "${ECHO_T}$ac_cv_header_zlib_h" >&6
6398 else
6399 # Is the header compilable?
6400 echo "$as_me:$LINENO: checking zlib.h usability" >&5
6401 echo $ECHO_N "checking zlib.h usability... $ECHO_C" >&6
6402 cat >conftest.$ac_ext <<_ACEOF
6403 /* confdefs.h. */
6404 _ACEOF
6405 cat confdefs.h >>conftest.$ac_ext
6406 cat >>conftest.$ac_ext <<_ACEOF
6407 /* end confdefs.h. */
6408 $ac_includes_default
6409 #include <zlib.h>
6410 _ACEOF
6411 rm -f conftest.$ac_objext
6412 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6413 (eval $ac_compile) 2>conftest.er1
6414 ac_status=$?
6415 grep -v '^ *+' conftest.er1 >conftest.err
6416 rm -f conftest.er1
6417 cat conftest.err >&5
6418 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6419 (exit $ac_status); } &&
6420 { ac_try='test -z "$ac_c_werror_flag"
6421 || test ! -s conftest.err'
6422 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6423 (eval $ac_try) 2>&5
6424 ac_status=$?
6425 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6426 (exit $ac_status); }; } &&
6427 { ac_try='test -s conftest.$ac_objext'
6428 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6429 (eval $ac_try) 2>&5
6430 ac_status=$?
6431 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6432 (exit $ac_status); }; }; then
6433 ac_header_compiler=yes
6434 else
6435 echo "$as_me: failed program was:" >&5
6436 sed 's/^/| /' conftest.$ac_ext >&5
6437
6438 ac_header_compiler=no
6439 fi
6440 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6441 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6442 echo "${ECHO_T}$ac_header_compiler" >&6
6443
6444 # Is the header present?
6445 echo "$as_me:$LINENO: checking zlib.h presence" >&5
6446 echo $ECHO_N "checking zlib.h presence... $ECHO_C" >&6
6447 cat >conftest.$ac_ext <<_ACEOF
6448 /* confdefs.h. */
6449 _ACEOF
6450 cat confdefs.h >>conftest.$ac_ext
6451 cat >>conftest.$ac_ext <<_ACEOF
6452 /* end confdefs.h. */
6453 #include <zlib.h>
6454 _ACEOF
6455 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6456 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6457 ac_status=$?
6458 grep -v '^ *+' conftest.er1 >conftest.err
6459 rm -f conftest.er1
6460 cat conftest.err >&5
6461 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6462 (exit $ac_status); } >/dev/null; then
6463 if test -s conftest.err; then
6464 ac_cpp_err=$ac_c_preproc_warn_flag
6465 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6466 else
6467 ac_cpp_err=
6468 fi
6469 else
6470 ac_cpp_err=yes
6471 fi
6472 if test -z "$ac_cpp_err"; then
6473 ac_header_preproc=yes
6474 else
6475 echo "$as_me: failed program was:" >&5
6476 sed 's/^/| /' conftest.$ac_ext >&5
6477
6478 ac_header_preproc=no
6479 fi
6480 rm -f conftest.err conftest.$ac_ext
6481 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
6482 echo "${ECHO_T}$ac_header_preproc" >&6
6483
6484 # So? What about this header?
6485 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
6486 yes:no: )
6487 { echo "$as_me:$LINENO: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
6488 echo "$as_me: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
6489 { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the compiler's result" >&5
6490 echo "$as_me: WARNING: zlib.h: proceeding with the compiler's result" >&2;}
6491 ac_header_preproc=yes
6492 ;;
6493 no:yes:* )
6494 { echo "$as_me:$LINENO: WARNING: zlib.h: present but cannot be compiled" >&5
6495 echo "$as_me: WARNING: zlib.h: present but cannot be compiled" >&2;}
6496 { echo "$as_me:$LINENO: WARNING: zlib.h: check for missing prerequisite headers?" >&5
6497 echo "$as_me: WARNING: zlib.h: check for missing prerequisite headers?" >&2;}
6498 { echo "$as_me:$LINENO: WARNING: zlib.h: see the Autoconf documentation" >&5
6499 echo "$as_me: WARNING: zlib.h: see the Autoconf documentation" >&2;}
6500 { echo "$as_me:$LINENO: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&5
6501 echo "$as_me: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
6502 { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the preprocessor's result" >&5
6503 echo "$as_me: WARNING: zlib.h: proceeding with the preprocessor's result" >&2;}
6504 { echo "$as_me:$LINENO: WARNING: zlib.h: in the future, the compiler will take precedence" >&5
6505 echo "$as_me: WARNING: zlib.h: in the future, the compiler will take precedence" >&2;}
6506 (
6507 cat <<\_ASBOX
6508 ## ------------------------------------------ ##
6509 ## Report this to the AC_PACKAGE_NAME lists. ##
6510 ## ------------------------------------------ ##
6511 _ASBOX
6512 ) |
6513 sed "s/^/$as_me: WARNING: /" >&2
6514 ;;
6515 esac
6516 echo "$as_me:$LINENO: checking for zlib.h" >&5
6517 echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6
6518 if test "${ac_cv_header_zlib_h+set}" = set; then
6519 echo $ECHO_N "(cached) $ECHO_C" >&6
6520 else
6521 ac_cv_header_zlib_h=$ac_header_preproc
6522 fi
6523 echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
6524 echo "${ECHO_T}$ac_cv_header_zlib_h" >&6
6525
6526 fi
6527 if test $ac_cv_header_zlib_h = yes; then
6528 libz_ok=yes
6529 else
6530 libz_ok=no
6531 fi
6532
6533
6534
6535 if test "x$libz_ok" = "xyes"; then
6536 old_libs="$LIBS"
6537 echo "$as_me:$LINENO: checking for zlibVersion in -lz" >&5
6538 echo $ECHO_N "checking for zlibVersion in -lz... $ECHO_C" >&6
6539 if test "${ac_cv_lib_z_zlibVersion+set}" = set; then
6540 echo $ECHO_N "(cached) $ECHO_C" >&6
6541 else
6542 ac_check_lib_save_LIBS=$LIBS
6543 LIBS="-lz $LIBS"
6544 cat >conftest.$ac_ext <<_ACEOF
6545 /* confdefs.h. */
6546 _ACEOF
6547 cat confdefs.h >>conftest.$ac_ext
6548 cat >>conftest.$ac_ext <<_ACEOF
6549 /* end confdefs.h. */
6550
6551 /* Override any gcc2 internal prototype to avoid an error. */
6552 #ifdef __cplusplus
6553 extern "C"
6554 #endif
6555 /* We use char because int might match the return type of a gcc2
6556 builtin and then its argument prototype would still apply. */
6557 char zlibVersion ();
6558 int
6559 main ()
6560 {
6561 zlibVersion ();
6562 ;
6563 return 0;
6564 }
6565 _ACEOF
6566 rm -f conftest.$ac_objext conftest$ac_exeext
6567 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6568 (eval $ac_link) 2>conftest.er1
6569 ac_status=$?
6570 grep -v '^ *+' conftest.er1 >conftest.err
6571 rm -f conftest.er1
6572 cat conftest.err >&5
6573 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6574 (exit $ac_status); } &&
6575 { ac_try='test -z "$ac_c_werror_flag"
6576 || test ! -s conftest.err'
6577 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6578 (eval $ac_try) 2>&5
6579 ac_status=$?
6580 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6581 (exit $ac_status); }; } &&
6582 { ac_try='test -s conftest$ac_exeext'
6583 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6584 (eval $ac_try) 2>&5
6585 ac_status=$?
6586 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6587 (exit $ac_status); }; }; then
6588 ac_cv_lib_z_zlibVersion=yes
6589 else
6590 echo "$as_me: failed program was:" >&5
6591 sed 's/^/| /' conftest.$ac_ext >&5
6592
6593 ac_cv_lib_z_zlibVersion=no
6594 fi
6595 rm -f conftest.err conftest.$ac_objext \
6596 conftest$ac_exeext conftest.$ac_ext
6597 LIBS=$ac_check_lib_save_LIBS
6598 fi
6599 echo "$as_me:$LINENO: result: $ac_cv_lib_z_zlibVersion" >&5
6600 echo "${ECHO_T}$ac_cv_lib_z_zlibVersion" >&6
6601 if test $ac_cv_lib_z_zlibVersion = yes; then
6602 libz_ok=yes
6603 else
6604 libz_ok=no
6605 fi
6606
6607 LIBS="$old_libs"
6608 fi
6609
6610 if test "x$libz_ok" = xyes; then
6611 LIBZ_LIBS="-lz"
6612 else
6613 { echo "$as_me:$LINENO: WARNING: *** No libz found. Disabling PNG images ***" >&5
6614 echo "$as_me: WARNING: *** No libz found. Disabling PNG images ***" >&2;}
6615 fi
6616 fi
6617
6618 if test "x$enable_png" = "xyes" && test "x$libz_ok" = "xyes"; then
6619 echo "$as_me:$LINENO: checking for libpng-config" >&5
6620 echo $ECHO_N "checking for libpng-config... $ECHO_C" >&6
6621
6622 if test -z "$PNG_CONFIG"; then
6623 PNG_CONFIG=`which libpng12-config`
6624 if test -z "$PNG_CONFIG"; then
6625 PNG_CONFIG=`which libpng-config`
6626 fi
6627 if test -z "$PNG_CONFIG"; then
6628 PNG_CONFIG=`which libpng10-config`
6629 fi
6630 fi
6631
6632 if test -n "$PNG_CONFIG" && test -x "$PNG_CONFIG"; then
6633 echo "$as_me:$LINENO: result: $PNG_CONFIG" >&5
6634 echo "${ECHO_T}$PNG_CONFIG" >&6
6635 png_ok="yes"
6636 else
6637 echo "$as_me:$LINENO: result: missing" >&5
6638 echo "${ECHO_T}missing" >&6
6639 png_ok="no"
6640 fi
6641
6642 if test "x$png_ok" = "xyes"; then
6643 echo "$as_me:$LINENO: checking for libpng version" >&5
6644 echo $ECHO_N "checking for libpng version... $ECHO_C" >&6
6645 png_version=`$PNG_CONFIG --version`
6646 case $png_version in
6647 1.2.*) echo "$as_me:$LINENO: result: $png_version (newer version)" >&5
6648 echo "${ECHO_T}$png_version (newer version)" >&6 ;;
6649 1.0.*) echo "$as_me:$LINENO: result: $png_version (older version)" >&5
6650 echo "${ECHO_T}$png_version (older version)" >&6 ;;
6651 *) echo "$as_me:$LINENO: result: ERROR" >&5
6652 echo "${ECHO_T}ERROR" >&6 ;;
6653 esac
6654
6655 LIBPNG_CFLAGS=`$PNG_CONFIG --cflags`
6656 LIBPNG_LIBS=`$PNG_CONFIG --ldflags`
6657 case $png_version in
6658 1.2.4*) LIBPNG_LIBS="$LIBPNG_LIBS `$PNG_CONFIG --libs`" ;;
6659 esac
6660 else
6661
6662
6663 for ac_header in png.h libpng/png.h
6664 do
6665 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
6666 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6667 echo "$as_me:$LINENO: checking for $ac_header" >&5
6668 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
6669 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6670 echo $ECHO_N "(cached) $ECHO_C" >&6
6671 fi
6672 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
6673 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
6674 else
6675 # Is the header compilable?
6676 echo "$as_me:$LINENO: checking $ac_header usability" >&5
6677 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
6678 cat >conftest.$ac_ext <<_ACEOF
6679 /* confdefs.h. */
6680 _ACEOF
6681 cat confdefs.h >>conftest.$ac_ext
6682 cat >>conftest.$ac_ext <<_ACEOF
6683 /* end confdefs.h. */
6684 $ac_includes_default
6685 #include <$ac_header>
6686 _ACEOF
6687 rm -f conftest.$ac_objext
6688 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6689 (eval $ac_compile) 2>conftest.er1
6690 ac_status=$?
6691 grep -v '^ *+' conftest.er1 >conftest.err
6692 rm -f conftest.er1
6693 cat conftest.err >&5
6694 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6695 (exit $ac_status); } &&
6696 { ac_try='test -z "$ac_c_werror_flag"
6697 || test ! -s conftest.err'
6698 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6699 (eval $ac_try) 2>&5
6700 ac_status=$?
6701 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6702 (exit $ac_status); }; } &&
6703 { ac_try='test -s conftest.$ac_objext'
6704 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6705 (eval $ac_try) 2>&5
6706 ac_status=$?
6707 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6708 (exit $ac_status); }; }; then
6709 ac_header_compiler=yes
6710 else
6711 echo "$as_me: failed program was:" >&5
6712 sed 's/^/| /' conftest.$ac_ext >&5
6713
6714 ac_header_compiler=no
6715 fi
6716 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6717 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6718 echo "${ECHO_T}$ac_header_compiler" >&6
6719
6720 # Is the header present?
6721 echo "$as_me:$LINENO: checking $ac_header presence" >&5
6722 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
6723 cat >conftest.$ac_ext <<_ACEOF
6724 /* confdefs.h. */
6725 _ACEOF
6726 cat confdefs.h >>conftest.$ac_ext
6727 cat >>conftest.$ac_ext <<_ACEOF
6728 /* end confdefs.h. */
6729 #include <$ac_header>
6730 _ACEOF
6731 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6732 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6733 ac_status=$?
6734 grep -v '^ *+' conftest.er1 >conftest.err
6735 rm -f conftest.er1
6736 cat conftest.err >&5
6737 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6738 (exit $ac_status); } >/dev/null; then
6739 if test -s conftest.err; then
6740 ac_cpp_err=$ac_c_preproc_warn_flag
6741 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6742 else
6743 ac_cpp_err=
6744 fi
6745 else
6746 ac_cpp_err=yes
6747 fi
6748 if test -z "$ac_cpp_err"; then
6749 ac_header_preproc=yes
6750 else
6751 echo "$as_me: failed program was:" >&5
6752 sed 's/^/| /' conftest.$ac_ext >&5
6753
6754 ac_header_preproc=no
6755 fi
6756 rm -f conftest.err conftest.$ac_ext
6757 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
6758 echo "${ECHO_T}$ac_header_preproc" >&6
6759
6760 # So? What about this header?
6761 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
6762 yes:no: )
6763 { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
6764 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
6765 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
6766 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
6767 ac_header_preproc=yes
6768 ;;
6769 no:yes:* )
6770 { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
6771 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
6772 { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
6773 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
6774 { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
6775 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
6776 { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
6777 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
6778 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
6779 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
6780 { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
6781 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
6782 (
6783 cat <<\_ASBOX
6784 ## ------------------------------------------ ##
6785 ## Report this to the AC_PACKAGE_NAME lists. ##
6786 ## ------------------------------------------ ##
6787 _ASBOX
6788 ) |
6789 sed "s/^/$as_me: WARNING: /" >&2
6790 ;;
6791 esac
6792 echo "$as_me:$LINENO: checking for $ac_header" >&5
6793 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
6794 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6795 echo $ECHO_N "(cached) $ECHO_C" >&6
6796 else
6797 eval "$as_ac_Header=\$ac_header_preproc"
6798 fi
6799 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
6800 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
6801
6802 fi
6803 if test `eval echo '${'$as_ac_Header'}'` = yes; then
6804 cat >>confdefs.h <<_ACEOF
6805 #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
6806 _ACEOF
6807 png_ok=yes && break
6808 else
6809 png_ok=no
6810 fi
6811
6812 done
6813
6814
6815 if test "x$png_ok" = "xyes"; then
6816 old_libs="$LIBS"
6817 echo "$as_me:$LINENO: checking for png_check_sig in -lpng" >&5
6818 echo $ECHO_N "checking for png_check_sig in -lpng... $ECHO_C" >&6
6819 if test "${ac_cv_lib_png_png_check_sig+set}" = set; then
6820 echo $ECHO_N "(cached) $ECHO_C" >&6
6821 else
6822 ac_check_lib_save_LIBS=$LIBS
6823 LIBS="-lpng $LIBZ_LIBS -lm $LIBS"
6824 cat >conftest.$ac_ext <<_ACEOF
6825 /* confdefs.h. */
6826 _ACEOF
6827 cat confdefs.h >>conftest.$ac_ext
6828 cat >>conftest.$ac_ext <<_ACEOF
6829 /* end confdefs.h. */
6830
6831 /* Override any gcc2 internal prototype to avoid an error. */
6832 #ifdef __cplusplus
6833 extern "C"
6834 #endif
6835 /* We use char because int might match the return type of a gcc2
6836 builtin and then its argument prototype would still apply. */
6837 char png_check_sig ();
6838 int
6839 main ()
6840 {
6841 png_check_sig ();
6842 ;
6843 return 0;
6844 }
6845 _ACEOF
6846 rm -f conftest.$ac_objext conftest$ac_exeext
6847 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6848 (eval $ac_link) 2>conftest.er1
6849 ac_status=$?
6850 grep -v '^ *+' conftest.er1 >conftest.err
6851 rm -f conftest.er1
6852 cat conftest.err >&5
6853 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6854 (exit $ac_status); } &&
6855 { ac_try='test -z "$ac_c_werror_flag"
6856 || test ! -s conftest.err'
6857 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6858 (eval $ac_try) 2>&5
6859 ac_status=$?
6860 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6861 (exit $ac_status); }; } &&
6862 { ac_try='test -s conftest$ac_exeext'
6863 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6864 (eval $ac_try) 2>&5
6865 ac_status=$?
6866 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6867 (exit $ac_status); }; }; then
6868 ac_cv_lib_png_png_check_sig=yes
6869 else
6870 echo "$as_me: failed program was:" >&5
6871 sed 's/^/| /' conftest.$ac_ext >&5
6872
6873 ac_cv_lib_png_png_check_sig=no
6874 fi
6875 rm -f conftest.err conftest.$ac_objext \
6876 conftest$ac_exeext conftest.$ac_ext
6877 LIBS=$ac_check_lib_save_LIBS
6878 fi
6879 echo "$as_me:$LINENO: result: $ac_cv_lib_png_png_check_sig" >&5
6880 echo "${ECHO_T}$ac_cv_lib_png_png_check_sig" >&6
6881 if test $ac_cv_lib_png_png_check_sig = yes; then
6882 png_ok=yes
6883 else
6884 png_ok=no
6885 fi
6886
6887 LIBS="$old_libs"
6888
6889 if test "x$png_ok" = "xyes"; then
6890 LIBPNG_LIBS="-lpng -lm"
6891 fi
6892 fi
6893
6894 if test "x$png_ok" = "xno"; then
6895 { echo "$as_me:$LINENO: WARNING: *** No libpng found. Disabling PNG images ***" >&5
6896 echo "$as_me: WARNING: *** No libpng found. Disabling PNG images ***" >&2;}
6897 fi
6898 fi
6899 fi
6900
6901 if test "x$png_ok" = "xyes"; then
6902
6903 cat >>confdefs.h <<\_ACEOF
6904 #define ENABLE_PNG
6905 _ACEOF
6906
6907 fi
6908
6909 if test "x$enable_gif" = "xyes"; then
6910
6911 cat >>confdefs.h <<\_ACEOF
6912 #define ENABLE_GIF
6913 _ACEOF
6914
6915 fi
6916
6917 if test "x$enable_ssl" = "xyes"; then
6918 if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
6919 echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
6920 echo $ECHO_N "checking for openssl/ssl.h... $ECHO_C" >&6
6921 if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
6922 echo $ECHO_N "(cached) $ECHO_C" >&6
6923 fi
6924 echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ssl_h" >&5
6925 echo "${ECHO_T}$ac_cv_header_openssl_ssl_h" >&6
6926 else
6927 # Is the header compilable?
6928 echo "$as_me:$LINENO: checking openssl/ssl.h usability" >&5
6929 echo $ECHO_N "checking openssl/ssl.h usability... $ECHO_C" >&6
6930 cat >conftest.$ac_ext <<_ACEOF
6931 /* confdefs.h. */
6932 _ACEOF
6933 cat confdefs.h >>conftest.$ac_ext
6934 cat >>conftest.$ac_ext <<_ACEOF
6935 /* end confdefs.h. */
6936 $ac_includes_default
6937 #include <openssl/ssl.h>
6938 _ACEOF
6939 rm -f conftest.$ac_objext
6940 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6941 (eval $ac_compile) 2>conftest.er1
6942 ac_status=$?
6943 grep -v '^ *+' conftest.er1 >conftest.err
6944 rm -f conftest.er1
6945 cat conftest.err >&5
6946 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6947 (exit $ac_status); } &&
6948 { ac_try='test -z "$ac_c_werror_flag"
6949 || test ! -s conftest.err'
6950 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6951 (eval $ac_try) 2>&5
6952 ac_status=$?
6953 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6954 (exit $ac_status); }; } &&
6955 { ac_try='test -s conftest.$ac_objext'
6956 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6957 (eval $ac_try) 2>&5
6958 ac_status=$?
6959 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6960 (exit $ac_status); }; }; then
6961 ac_header_compiler=yes
6962 else
6963 echo "$as_me: failed program was:" >&5
6964 sed 's/^/| /' conftest.$ac_ext >&5
6965
6966 ac_header_compiler=no
6967 fi
6968 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6969 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6970 echo "${ECHO_T}$ac_header_compiler" >&6
6971
6972 # Is the header present?
6973 echo "$as_me:$LINENO: checking openssl/ssl.h presence" >&5
6974 echo $ECHO_N "checking openssl/ssl.h presence... $ECHO_C" >&6
6975 cat >conftest.$ac_ext <<_ACEOF
6976 /* confdefs.h. */
6977 _ACEOF
6978 cat confdefs.h >>conftest.$ac_ext
6979 cat >>conftest.$ac_ext <<_ACEOF
6980 /* end confdefs.h. */
6981 #include <openssl/ssl.h>
6982 _ACEOF
6983 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6984 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6985 ac_status=$?
6986 grep -v '^ *+' conftest.er1 >conftest.err
6987 rm -f conftest.er1
6988 cat conftest.err >&5
6989 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6990 (exit $ac_status); } >/dev/null; then
6991 if test -s conftest.err; then
6992 ac_cpp_err=$ac_c_preproc_warn_flag
6993 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6994 else
6995 ac_cpp_err=
6996 fi
6997 else
6998 ac_cpp_err=yes
6999 fi
7000 if test -z "$ac_cpp_err"; then
7001 ac_header_preproc=yes
7002 else
7003 echo "$as_me: failed program was:" >&5
7004 sed 's/^/| /' conftest.$ac_ext >&5
7005
7006 ac_header_preproc=no
7007 fi
7008 rm -f conftest.err conftest.$ac_ext
7009 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
7010 echo "${ECHO_T}$ac_header_preproc" >&6
7011
7012 # So? What about this header?
7013 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
7014 yes:no: )
7015 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!" >&5
7016 echo "$as_me: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
7017 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: proceeding with the compiler's result" >&5
7018 echo "$as_me: WARNING: openssl/ssl.h: proceeding with the compiler's result" >&2;}
7019 ac_header_preproc=yes
7020 ;;
7021 no:yes:* )
7022 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: present but cannot be compiled" >&5
7023 echo "$as_me: WARNING: openssl/ssl.h: present but cannot be compiled" >&2;}
7024 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: check for missing prerequisite headers?" >&5
7025 echo "$as_me: WARNING: openssl/ssl.h: check for missing prerequisite headers?" >&2;}
7026 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: see the Autoconf documentation" >&5
7027 echo "$as_me: WARNING: openssl/ssl.h: see the Autoconf documentation" >&2;}
7028 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: section \"Present But Cannot Be Compiled\"" >&5
7029 echo "$as_me: WARNING: openssl/ssl.h: section \"Present But Cannot Be Compiled\"" >&2;}
7030 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: proceeding with the preprocessor's result" >&5
7031 echo "$as_me: WARNING: openssl/ssl.h: proceeding with the preprocessor's result" >&2;}
7032 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: in the future, the compiler will take precedence" >&5
7033 echo "$as_me: WARNING: openssl/ssl.h: in the future, the compiler will take precedence" >&2;}
7034 (
7035 cat <<\_ASBOX
7036 ## ------------------------------------------ ##
7037 ## Report this to the AC_PACKAGE_NAME lists. ##
7038 ## ------------------------------------------ ##
7039 _ASBOX
7040 ) |
7041 sed "s/^/$as_me: WARNING: /" >&2
7042 ;;
7043 esac
7044 echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
7045 echo $ECHO_N "checking for openssl/ssl.h... $ECHO_C" >&6
7046 if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
7047 echo $ECHO_N "(cached) $ECHO_C" >&6
7048 else
7049 ac_cv_header_openssl_ssl_h=$ac_header_preproc
7050 fi
7051 echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ssl_h" >&5
7052 echo "${ECHO_T}$ac_cv_header_openssl_ssl_h" >&6
7053
7054 fi
7055 if test $ac_cv_header_openssl_ssl_h = yes; then
7056 ssl_ok=yes
7057 else
7058 ssl_ok=no
7059 fi
7060
7061
7062
7063 if test "x$ssl_ok" = "xyes"; then
7064 old_libs="$LIBS"
7065 echo "$as_me:$LINENO: checking for SSL_library_init in -lssl" >&5
7066 echo $ECHO_N "checking for SSL_library_init in -lssl... $ECHO_C" >&6
7067 if test "${ac_cv_lib_ssl_SSL_library_init+set}" = set; then
7068 echo $ECHO_N "(cached) $ECHO_C" >&6
7069 else
7070 ac_check_lib_save_LIBS=$LIBS
7071 LIBS="-lssl -lcrypto $LIBS"
7072 cat >conftest.$ac_ext <<_ACEOF
7073 /* confdefs.h. */
7074 _ACEOF
7075 cat confdefs.h >>conftest.$ac_ext
7076 cat >>conftest.$ac_ext <<_ACEOF
7077 /* end confdefs.h. */
7078
7079 /* Override any gcc2 internal prototype to avoid an error. */
7080 #ifdef __cplusplus
7081 extern "C"
7082 #endif
7083 /* We use char because int might match the return type of a gcc2
7084 builtin and then its argument prototype would still apply. */
7085 char SSL_library_init ();
7086 int
7087 main ()
7088 {
7089 SSL_library_init ();
7090 ;
7091 return 0;
7092 }
7093 _ACEOF
7094 rm -f conftest.$ac_objext conftest$ac_exeext
7095 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7096 (eval $ac_link) 2>conftest.er1
7097 ac_status=$?
7098 grep -v '^ *+' conftest.er1 >conftest.err
7099 rm -f conftest.er1
7100 cat conftest.err >&5
7101 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7102 (exit $ac_status); } &&
7103 { ac_try='test -z "$ac_c_werror_flag"
7104 || test ! -s conftest.err'
7105 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7106 (eval $ac_try) 2>&5
7107 ac_status=$?
7108 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7109 (exit $ac_status); }; } &&
7110 { ac_try='test -s conftest$ac_exeext'
7111 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7112 (eval $ac_try) 2>&5
7113 ac_status=$?
7114 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7115 (exit $ac_status); }; }; then
7116 ac_cv_lib_ssl_SSL_library_init=yes
7117 else
7118 echo "$as_me: failed program was:" >&5
7119 sed 's/^/| /' conftest.$ac_ext >&5
7120
7121 ac_cv_lib_ssl_SSL_library_init=no
7122 fi
7123 rm -f conftest.err conftest.$ac_objext \
7124 conftest$ac_exeext conftest.$ac_ext
7125 LIBS=$ac_check_lib_save_LIBS
7126 fi
7127 echo "$as_me:$LINENO: result: $ac_cv_lib_ssl_SSL_library_init" >&5
7128 echo "${ECHO_T}$ac_cv_lib_ssl_SSL_library_init" >&6
7129 if test $ac_cv_lib_ssl_SSL_library_init = yes; then
7130 ssl_ok=yes
7131 else
7132 ssl_ok=no
7133 fi
7134
7135 LIBS="$old_libs"
7136 fi
7137
7138 if test "x$ssl_ok" = "xyes"; then
7139 LIBSSL_LIBS="-lcrypto -lssl"
7140 else
7141 { echo "$as_me:$LINENO: WARNING: *** No libssl found. Disabling ssl support.***" >&5
7142 echo "$as_me: WARNING: *** No libssl found. Disabling ssl support.***" >&2;}
7143 fi
7144 fi
7145
7146 if test "x$ssl_ok" = "xyes"; then
7147
7148 cat >>confdefs.h <<\_ACEOF
7149 #define ENABLE_SSL
7150 _ACEOF
7151
7152 fi
7153
7154
7155 if test -z "$LIBPTHREAD_LIBS"; then
7156 case $target in
7157 *-*-linux*|*-*-solaris*)
7158 old_libs="$LIBS"
7159 echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5
7160 echo $ECHO_N "checking for pthread_create in -lpthread... $ECHO_C" >&6
7161 if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then
7162 echo $ECHO_N "(cached) $ECHO_C" >&6
7163 else
7164 ac_check_lib_save_LIBS=$LIBS
7165 LIBS="-lpthread $LIBS"
7166 cat >conftest.$ac_ext <<_ACEOF
7167 /* confdefs.h. */
7168 _ACEOF
7169 cat confdefs.h >>conftest.$ac_ext
7170 cat >>conftest.$ac_ext <<_ACEOF
7171 /* end confdefs.h. */
7172
7173 /* Override any gcc2 internal prototype to avoid an error. */
7174 #ifdef __cplusplus
7175 extern "C"
7176 #endif
7177 /* We use char because int might match the return type of a gcc2
7178 builtin and then its argument prototype would still apply. */
7179 char pthread_create ();
7180 int
7181 main ()
7182 {
7183 pthread_create ();
7184 ;
7185 return 0;
7186 }
7187 _ACEOF
7188 rm -f conftest.$ac_objext conftest$ac_exeext
7189 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7190 (eval $ac_link) 2>conftest.er1
7191 ac_status=$?
7192 grep -v '^ *+' conftest.er1 >conftest.err
7193 rm -f conftest.er1
7194 cat conftest.err >&5
7195 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7196 (exit $ac_status); } &&
7197 { ac_try='test -z "$ac_c_werror_flag"
7198 || test ! -s conftest.err'
7199 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7200 (eval $ac_try) 2>&5
7201 ac_status=$?
7202 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7203 (exit $ac_status); }; } &&
7204 { ac_try='test -s conftest$ac_exeext'
7205 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7206 (eval $ac_try) 2>&5
7207 ac_status=$?
7208 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7209 (exit $ac_status); }; }; then
7210 ac_cv_lib_pthread_pthread_create=yes
7211 else
7212 echo "$as_me: failed program was:" >&5
7213 sed 's/^/| /' conftest.$ac_ext >&5
7214
7215 ac_cv_lib_pthread_pthread_create=no
7216 fi
7217 rm -f conftest.err conftest.$ac_objext \
7218 conftest$ac_exeext conftest.$ac_ext
7219 LIBS=$ac_check_lib_save_LIBS
7220 fi
7221 echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_create" >&5
7222 echo "${ECHO_T}$ac_cv_lib_pthread_pthread_create" >&6
7223 if test $ac_cv_lib_pthread_pthread_create = yes; then
7224 LIBPTHREAD_LIBS="-lpthread"
7225 fi
7226
7227 LIBS="$old_libs"
7228 ;;
7229
7230 *-*-osf1*)
7231 echo "$as_me:$LINENO: checking whether pthreads work" >&5
7232 echo $ECHO_N "checking whether pthreads work... $ECHO_C" >&6
7233 LIBPTHREAD_LIBS="-lpthread -lexc -ldb"
7234 { echo "$as_me:$LINENO: WARNING: *** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***" >&5
7235 echo "$as_me: WARNING: *** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***" >&2;}
7236 ;;
7237
7238 *)
7239 echo "$as_me:$LINENO: checking whether threads work with -pthread" >&5
7240 echo $ECHO_N "checking whether threads work with -pthread... $ECHO_C" >&6
7241 LDSAVEFLAGS=$LDFLAGS
7242 LDFLAGS="$LDFLAGS -pthread"
7243 cat >conftest.$ac_ext <<_ACEOF
7244 /* confdefs.h. */
7245 _ACEOF
7246 cat confdefs.h >>conftest.$ac_ext
7247 cat >>conftest.$ac_ext <<_ACEOF
7248 /* end confdefs.h. */
7249
7250 /* Override any gcc2 internal prototype to avoid an error. */
7251 #ifdef __cplusplus
7252 extern "C"
7253 #endif
7254 /* We use char because int might match the return type of a gcc2
7255 builtin and then its argument prototype would still apply. */
7256 char pthread_create ();
7257 int
7258 main ()
7259 {
7260 pthread_create ();
7261 ;
7262 return 0;
7263 }
7264 _ACEOF
7265 rm -f conftest.$ac_objext conftest$ac_exeext
7266 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7267 (eval $ac_link) 2>conftest.er1
7268 ac_status=$?
7269 grep -v '^ *+' conftest.er1 >conftest.err
7270 rm -f conftest.er1
7271 cat conftest.err >&5
7272 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7273 (exit $ac_status); } &&
7274 { ac_try='test -z "$ac_c_werror_flag"
7275 || test ! -s conftest.err'
7276 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7277 (eval $ac_try) 2>&5
7278 ac_status=$?
7279 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7280 (exit $ac_status); }; } &&
7281 { ac_try='test -s conftest$ac_exeext'
7282 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7283 (eval $ac_try) 2>&5
7284 ac_status=$?
7285 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7286 (exit $ac_status); }; }; then
7287 pthread_ok=yes
7288 else
7289 echo "$as_me: failed program was:" >&5
7290 sed 's/^/| /' conftest.$ac_ext >&5
7291
7292 pthread_ok=no
7293 fi
7294 rm -f conftest.err conftest.$ac_objext \
7295 conftest$ac_exeext conftest.$ac_ext
7296 LDFLAGS=$LDSAVEFLAGS
7297
7298 if test "x$pthread_ok" = "xyes"; then
7299 echo "$as_me:$LINENO: result: yes" >&5
7300 echo "${ECHO_T}yes" >&6
7301 LIBPTHREAD_LDFLAGS="-pthread"
7302 else
7303 echo "$as_me:$LINENO: result: no. Now we will try some libraries." >&5
7304 echo "${ECHO_T}no. Now we will try some libraries." >&6
7305
7306 echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
7307 echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
7308 if test "${ac_cv_search_pthread_create+set}" = set; then
7309 echo $ECHO_N "(cached) $ECHO_C" >&6
7310 else
7311 ac_func_search_save_LIBS=$LIBS
7312 ac_cv_search_pthread_create=no
7313 cat >conftest.$ac_ext <<_ACEOF
7314 /* confdefs.h. */
7315 _ACEOF
7316 cat confdefs.h >>conftest.$ac_ext
7317 cat >>conftest.$ac_ext <<_ACEOF
7318 /* end confdefs.h. */
7319
7320 /* Override any gcc2 internal prototype to avoid an error. */
7321 #ifdef __cplusplus
7322 extern "C"
7323 #endif
7324 /* We use char because int might match the return type of a gcc2
7325 builtin and then its argument prototype would still apply. */
7326 char pthread_create ();
7327 int
7328 main ()
7329 {
7330 pthread_create ();
7331 ;
7332 return 0;
7333 }
7334 _ACEOF
7335 rm -f conftest.$ac_objext conftest$ac_exeext
7336 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7337 (eval $ac_link) 2>conftest.er1
7338 ac_status=$?
7339 grep -v '^ *+' conftest.er1 >conftest.err
7340 rm -f conftest.er1
7341 cat conftest.err >&5
7342 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7343 (exit $ac_status); } &&
7344 { ac_try='test -z "$ac_c_werror_flag"
7345 || test ! -s conftest.err'
7346 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7347 (eval $ac_try) 2>&5
7348 ac_status=$?
7349 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7350 (exit $ac_status); }; } &&
7351 { ac_try='test -s conftest$ac_exeext'
7352 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7353 (eval $ac_try) 2>&5
7354 ac_status=$?
7355 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7356 (exit $ac_status); }; }; then
7357 ac_cv_search_pthread_create="none required"
7358 else
7359 echo "$as_me: failed program was:" >&5
7360 sed 's/^/| /' conftest.$ac_ext >&5
7361
7362 fi
7363 rm -f conftest.err conftest.$ac_objext \
7364 conftest$ac_exeext conftest.$ac_ext
7365 if test "$ac_cv_search_pthread_create" = no; then
7366 for ac_lib in pthread; do
7367 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
7368 cat >conftest.$ac_ext <<_ACEOF
7369 /* confdefs.h. */
7370 _ACEOF
7371 cat confdefs.h >>conftest.$ac_ext
7372 cat >>conftest.$ac_ext <<_ACEOF
7373 /* end confdefs.h. */
7374
7375 /* Override any gcc2 internal prototype to avoid an error. */
7376 #ifdef __cplusplus
7377 extern "C"
7378 #endif
7379 /* We use char because int might match the return type of a gcc2
7380 builtin and then its argument prototype would still apply. */
7381 char pthread_create ();
7382 int
7383 main ()
7384 {
7385 pthread_create ();
7386 ;
7387 return 0;
7388 }
7389 _ACEOF
7390 rm -f conftest.$ac_objext conftest$ac_exeext
7391 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7392 (eval $ac_link) 2>conftest.er1
7393 ac_status=$?
7394 grep -v '^ *+' conftest.er1 >conftest.err
7395 rm -f conftest.er1
7396 cat conftest.err >&5
7397 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7398 (exit $ac_status); } &&
7399 { ac_try='test -z "$ac_c_werror_flag"
7400 || test ! -s conftest.err'
7401 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7402 (eval $ac_try) 2>&5
7403 ac_status=$?
7404 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7405 (exit $ac_status); }; } &&
7406 { ac_try='test -s conftest$ac_exeext'
7407 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7408 (eval $ac_try) 2>&5
7409 ac_status=$?
7410 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7411 (exit $ac_status); }; }; then
7412 ac_cv_search_pthread_create="-l$ac_lib"
7413 break
7414 else
7415 echo "$as_me: failed program was:" >&5
7416 sed 's/^/| /' conftest.$ac_ext >&5
7417
7418 fi
7419 rm -f conftest.err conftest.$ac_objext \
7420 conftest$ac_exeext conftest.$ac_ext
7421 done
7422 fi
7423 LIBS=$ac_func_search_save_LIBS
7424 fi
7425 echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
7426 echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
7427 if test "$ac_cv_search_pthread_create" != no; then
7428 test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
7429 LIBPTHREADS_LIBS="-lpthread"
7430 else
7431 echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
7432 echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
7433 if test "${ac_cv_search_pthread_create+set}" = set; then
7434 echo $ECHO_N "(cached) $ECHO_C" >&6
7435 else
7436 ac_func_search_save_LIBS=$LIBS
7437 ac_cv_search_pthread_create=no
7438 cat >conftest.$ac_ext <<_ACEOF
7439 /* confdefs.h. */
7440 _ACEOF
7441 cat confdefs.h >>conftest.$ac_ext
7442 cat >>conftest.$ac_ext <<_ACEOF
7443 /* end confdefs.h. */
7444
7445 /* Override any gcc2 internal prototype to avoid an error. */
7446 #ifdef __cplusplus
7447 extern "C"
7448 #endif
7449 /* We use char because int might match the return type of a gcc2
7450 builtin and then its argument prototype would still apply. */
7451 char pthread_create ();
7452 int
7453 main ()
7454 {
7455 pthread_create ();
7456 ;
7457 return 0;
7458 }
7459 _ACEOF
7460 rm -f conftest.$ac_objext conftest$ac_exeext
7461 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7462 (eval $ac_link) 2>conftest.er1
7463 ac_status=$?
7464 grep -v '^ *+' conftest.er1 >conftest.err
7465 rm -f conftest.er1
7466 cat conftest.err >&5
7467 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7468 (exit $ac_status); } &&
7469 { ac_try='test -z "$ac_c_werror_flag"
7470 || test ! -s conftest.err'
7471 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7472 (eval $ac_try) 2>&5
7473 ac_status=$?
7474 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7475 (exit $ac_status); }; } &&
7476 { ac_try='test -s conftest$ac_exeext'
7477 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7478 (eval $ac_try) 2>&5
7479 ac_status=$?
7480 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7481 (exit $ac_status); }; }; then
7482 ac_cv_search_pthread_create="none required"
7483 else
7484 echo "$as_me: failed program was:" >&5
7485 sed 's/^/| /' conftest.$ac_ext >&5
7486
7487 fi
7488 rm -f conftest.err conftest.$ac_objext \
7489 conftest$ac_exeext conftest.$ac_ext
7490 if test "$ac_cv_search_pthread_create" = no; then
7491 for ac_lib in pthreads; do
7492 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
7493 cat >conftest.$ac_ext <<_ACEOF
7494 /* confdefs.h. */
7495 _ACEOF
7496 cat confdefs.h >>conftest.$ac_ext
7497 cat >>conftest.$ac_ext <<_ACEOF
7498 /* end confdefs.h. */
7499
7500 /* Override any gcc2 internal prototype to avoid an error. */
7501 #ifdef __cplusplus
7502 extern "C"
7503 #endif
7504 /* We use char because int might match the return type of a gcc2
7505 builtin and then its argument prototype would still apply. */
7506 char pthread_create ();
7507 int
7508 main ()
7509 {
7510 pthread_create ();
7511 ;
7512 return 0;
7513 }
7514 _ACEOF
7515 rm -f conftest.$ac_objext conftest$ac_exeext
7516 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7517 (eval $ac_link) 2>conftest.er1
7518 ac_status=$?
7519 grep -v '^ *+' conftest.er1 >conftest.err
7520 rm -f conftest.er1
7521 cat conftest.err >&5
7522 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7523 (exit $ac_status); } &&
7524 { ac_try='test -z "$ac_c_werror_flag"
7525 || test ! -s conftest.err'
7526 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7527 (eval $ac_try) 2>&5
7528 ac_status=$?
7529 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7530 (exit $ac_status); }; } &&
7531 { ac_try='test -s conftest$ac_exeext'
7532 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7533 (eval $ac_try) 2>&5
7534 ac_status=$?
7535 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7536 (exit $ac_status); }; }; then
7537 ac_cv_search_pthread_create="-l$ac_lib"
7538 break
7539 else
7540 echo "$as_me: failed program was:" >&5
7541 sed 's/^/| /' conftest.$ac_ext >&5
7542
7543 fi
7544 rm -f conftest.err conftest.$ac_objext \
7545 conftest$ac_exeext conftest.$ac_ext
7546 done
7547 fi
7548 LIBS=$ac_func_search_save_LIBS
7549 fi
7550 echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
7551 echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
7552 if test "$ac_cv_search_pthread_create" != no; then
7553 test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
7554 LIBPTHREADS_LIBS="-lpthreads"
7555 else
7556 echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
7557 echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
7558 if test "${ac_cv_search_pthread_create+set}" = set; then
7559 echo $ECHO_N "(cached) $ECHO_C" >&6
7560 else
7561 ac_func_search_save_LIBS=$LIBS
7562 ac_cv_search_pthread_create=no
7563 cat >conftest.$ac_ext <<_ACEOF
7564 /* confdefs.h. */
7565 _ACEOF
7566 cat confdefs.h >>conftest.$ac_ext
7567 cat >>conftest.$ac_ext <<_ACEOF
7568 /* end confdefs.h. */
7569
7570 /* Override any gcc2 internal prototype to avoid an error. */
7571 #ifdef __cplusplus
7572 extern "C"
7573 #endif
7574 /* We use char because int might match the return type of a gcc2
7575 builtin and then its argument prototype would still apply. */
7576 char pthread_create ();
7577 int
7578 main ()
7579 {
7580 pthread_create ();
7581 ;
7582 return 0;
7583 }
7584 _ACEOF
7585 rm -f conftest.$ac_objext conftest$ac_exeext
7586 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7587 (eval $ac_link) 2>conftest.er1
7588 ac_status=$?
7589 grep -v '^ *+' conftest.er1 >conftest.err
7590 rm -f conftest.er1
7591 cat conftest.err >&5
7592 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7593 (exit $ac_status); } &&
7594 { ac_try='test -z "$ac_c_werror_flag"
7595 || test ! -s conftest.err'
7596 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7597 (eval $ac_try) 2>&5
7598 ac_status=$?
7599 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7600 (exit $ac_status); }; } &&
7601 { ac_try='test -s conftest$ac_exeext'
7602 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7603 (eval $ac_try) 2>&5
7604 ac_status=$?
7605 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7606 (exit $ac_status); }; }; then
7607 ac_cv_search_pthread_create="none required"
7608 else
7609 echo "$as_me: failed program was:" >&5
7610 sed 's/^/| /' conftest.$ac_ext >&5
7611
7612 fi
7613 rm -f conftest.err conftest.$ac_objext \
7614 conftest$ac_exeext conftest.$ac_ext
7615 if test "$ac_cv_search_pthread_create" = no; then
7616 for ac_lib in c_r; do
7617 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
7618 cat >conftest.$ac_ext <<_ACEOF
7619 /* confdefs.h. */
7620 _ACEOF
7621 cat confdefs.h >>conftest.$ac_ext
7622 cat >>conftest.$ac_ext <<_ACEOF
7623 /* end confdefs.h. */
7624
7625 /* Override any gcc2 internal prototype to avoid an error. */
7626 #ifdef __cplusplus
7627 extern "C"
7628 #endif
7629 /* We use char because int might match the return type of a gcc2
7630 builtin and then its argument prototype would still apply. */
7631 char pthread_create ();
7632 int
7633 main ()
7634 {
7635 pthread_create ();
7636 ;
7637 return 0;
7638 }
7639 _ACEOF
7640 rm -f conftest.$ac_objext conftest$ac_exeext
7641 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7642 (eval $ac_link) 2>conftest.er1
7643 ac_status=$?
7644 grep -v '^ *+' conftest.er1 >conftest.err
7645 rm -f conftest.er1
7646 cat conftest.err >&5
7647 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7648 (exit $ac_status); } &&
7649 { ac_try='test -z "$ac_c_werror_flag"
7650 || test ! -s conftest.err'
7651 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7652 (eval $ac_try) 2>&5
7653 ac_status=$?
7654 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7655 (exit $ac_status); }; } &&
7656 { ac_try='test -s conftest$ac_exeext'
7657 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7658 (eval $ac_try) 2>&5
7659 ac_status=$?
7660 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7661 (exit $ac_status); }; }; then
7662 ac_cv_search_pthread_create="-l$ac_lib"
7663 break
7664 else
7665 echo "$as_me: failed program was:" >&5
7666 sed 's/^/| /' conftest.$ac_ext >&5
7667
7668 fi
7669 rm -f conftest.err conftest.$ac_objext \
7670 conftest$ac_exeext conftest.$ac_ext
7671 done
7672 fi
7673 LIBS=$ac_func_search_save_LIBS
7674 fi
7675 echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
7676 echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
7677 if test "$ac_cv_search_pthread_create" != no; then
7678 test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
7679 LIBPTHREADS_LIBS="-lc_r"
7680 else
7681 thread_ok=no
7682 fi
7683
7684 fi
7685
7686 fi
7687
7688
7689 if test "x$thread_ok" = "xno"; then
7690 { echo "$as_me:$LINENO: WARNING: *** No pthreads found. ***" >&5
7691 echo "$as_me: WARNING: *** No pthreads found. ***" >&2;}
7692 { { echo "$as_me:$LINENO: error: *** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***" >&5
7693 echo "$as_me: error: *** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***" >&2;}
7694 { (exit 1); exit 1; }; }
7695 exit 1
7696 else
7697 { echo "$as_me:$LINENO: WARNING: found a way to link threads, but it may not work..." >&5
7698 echo "$as_me: WARNING: found a way to link threads, but it may not work..." >&2;}
7699 fi
7700 fi
7701 ;;
7702
7703 esac
7704 fi
7705
7706 case $target in
7707 *-*-solaris*)
7708 echo "$as_me:$LINENO: checking whether SunOS has -lrt " >&5
7709 echo $ECHO_N "checking whether SunOS has -lrt ... $ECHO_C" >&6
7710 LDSAVEFLAGS="$LDFLAGS"
7711 LDFLAGS="$LDFLAGS -lrt"
7712 cat >conftest.$ac_ext <<_ACEOF
7713 /* confdefs.h. */
7714 _ACEOF
7715 cat confdefs.h >>conftest.$ac_ext
7716 cat >>conftest.$ac_ext <<_ACEOF
7717 /* end confdefs.h. */
7718
7719 /* Override any gcc2 internal prototype to avoid an error. */
7720 #ifdef __cplusplus
7721 extern "C"
7722 #endif
7723 /* We use char because int might match the return type of a gcc2
7724 builtin and then its argument prototype would still apply. */
7725 char nanosleep ();
7726 int
7727 main ()
7728 {
7729 nanosleep ();
7730 ;
7731 return 0;
7732 }
7733 _ACEOF
7734 rm -f conftest.$ac_objext conftest$ac_exeext
7735 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7736 (eval $ac_link) 2>conftest.er1
7737 ac_status=$?
7738 grep -v '^ *+' conftest.er1 >conftest.err
7739 rm -f conftest.er1
7740 cat conftest.err >&5
7741 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7742 (exit $ac_status); } &&
7743 { ac_try='test -z "$ac_c_werror_flag"
7744 || test ! -s conftest.err'
7745 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7746 (eval $ac_try) 2>&5
7747 ac_status=$?
7748 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7749 (exit $ac_status); }; } &&
7750 { ac_try='test -s conftest$ac_exeext'
7751 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7752 (eval $ac_try) 2>&5
7753 ac_status=$?
7754 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7755 (exit $ac_status); }; }; then
7756 rt_ok=yes
7757 else
7758 echo "$as_me: failed program was:" >&5
7759 sed 's/^/| /' conftest.$ac_ext >&5
7760
7761 rt_ok=no
7762 fi
7763 rm -f conftest.err conftest.$ac_objext \
7764 conftest$ac_exeext conftest.$ac_ext
7765 if test "x$rt_ok" = "xyes"; then
7766 echo "$as_me:$LINENO: result: yes" >&5
7767 echo "${ECHO_T}yes" >&6
7768 else
7769 echo "$as_me:$LINENO: result: no" >&5
7770 echo "${ECHO_T}no" >&6
7771 echo "$as_me:$LINENO: checking whether SunOS has -lposix4 " >&5
7772 echo $ECHO_N "checking whether SunOS has -lposix4 ... $ECHO_C" >&6
7773 LDFLAGS="$LDSAVEFLAGS -lposix4"
7774 cat >conftest.$ac_ext <<_ACEOF
7775 /* confdefs.h. */
7776 _ACEOF
7777 cat confdefs.h >>conftest.$ac_ext
7778 cat >>conftest.$ac_ext <<_ACEOF
7779 /* end confdefs.h. */
7780
7781 /* Override any gcc2 internal prototype to avoid an error. */
7782 #ifdef __cplusplus
7783 extern "C"
7784 #endif
7785 /* We use char because int might match the return type of a gcc2
7786 builtin and then its argument prototype would still apply. */
7787 char nanosleep ();
7788 int
7789 main ()
7790 {
7791 nanosleep ();
7792 ;
7793 return 0;
7794 }
7795 _ACEOF
7796 rm -f conftest.$ac_objext conftest$ac_exeext
7797 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7798 (eval $ac_link) 2>conftest.er1
7799 ac_status=$?
7800 grep -v '^ *+' conftest.er1 >conftest.err
7801 rm -f conftest.er1
7802 cat conftest.err >&5
7803 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7804 (exit $ac_status); } &&
7805 { ac_try='test -z "$ac_c_werror_flag"
7806 || test ! -s conftest.err'
7807 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7808 (eval $ac_try) 2>&5
7809 ac_status=$?
7810 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7811 (exit $ac_status); }; } &&
7812 { ac_try='test -s conftest$ac_exeext'
7813 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7814 (eval $ac_try) 2>&5
7815 ac_status=$?
7816 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7817 (exit $ac_status); }; }; then
7818 posix_ok=yes
7819 else
7820 echo "$as_me: failed program was:" >&5
7821 sed 's/^/| /' conftest.$ac_ext >&5
7822
7823 posix_ok=no
7824 fi
7825 rm -f conftest.err conftest.$ac_objext \
7826 conftest$ac_exeext conftest.$ac_ext
7827 if test "x$posix_ok" = "xyes"; then
7828 echo "$as_me:$LINENO: result: yes" >&5
7829 echo "${ECHO_T}yes" >&6
7830 else
7831 LDFLAGS=$LDSAVEFLAGS
7832 echo "$as_me:$LINENO: result: no" >&5
7833 echo "${ECHO_T}no" >&6
7834 { echo "$as_me:$LINENO: WARNING: *** Try setting LIBS or LDFLAGS manually to point to the library with nanosleep()***" >&5
7835 echo "$as_me: WARNING: *** Try setting LIBS or LDFLAGS manually to point to the library with nanosleep()***" >&2;}
7836 fi
7837 fi
7838 ;;
7839 esac
7840
7841 if test "x$enable_cookies" = "xno" ; then
7842 CFLAGS="$CFLAGS -DDISABLE_COOKIES"
7843 fi
7844 if test "x$enable_ipv6" = "xyes" ; then
7845 CFLAGS="$CFLAGS -DENABLE_IPV6"
7846 fi
7847 if test "x$enable_efence" = "xyes" ; then
7848 LIBS="-lefence $LIBS"
7849 fi
7850 if test "x$enable_gprof" = "xyes" ; then
7851 CFLAGS="$CFLAGS -pg"
7852 fi
7853 if test "x$enable_insure" = "xyes" ; then
7854 CC="insure -Zoi \"compiler $CC\""
7855 LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0"
7856 fi
7857 if test "x$enable_rtfl" = "xyes" ; then
7858 CFLAGS="$CFLAGS -DDBG_RTFL"
7859 fi
7860 if test "x$enable_threaded_dns" = "xyes" ; then
7861 CFLAGS="$CFLAGS -DD_DNS_THREADED"
7862 fi
7863
7864 echo "$as_me:$LINENO: checking for ANSI C header files" >&5
7865 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
7866 if test "${ac_cv_header_stdc+set}" = set; then
7867 echo $ECHO_N "(cached) $ECHO_C" >&6
7868 else
7869 cat >conftest.$ac_ext <<_ACEOF
7870 /* confdefs.h. */
7871 _ACEOF
7872 cat confdefs.h >>conftest.$ac_ext
7873 cat >>conftest.$ac_ext <<_ACEOF
7874 /* end confdefs.h. */
7875 #include <stdlib.h>
7876 #include <stdarg.h>
7877 #include <string.h>
7878 #include <float.h>
7879
7880 int
7881 main ()
7882 {
7883
7884 ;
7885 return 0;
7886 }
7887 _ACEOF
7888 rm -f conftest.$ac_objext
7889 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
7890 (eval $ac_compile) 2>conftest.er1
7891 ac_status=$?
7892 grep -v '^ *+' conftest.er1 >conftest.err
7893 rm -f conftest.er1
7894 cat conftest.err >&5
7895 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7896 (exit $ac_status); } &&
7897 { ac_try='test -z "$ac_c_werror_flag"
7898 || test ! -s conftest.err'
7899 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7900 (eval $ac_try) 2>&5
7901 ac_status=$?
7902 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7903 (exit $ac_status); }; } &&
7904 { ac_try='test -s conftest.$ac_objext'
7905 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7906 (eval $ac_try) 2>&5
7907 ac_status=$?
7908 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7909 (exit $ac_status); }; }; then
7910 ac_cv_header_stdc=yes
7911 else
7912 echo "$as_me: failed program was:" >&5
7913 sed 's/^/| /' conftest.$ac_ext >&5
7914
7915 ac_cv_header_stdc=no
7916 fi
7917 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
7918
7919 if test $ac_cv_header_stdc = yes; then
7920 # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
7921 cat >conftest.$ac_ext <<_ACEOF
7922 /* confdefs.h. */
7923 _ACEOF
7924 cat confdefs.h >>conftest.$ac_ext
7925 cat >>conftest.$ac_ext <<_ACEOF
7926 /* end confdefs.h. */
7927 #include <string.h>
7928
7929 _ACEOF
7930 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
7931 $EGREP "memchr" >/dev/null 2>&1; then
7932 :
7933 else
7934 ac_cv_header_stdc=no
7935 fi
7936 rm -f conftest*
7937
7938 fi
7939
7940 if test $ac_cv_header_stdc = yes; then
7941 # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
7942 cat >conftest.$ac_ext <<_ACEOF
7943 /* confdefs.h. */
7944 _ACEOF
7945 cat confdefs.h >>conftest.$ac_ext
7946 cat >>conftest.$ac_ext <<_ACEOF
7947 /* end confdefs.h. */
7948 #include <stdlib.h>
7949
7950 _ACEOF
7951 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
7952 $EGREP "free" >/dev/null 2>&1; then
7953 :
7954 else
7955 ac_cv_header_stdc=no
7956 fi
7957 rm -f conftest*
7958
7959 fi
7960
7961 if test $ac_cv_header_stdc = yes; then
7962 # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
7963 if test "$cross_compiling" = yes; then
7964 :
7965 else
7966 cat >conftest.$ac_ext <<_ACEOF
7967 /* confdefs.h. */
7968 _ACEOF
7969 cat confdefs.h >>conftest.$ac_ext
7970 cat >>conftest.$ac_ext <<_ACEOF
7971 /* end confdefs.h. */
7972 #include <ctype.h>
7973 #if ((' ' & 0x0FF) == 0x020)
7974 # define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
7975 # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
7976 #else
7977 # define ISLOWER(c) \
7978 (('a' <= (c) && (c) <= 'i') \
7979 || ('j' <= (c) && (c) <= 'r') \
7980 || ('s' <= (c) && (c) <= 'z'))
7981 # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
7982 #endif
7983
7984 #define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
7985 int
7986 main ()
7987 {
7988 int i;
7989 for (i = 0; i < 256; i++)
7990 if (XOR (islower (i), ISLOWER (i))
7991 || toupper (i) != TOUPPER (i))
7992 exit(2);
7993 exit (0);
7994 }
7995 _ACEOF
7996 rm -f conftest$ac_exeext
7997 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7998 (eval $ac_link) 2>&5
7999 ac_status=$?
8000 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8001 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
8002 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
8003 (eval $ac_try) 2>&5
8004 ac_status=$?
8005 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8006 (exit $ac_status); }; }; then
8007 :
8008 else
8009 echo "$as_me: program exited with status $ac_status" >&5
8010 echo "$as_me: failed program was:" >&5
8011 sed 's/^/| /' conftest.$ac_ext >&5
8012
8013 ( exit $ac_status )
8014 ac_cv_header_stdc=no
8015 fi
8016 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
8017 fi
8018 fi
8019 fi
8020 echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
8021 echo "${ECHO_T}$ac_cv_header_stdc" >&6
8022 if test $ac_cv_header_stdc = yes; then
8023
8024 cat >>confdefs.h <<\_ACEOF
8025 #define STDC_HEADERS 1
8026 _ACEOF
8027
8028 fi
8029
8030
8031
8032
8033 for ac_header in fcntl.h unistd.h sys/uio.h
8034 do
8035 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
8036 if eval "test \"\${$as_ac_Header+set}\" = set"; then
8037 echo "$as_me:$LINENO: checking for $ac_header" >&5
8038 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
8039 if eval "test \"\${$as_ac_Header+set}\" = set"; then
8040 echo $ECHO_N "(cached) $ECHO_C" >&6
8041 fi
8042 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
8043 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
8044 else
8045 # Is the header compilable?
8046 echo "$as_me:$LINENO: checking $ac_header usability" >&5
8047 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
8048 cat >conftest.$ac_ext <<_ACEOF
8049 /* confdefs.h. */
8050 _ACEOF
8051 cat confdefs.h >>conftest.$ac_ext
8052 cat >>conftest.$ac_ext <<_ACEOF
8053 /* end confdefs.h. */
8054 $ac_includes_default
8055 #include <$ac_header>
8056 _ACEOF
8057 rm -f conftest.$ac_objext
8058 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
8059 (eval $ac_compile) 2>conftest.er1
8060 ac_status=$?
8061 grep -v '^ *+' conftest.er1 >conftest.err
8062 rm -f conftest.er1
8063 cat conftest.err >&5
8064 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8065 (exit $ac_status); } &&
8066 { ac_try='test -z "$ac_c_werror_flag"
8067 || test ! -s conftest.err'
8068 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
8069 (eval $ac_try) 2>&5
8070 ac_status=$?
8071 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8072 (exit $ac_status); }; } &&
8073 { ac_try='test -s conftest.$ac_objext'
8074 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
8075 (eval $ac_try) 2>&5
8076 ac_status=$?
8077 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8078 (exit $ac_status); }; }; then
8079 ac_header_compiler=yes
8080 else
8081 echo "$as_me: failed program was:" >&5
8082 sed 's/^/| /' conftest.$ac_ext >&5
8083
8084 ac_header_compiler=no
8085 fi
8086 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
8087 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
8088 echo "${ECHO_T}$ac_header_compiler" >&6
8089
8090 # Is the header present?
8091 echo "$as_me:$LINENO: checking $ac_header presence" >&5
8092 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
8093 cat >conftest.$ac_ext <<_ACEOF
8094 /* confdefs.h. */
8095 _ACEOF
8096 cat confdefs.h >>conftest.$ac_ext
8097 cat >>conftest.$ac_ext <<_ACEOF
8098 /* end confdefs.h. */
8099 #include <$ac_header>
8100 _ACEOF
8101 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
8102 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
8103 ac_status=$?
8104 grep -v '^ *+' conftest.er1 >conftest.err
8105 rm -f conftest.er1
8106 cat conftest.err >&5
8107 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8108 (exit $ac_status); } >/dev/null; then
8109 if test -s conftest.err; then
8110 ac_cpp_err=$ac_c_preproc_warn_flag
8111 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
8112 else
8113 ac_cpp_err=
8114 fi
8115 else
8116 ac_cpp_err=yes
8117 fi
8118 if test -z "$ac_cpp_err"; then
8119 ac_header_preproc=yes
8120 else
8121 echo "$as_me: failed program was:" >&5
8122 sed 's/^/| /' conftest.$ac_ext >&5
8123
8124 ac_header_preproc=no
8125 fi
8126 rm -f conftest.err conftest.$ac_ext
8127 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
8128 echo "${ECHO_T}$ac_header_preproc" >&6
8129
8130 # So? What about this header?
8131 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
8132 yes:no: )
8133 { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
8134 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
8135 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
8136 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
8137 ac_header_preproc=yes
8138 ;;
8139 no:yes:* )
8140 { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
8141 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
8142 { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
8143 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
8144 { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
8145 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
8146 { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
8147 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
8148 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
8149 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
8150 { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
8151 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
8152 (
8153 cat <<\_ASBOX
8154 ## ------------------------------------------ ##
8155 ## Report this to the AC_PACKAGE_NAME lists. ##
8156 ## ------------------------------------------ ##
8157 _ASBOX
8158 ) |
8159 sed "s/^/$as_me: WARNING: /" >&2
8160 ;;
8161 esac
8162 echo "$as_me:$LINENO: checking for $ac_header" >&5
8163 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
8164 if eval "test \"\${$as_ac_Header+set}\" = set"; then
8165 echo $ECHO_N "(cached) $ECHO_C" >&6
8166 else
8167 eval "$as_ac_Header=\$ac_header_preproc"
8168 fi
8169 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
8170 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
8171
8172 fi
8173 if test `eval echo '${'$as_ac_Header'}'` = yes; then
8174 cat >>confdefs.h <<_ACEOF
8175 #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
8176 _ACEOF
8177
8178 fi
8179
8180 done
8181
8182
8183 if eval "test x$GCC = xyes"; then
8184 if test "`echo $CFLAGS | grep '\-D_REENTRANT' 2> /dev/null`" = ""; then
8185 CFLAGS="$CFLAGS -D_REENTRANT"
8186 fi
8187 if test "`echo $CFLAGS | grep '\-D_THREAD_SAFE' 2> /dev/null`" = ""; then
8188 CFLAGS="$CFLAGS -D_THREAD_SAFE"
8189 fi
8190 if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
8191 CFLAGS="$CFLAGS -Wall"
8192 fi
8193 if test "`echo $CFLAGS | grep '\-W^a' 2> /dev/null`" = ""; then
8194 if test "`$CC -v 2>&1 | grep 'version 3'`" != ""; then
8195 CFLAGS="$CFLAGS -W -Wno-unused-parameter"
8196 fi
8197 fi
8198 if test "`echo $CFLAGS | grep '\-Waggregate-return' 2> /dev/null`" = ""; then
8199 CFLAGS="$CFLAGS -Waggregate-return"
8200 fi
8201
8202 if eval "test x$enable_ansi = xyes"; then
8203 if test "`echo $CFLAGS | grep '\-ansi' 2> /dev/null`" = ""; then
8204 CFLAGS="$CFLAGS -ansi"
8205 fi
8206
8207 if test "`echo $CFLAGS | grep '\-pedantic' 2> /dev/null`" = ""; then
8208 CFLAGS="$CFLAGS -pedantic"
8209 fi
8210 fi
8211 fi
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227 ac_config_files="$ac_config_files Makefile dpip/Makefile dpid/Makefile dpi/Makefile doc/Makefile src/Makefile src/IO/Makefile"
8228 cat >confcache <<\_ACEOF
8229 # This file is a shell script that caches the results of configure
8230 # tests run on this system so they can be shared between configure
8231 # scripts and configure runs, see configure's option --config-cache.
8232 # It is not useful on other systems. If it contains results you don't
8233 # want to keep, you may remove or edit it.
8234 #
8235 # config.status only pays attention to the cache file if you give it
8236 # the --recheck option to rerun configure.
8237 #
8238 # `ac_cv_env_foo' variables (set or unset) will be overridden when
8239 # loading this file, other *unset* `ac_cv_foo' will be assigned the
8240 # following values.
8241
8242 _ACEOF
8243
8244 # The following way of writing the cache mishandles newlines in values,
8245 # but we know of no workaround that is simple, portable, and efficient.
8246 # So, don't put newlines in cache variables' values.
8247 # Ultrix sh set writes to stderr and can't be redirected directly,
8248 # and sets the high bit in the cache file unless we assign to the vars.
8249 {
8250 (set) 2>&1 |
8251 case `(ac_space=' '; set | grep ac_space) 2>&1` in
8252 *ac_space=\ *)
8253 # `set' does not quote correctly, so add quotes (double-quote
8254 # substitution turns \\\\ into \\, and sed turns \\ into \).
8255 sed -n \
8256 "s/'/'\\\\''/g;
8257 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
8258 ;;
8259 *)
8260 # `set' quotes correctly as required by POSIX, so do not add quotes.
8261 sed -n \
8262 "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
8263 ;;
8264 esac;
8265 } |
8266 sed '
8267 t clear
8268 : clear
8269 s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
8270 t end
8271 /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
8272 : end' >>confcache
8273 if diff $cache_file confcache >/dev/null 2>&1; then :; else
8274 if test -w $cache_file; then
8275 test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
8276 cat confcache >$cache_file
8277 else
8278 echo "not updating unwritable cache $cache_file"
8279 fi
8280 fi
8281 rm -f confcache
8282
8283 test "x$prefix" = xNONE && prefix=$ac_default_prefix
8284 # Let make expand exec_prefix.
8285 test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
8286
8287 # VPATH may cause trouble with some makes, so we remove $(srcdir),
8288 # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
8289 # trailing colons and then remove the whole line if VPATH becomes empty
8290 # (actually we leave an empty line to preserve line numbers).
8291 if test "x$srcdir" = x.; then
8292 ac_vpsub='/^[ ]*VPATH[ ]*=/{
8293 s/:*\$(srcdir):*/:/;
8294 s/:*\${srcdir}:*/:/;
8295 s/:*@srcdir@:*/:/;
8296 s/^\([^=]*=[ ]*\):*/\1/;
8297 s/:*$//;
8298 s/^[^=]*=[ ]*$//;
8299 }'
8300 fi
8301
8302 DEFS=-DHAVE_CONFIG_H
8303
8304 ac_libobjs=
8305 ac_ltlibobjs=
8306 for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
8307 # 1. Remove the extension, and $U if already installed.
8308 ac_i=`echo "$ac_i" |
8309 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
8310 # 2. Add them.
8311 ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
8312 ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
8313 done
8314 LIBOBJS=$ac_libobjs
8315
8316 LTLIBOBJS=$ac_ltlibobjs
8317
8318
8319 if test -z "${DLGUI_TRUE}" && test -z "${DLGUI_FALSE}"; then
8320 { { echo "$as_me:$LINENO: error: conditional \"DLGUI\" was never defined.
8321 Usually this means the macro was only invoked conditionally." >&5
8322 echo "$as_me: error: conditional \"DLGUI\" was never defined.
8323 Usually this means the macro was only invoked conditionally." >&2;}
8324 { (exit 1); exit 1; }; }
8325 fi
8326 if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
8327 { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
8328 Usually this means the macro was only invoked conditionally." >&5
8329 echo "$as_me: error: conditional \"AMDEP\" was never defined.
8330 Usually this means the macro was only invoked conditionally." >&2;}
8331 { (exit 1); exit 1; }; }
8332 fi
8333 if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
8334 { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
8335 Usually this means the macro was only invoked conditionally." >&5
8336 echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
8337 Usually this means the macro was only invoked conditionally." >&2;}
8338 { (exit 1); exit 1; }; }
8339 fi
8340 if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
8341 { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
8342 Usually this means the macro was only invoked conditionally." >&5
8343 echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
8344 Usually this means the macro was only invoked conditionally." >&2;}
8345 { (exit 1); exit 1; }; }
8346 fi
8347 if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
8348 { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined.
8349 Usually this means the macro was only invoked conditionally." >&5
8350 echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined.
8351 Usually this means the macro was only invoked conditionally." >&2;}
8352 { (exit 1); exit 1; }; }
8353 fi
8354
8355 : ${CONFIG_STATUS=./config.status}
8356 ac_clean_files_save=$ac_clean_files
8357 ac_clean_files="$ac_clean_files $CONFIG_STATUS"
8358 { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
8359 echo "$as_me: creating $CONFIG_STATUS" >&6;}
8360 cat >$CONFIG_STATUS <<_ACEOF
8361 #! $SHELL
8362 # Generated by $as_me.
8363 # Run this file to recreate the current configuration.
8364 # Compiler output produced by configure, useful for debugging
8365 # configure, is in config.log if it exists.
8366
8367 debug=false
8368 ac_cs_recheck=false
8369 ac_cs_silent=false
8370 SHELL=\${CONFIG_SHELL-$SHELL}
8371 _ACEOF
8372
8373 cat >>$CONFIG_STATUS <<\_ACEOF
8374 ## --------------------- ##
8375 ## M4sh Initialization. ##
8376 ## --------------------- ##
8377
8378 # Be Bourne compatible
8379 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
8380 emulate sh
8381 NULLCMD=:
8382 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
8383 # is contrary to our usage. Disable this feature.
8384 alias -g '${1+"$@"}'='"$@"'
8385 elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
8386 set -o posix
8387 fi
8388 DUALCASE=1; export DUALCASE # for MKS sh
8389
8390 # Support unset when possible.
8391 if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
8392 as_unset=unset
8393 else
8394 as_unset=false
8395 fi
8396
8397
8398 # Work around bugs in pre-3.0 UWIN ksh.
8399 $as_unset ENV MAIL MAILPATH
8400 PS1='$ '
8401 PS2='> '
8402 PS4='+ '
8403
8404 # NLS nuisances.
8405 for as_var in \
8406 LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
8407 LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
8408 LC_TELEPHONE LC_TIME
8409 do
8410 if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
8411 eval $as_var=C; export $as_var
8412 else
8413 $as_unset $as_var
8414 fi
8415 done
8416
8417 # Required to use basename.
8418 if expr a : '\(a\)' >/dev/null 2>&1; then
8419 as_expr=expr
8420 else
8421 as_expr=false
8422 fi
8423
8424 if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
8425 as_basename=basename
8426 else
8427 as_basename=false
8428 fi
8429
8430
8431 # Name of the executable.
8432 as_me=`$as_basename "$0" ||
8433 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
8434 X"$0" : 'X\(//\)$' \| \
8435 X"$0" : 'X\(/\)$' \| \
8436 . : '\(.\)' 2>/dev/null ||
8437 echo X/"$0" |
8438 sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
8439 /^X\/\(\/\/\)$/{ s//\1/; q; }
8440 /^X\/\(\/\).*/{ s//\1/; q; }
8441 s/.*/./; q'`
8442
8443
8444 # PATH needs CR, and LINENO needs CR and PATH.
8445 # Avoid depending upon Character Ranges.
8446 as_cr_letters='abcdefghijklmnopqrstuvwxyz'
8447 as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
8448 as_cr_Letters=$as_cr_letters$as_cr_LETTERS
8449 as_cr_digits='0123456789'
8450 as_cr_alnum=$as_cr_Letters$as_cr_digits
8451
8452 # The user is always right.
8453 if test "${PATH_SEPARATOR+set}" != set; then
8454 echo "#! /bin/sh" >conf$$.sh
8455 echo "exit 0" >>conf$$.sh
8456 chmod +x conf$$.sh
8457 if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
8458 PATH_SEPARATOR=';'
8459 else
8460 PATH_SEPARATOR=:
8461 fi
8462 rm -f conf$$.sh
8463 fi
8464
8465
8466 as_lineno_1=$LINENO
8467 as_lineno_2=$LINENO
8468 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
8469 test "x$as_lineno_1" != "x$as_lineno_2" &&
8470 test "x$as_lineno_3" = "x$as_lineno_2" || {
8471 # Find who we are. Look in the path if we contain no path at all
8472 # relative or not.
8473 case $0 in
8474 *[\\/]* ) as_myself=$0 ;;
8475 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8476 for as_dir in $PATH
8477 do
8478 IFS=$as_save_IFS
8479 test -z "$as_dir" && as_dir=.
8480 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
8481 done
8482
8483 ;;
8484 esac
8485 # We did not find ourselves, most probably we were run as `sh COMMAND'
8486 # in which case we are not to be found in the path.
8487 if test "x$as_myself" = x; then
8488 as_myself=$0
8489 fi
8490 if test ! -f "$as_myself"; then
8491 { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
8492 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
8493 { (exit 1); exit 1; }; }
8494 fi
8495 case $CONFIG_SHELL in
8496 '')
8497 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8498 for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
8499 do
8500 IFS=$as_save_IFS
8501 test -z "$as_dir" && as_dir=.
8502 for as_base in sh bash ksh sh5; do
8503 case $as_dir in
8504 /*)
8505 if ("$as_dir/$as_base" -c '
8506 as_lineno_1=$LINENO
8507 as_lineno_2=$LINENO
8508 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
8509 test "x$as_lineno_1" != "x$as_lineno_2" &&
8510 test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
8511 $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
8512 $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
8513 CONFIG_SHELL=$as_dir/$as_base
8514 export CONFIG_SHELL
8515 exec "$CONFIG_SHELL" "$0" ${1+"$@"}
8516 fi;;
8517 esac
8518 done
8519 done
8520 ;;
8521 esac
8522
8523 # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
8524 # uniformly replaced by the line number. The first 'sed' inserts a
8525 # line-number line before each line; the second 'sed' does the real
8526 # work. The second script uses 'N' to pair each line-number line
8527 # with the numbered line, and appends trailing '-' during
8528 # substitution so that $LINENO is not a special case at line end.
8529 # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
8530 # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
8531 sed '=' <$as_myself |
8532 sed '
8533 N
8534 s,$,-,
8535 : loop
8536 s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
8537 t loop
8538 s,-$,,
8539 s,^['$as_cr_digits']*\n,,
8540 ' >$as_me.lineno &&
8541 chmod +x $as_me.lineno ||
8542 { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
8543 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
8544 { (exit 1); exit 1; }; }
8545
8546 # Don't try to exec as it changes $[0], causing all sort of problems
8547 # (the dirname of $[0] is not the place where we might find the
8548 # original and so on. Autoconf is especially sensible to this).
8549 . ./$as_me.lineno
8550 # Exit status is that of the last command.
8551 exit
8552 }
8553
8554
8555 case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
8556 *c*,-n*) ECHO_N= ECHO_C='
8557 ' ECHO_T=' ' ;;
8558 *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
8559 *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
8560 esac
8561
8562 if expr a : '\(a\)' >/dev/null 2>&1; then
8563 as_expr=expr
8564 else
8565 as_expr=false
8566 fi
8567
8568 rm -f conf$$ conf$$.exe conf$$.file
8569 echo >conf$$.file
8570 if ln -s conf$$.file conf$$ 2>/dev/null; then
8571 # We could just check for DJGPP; but this test a) works b) is more generic
8572 # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
8573 if test -f conf$$.exe; then
8574 # Don't use ln at all; we don't have any links
8575 as_ln_s='cp -p'
8576 else
8577 as_ln_s='ln -s'
8578 fi
8579 elif ln conf$$.file conf$$ 2>/dev/null; then
8580 as_ln_s=ln
8581 else
8582 as_ln_s='cp -p'
8583 fi
8584 rm -f conf$$ conf$$.exe conf$$.file
8585
8586 if mkdir -p . 2>/dev/null; then
8587 as_mkdir_p=:
8588 else
8589 test -d ./-p && rmdir ./-p
8590 as_mkdir_p=false
8591 fi
8592
8593 as_executable_p="test -f"
8594
8595 # Sed expression to map a string onto a valid CPP name.
8596 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
8597
8598 # Sed expression to map a string onto a valid variable name.
8599 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
8600
8601
8602 # IFS
8603 # We need space, tab and new line, in precisely that order.
8604 as_nl='
8605 '
8606 IFS=" $as_nl"
8607
8608 # CDPATH.
8609 $as_unset CDPATH
8610
8611 exec 6>&1
8612
8613 # Open the log real soon, to keep \$[0] and so on meaningful, and to
8614 # report actual input values of CONFIG_FILES etc. instead of their
8615 # values after options handling. Logging --version etc. is OK.
8616 exec 5>>config.log
8617 {
8618 echo
8619 sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
8620 ## Running $as_me. ##
8621 _ASBOX
8622 } >&5
8623 cat >&5 <<_CSEOF
8624
8625 This file was extended by $as_me, which was
8626 generated by GNU Autoconf 2.59. Invocation command line was
8627
8628 CONFIG_FILES = $CONFIG_FILES
8629 CONFIG_HEADERS = $CONFIG_HEADERS
8630 CONFIG_LINKS = $CONFIG_LINKS
8631 CONFIG_COMMANDS = $CONFIG_COMMANDS
8632 $ $0 $@
8633
8634 _CSEOF
8635 echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
8636 echo >&5
8637 _ACEOF
8638
8639 # Files that config.status was made for.
8640 if test -n "$ac_config_files"; then
8641 echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
8642 fi
8643
8644 if test -n "$ac_config_headers"; then
8645 echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
8646 fi
8647
8648 if test -n "$ac_config_links"; then
8649 echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
8650 fi
8651
8652 if test -n "$ac_config_commands"; then
8653 echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
8654 fi
8655
8656 cat >>$CONFIG_STATUS <<\_ACEOF
8657
8658 ac_cs_usage="\
8659 \`$as_me' instantiates files from templates according to the
8660 current configuration.
8661
8662 Usage: $0 [OPTIONS] [FILE]...
8663
8664 -h, --help print this help, then exit
8665 -V, --version print version number, then exit
8666 -q, --quiet do not print progress messages
8667 -d, --debug don't remove temporary files
8668 --recheck update $as_me by reconfiguring in the same conditions
8669 --file=FILE[:TEMPLATE]
8670 instantiate the configuration file FILE
8671 --header=FILE[:TEMPLATE]
8672 instantiate the configuration header FILE
8673
8674 Configuration files:
8675 $config_files
8676
8677 Configuration headers:
8678 $config_headers
8679
8680 Configuration commands:
8681 $config_commands
8682
8683 Report bugs to <bug-autoconf@gnu.org>."
8684 _ACEOF
8685
8686 cat >>$CONFIG_STATUS <<_ACEOF
8687 ac_cs_version="\\
8688 config.status
8689 configured by $0, generated by GNU Autoconf 2.59,
8690 with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
8691
8692 Copyright (C) 2003 Free Software Foundation, Inc.
8693 This config.status script is free software; the Free Software Foundation
8694 gives unlimited permission to copy, distribute and modify it."
8695 srcdir=$srcdir
8696 INSTALL="$INSTALL"
8697 _ACEOF
8698
8699 cat >>$CONFIG_STATUS <<\_ACEOF
8700 # If no file are specified by the user, then we need to provide default
8701 # value. By we need to know if files were specified by the user.
8702 ac_need_defaults=:
8703 while test $# != 0
8704 do
8705 case $1 in
8706 --*=*)
8707 ac_option=`expr "x$1" : 'x\([^=]*\)='`
8708 ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
8709 ac_shift=:
8710 ;;
8711 -*)
8712 ac_option=$1
8713 ac_optarg=$2
8714 ac_shift=shift
8715 ;;
8716 *) # This is not an option, so the user has probably given explicit
8717 # arguments.
8718 ac_option=$1
8719 ac_need_defaults=false;;
8720 esac
8721
8722 case $ac_option in
8723 # Handling of the options.
8724 _ACEOF
8725 cat >>$CONFIG_STATUS <<\_ACEOF
8726 -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
8727 ac_cs_recheck=: ;;
8728 --version | --vers* | -V )
8729 echo "$ac_cs_version"; exit 0 ;;
8730 --he | --h)
8731 # Conflict between --help and --header
8732 { { echo "$as_me:$LINENO: error: ambiguous option: $1
8733 Try \`$0 --help' for more information." >&5
8734 echo "$as_me: error: ambiguous option: $1
8735 Try \`$0 --help' for more information." >&2;}
8736 { (exit 1); exit 1; }; };;
8737 --help | --hel | -h )
8738 echo "$ac_cs_usage"; exit 0 ;;
8739 --debug | --d* | -d )
8740 debug=: ;;
8741 --file | --fil | --fi | --f )
8742 $ac_shift
8743 CONFIG_FILES="$CONFIG_FILES $ac_optarg"
8744 ac_need_defaults=false;;
8745 --header | --heade | --head | --hea )
8746 $ac_shift
8747 CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
8748 ac_need_defaults=false;;
8749 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
8750 | -silent | --silent | --silen | --sile | --sil | --si | --s)
8751 ac_cs_silent=: ;;
8752
8753 # This is an error.
8754 -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
8755 Try \`$0 --help' for more information." >&5
8756 echo "$as_me: error: unrecognized option: $1
8757 Try \`$0 --help' for more information." >&2;}
8758 { (exit 1); exit 1; }; } ;;
8759
8760 *) ac_config_targets="$ac_config_targets $1" ;;
8761
8762 esac
8763 shift
8764 done
8765
8766 ac_configure_extra_args=
8767
8768 if $ac_cs_silent; then
8769 exec 6>/dev/null
8770 ac_configure_extra_args="$ac_configure_extra_args --silent"
8771 fi
8772
8773 _ACEOF
8774 cat >>$CONFIG_STATUS <<_ACEOF
8775 if \$ac_cs_recheck; then
8776 echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
8777 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
8778 fi
8779
8780 _ACEOF
8781
8782 cat >>$CONFIG_STATUS <<_ACEOF
8783 #
8784 # INIT-COMMANDS section.
8785 #
8786
8787 AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
8788
8789 _ACEOF
8790
8791
8792
8793 cat >>$CONFIG_STATUS <<\_ACEOF
8794 for ac_config_target in $ac_config_targets
8795 do
8796 case "$ac_config_target" in
8797 # Handling of arguments.
8798 "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
8799 "dpip/Makefile" ) CONFIG_FILES="$CONFIG_FILES dpip/Makefile" ;;
8800 "dpid/Makefile" ) CONFIG_FILES="$CONFIG_FILES dpid/Makefile" ;;
8801 "dpi/Makefile" ) CONFIG_FILES="$CONFIG_FILES dpi/Makefile" ;;
8802 "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
8803 "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
8804 "src/IO/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/IO/Makefile" ;;
8805 "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
8806 "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
8807 *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
8808 echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
8809 { (exit 1); exit 1; }; };;
8810 esac
8811 done
8812
8813 # If the user did not use the arguments to specify the items to instantiate,
8814 # then the envvar interface is used. Set only those that are not.
8815 # We use the long form for the default assignment because of an extremely
8816 # bizarre bug on SunOS 4.1.3.
8817 if $ac_need_defaults; then
8818 test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
8819 test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
8820 test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
8821 fi
8822
8823 # Have a temporary directory for convenience. Make it in the build tree
8824 # simply because there is no reason to put it here, and in addition,
8825 # creating and moving files from /tmp can sometimes cause problems.
8826 # Create a temporary directory, and hook for its removal unless debugging.
8827 $debug ||
8828 {
8829 trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
8830 trap '{ (exit 1); exit 1; }' 1 2 13 15
8831 }
8832
8833 # Create a (secure) tmp directory for tmp files.
8834
8835 {
8836 tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
8837 test -n "$tmp" && test -d "$tmp"
8838 } ||
8839 {
8840 tmp=./confstat$$-$RANDOM
8841 (umask 077 && mkdir $tmp)
8842 } ||
8843 {
8844 echo "$me: cannot create a temporary directory in ." >&2
8845 { (exit 1); exit 1; }
8846 }
8847
8848 _ACEOF
8849
8850 cat >>$CONFIG_STATUS <<_ACEOF
8851
8852 #
8853 # CONFIG_FILES section.
8854 #
8855
8856 # No need to generate the scripts if there are no CONFIG_FILES.
8857 # This happens for instance when ./config.status config.h
8858 if test -n "\$CONFIG_FILES"; then
8859 # Protect against being on the right side of a sed subst in config.status.
8860 sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
8861 s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
8862 s,@SHELL@,$SHELL,;t t
8863 s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
8864 s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
8865 s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
8866 s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
8867 s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
8868 s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
8869 s,@exec_prefix@,$exec_prefix,;t t
8870 s,@prefix@,$prefix,;t t
8871 s,@program_transform_name@,$program_transform_name,;t t
8872 s,@bindir@,$bindir,;t t
8873 s,@sbindir@,$sbindir,;t t
8874 s,@libexecdir@,$libexecdir,;t t
8875 s,@datadir@,$datadir,;t t
8876 s,@sysconfdir@,$sysconfdir,;t t
8877 s,@sharedstatedir@,$sharedstatedir,;t t
8878 s,@localstatedir@,$localstatedir,;t t
8879 s,@libdir@,$libdir,;t t
8880 s,@includedir@,$includedir,;t t
8881 s,@oldincludedir@,$oldincludedir,;t t
8882 s,@infodir@,$infodir,;t t
8883 s,@mandir@,$mandir,;t t
8884 s,@build_alias@,$build_alias,;t t
8885 s,@host_alias@,$host_alias,;t t
8886 s,@target_alias@,$target_alias,;t t
8887 s,@DEFS@,$DEFS,;t t
8888 s,@ECHO_C@,$ECHO_C,;t t
8889 s,@ECHO_N@,$ECHO_N,;t t
8890 s,@ECHO_T@,$ECHO_T,;t t
8891 s,@LIBS@,$LIBS,;t t
8892 s,@build@,$build,;t t
8893 s,@build_cpu@,$build_cpu,;t t
8894 s,@build_vendor@,$build_vendor,;t t
8895 s,@build_os@,$build_os,;t t
8896 s,@host@,$host,;t t
8897 s,@host_cpu@,$host_cpu,;t t
8898 s,@host_vendor@,$host_vendor,;t t
8899 s,@host_os@,$host_os,;t t
8900 s,@target@,$target,;t t
8901 s,@target_cpu@,$target_cpu,;t t
8902 s,@target_vendor@,$target_vendor,;t t
8903 s,@target_os@,$target_os,;t t
8904 s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
8905 s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
8906 s,@INSTALL_DATA@,$INSTALL_DATA,;t t
8907 s,@CYGPATH_W@,$CYGPATH_W,;t t
8908 s,@PACKAGE@,$PACKAGE,;t t
8909 s,@VERSION@,$VERSION,;t t
8910 s,@ACLOCAL@,$ACLOCAL,;t t
8911 s,@AUTOCONF@,$AUTOCONF,;t t
8912 s,@AUTOMAKE@,$AUTOMAKE,;t t
8913 s,@AUTOHEADER@,$AUTOHEADER,;t t
8914 s,@MAKEINFO@,$MAKEINFO,;t t
8915 s,@install_sh@,$install_sh,;t t
8916 s,@STRIP@,$STRIP,;t t
8917 s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
8918 s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t
8919 s,@mkdir_p@,$mkdir_p,;t t
8920 s,@AWK@,$AWK,;t t
8921 s,@SET_MAKE@,$SET_MAKE,;t t
8922 s,@am__leading_dot@,$am__leading_dot,;t t
8923 s,@AMTAR@,$AMTAR,;t t
8924 s,@am__tar@,$am__tar,;t t
8925 s,@am__untar@,$am__untar,;t t
8926 s,@DLGUI_TRUE@,$DLGUI_TRUE,;t t
8927 s,@DLGUI_FALSE@,$DLGUI_FALSE,;t t
8928 s,@CC@,$CC,;t t
8929 s,@CFLAGS@,$CFLAGS,;t t
8930 s,@LDFLAGS@,$LDFLAGS,;t t
8931 s,@CPPFLAGS@,$CPPFLAGS,;t t
8932 s,@ac_ct_CC@,$ac_ct_CC,;t t
8933 s,@EXEEXT@,$EXEEXT,;t t
8934 s,@OBJEXT@,$OBJEXT,;t t
8935 s,@DEPDIR@,$DEPDIR,;t t
8936 s,@am__include@,$am__include,;t t
8937 s,@am__quote@,$am__quote,;t t
8938 s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t
8939 s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t
8940 s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t
8941 s,@CCDEPMODE@,$CCDEPMODE,;t t
8942 s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t
8943 s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
8944 s,@RANLIB@,$RANLIB,;t t
8945 s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
8946 s,@CPP@,$CPP,;t t
8947 s,@CXX@,$CXX,;t t
8948 s,@CXXFLAGS@,$CXXFLAGS,;t t
8949 s,@ac_ct_CXX@,$ac_ct_CXX,;t t
8950 s,@CXXDEPMODE@,$CXXDEPMODE,;t t
8951 s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t
8952 s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t
8953 s,@GLIB_CONFIG@,$GLIB_CONFIG,;t t
8954 s,@GLIB_CFLAGS@,$GLIB_CFLAGS,;t t
8955 s,@GLIB_LIBS@,$GLIB_LIBS,;t t
8956 s,@GTK_CONFIG@,$GTK_CONFIG,;t t
8957 s,@GTK_CFLAGS@,$GTK_CFLAGS,;t t
8958 s,@GTK_LIBS@,$GTK_LIBS,;t t
8959 s,@EGREP@,$EGREP,;t t
8960 s,@LIBJPEG_LIBS@,$LIBJPEG_LIBS,;t t
8961 s,@LIBJPEG_LDFLAGS@,$LIBJPEG_LDFLAGS,;t t
8962 s,@LIBJPEG_CPPFLAGS@,$LIBJPEG_CPPFLAGS,;t t
8963 s,@LIBPNG_LIBS@,$LIBPNG_LIBS,;t t
8964 s,@LIBPNG_CFLAGS@,$LIBPNG_CFLAGS,;t t
8965 s,@LIBZ_LIBS@,$LIBZ_LIBS,;t t
8966 s,@LIBSSL_LIBS@,$LIBSSL_LIBS,;t t
8967 s,@LIBPTHREAD_LIBS@,$LIBPTHREAD_LIBS,;t t
8968 s,@LIBPTHREAD_LDFLAGS@,$LIBPTHREAD_LDFLAGS,;t t
8969 s,@LIBFLTK_CXXFLAGS@,$LIBFLTK_CXXFLAGS,;t t
8970 s,@LIBFLTK_LIBS@,$LIBFLTK_LIBS,;t t
8971 s,@src@,$src,;t t
8972 s,@doc@,$doc,;t t
8973 s,@bin@,$bin,;t t
8974 s,@util@,$util,;t t
8975 s,@lib@,$lib,;t t
8976 s,@LIBOBJS@,$LIBOBJS,;t t
8977 s,@LTLIBOBJS@,$LTLIBOBJS,;t t
8978 CEOF
8979
8980 _ACEOF
8981
8982 cat >>$CONFIG_STATUS <<\_ACEOF
8983 # Split the substitutions into bite-sized pieces for seds with
8984 # small command number limits, like on Digital OSF/1 and HP-UX.
8985 ac_max_sed_lines=48
8986 ac_sed_frag=1 # Number of current file.
8987 ac_beg=1 # First line for current file.
8988 ac_end=$ac_max_sed_lines # Line after last line for current file.
8989 ac_more_lines=:
8990 ac_sed_cmds=
8991 while $ac_more_lines; do
8992 if test $ac_beg -gt 1; then
8993 sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
8994 else
8995 sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
8996 fi
8997 if test ! -s $tmp/subs.frag; then
8998 ac_more_lines=false
8999 else
9000 # The purpose of the label and of the branching condition is to
9001 # speed up the sed processing (if there are no `@' at all, there
9002 # is no need to browse any of the substitutions).
9003 # These are the two extra sed commands mentioned above.
9004 (echo ':t
9005 /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
9006 if test -z "$ac_sed_cmds"; then
9007 ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
9008 else
9009 ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
9010 fi
9011 ac_sed_frag=`expr $ac_sed_frag + 1`
9012 ac_beg=$ac_end
9013 ac_end=`expr $ac_end + $ac_max_sed_lines`
9014 fi
9015 done
9016 if test -z "$ac_sed_cmds"; then
9017 ac_sed_cmds=cat
9018 fi
9019 fi # test -n "$CONFIG_FILES"
9020
9021 _ACEOF
9022 cat >>$CONFIG_STATUS <<\_ACEOF
9023 for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
9024 # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
9025 case $ac_file in
9026 - | *:- | *:-:* ) # input from stdin
9027 cat >$tmp/stdin
9028 ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9029 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9030 *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9031 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9032 * ) ac_file_in=$ac_file.in ;;
9033 esac
9034
9035 # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
9036 ac_dir=`(dirname "$ac_file") 2>/dev/null ||
9037 $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9038 X"$ac_file" : 'X\(//\)[^/]' \| \
9039 X"$ac_file" : 'X\(//\)$' \| \
9040 X"$ac_file" : 'X\(/\)' \| \
9041 . : '\(.\)' 2>/dev/null ||
9042 echo X"$ac_file" |
9043 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9044 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9045 /^X\(\/\/\)$/{ s//\1/; q; }
9046 /^X\(\/\).*/{ s//\1/; q; }
9047 s/.*/./; q'`
9048 { if $as_mkdir_p; then
9049 mkdir -p "$ac_dir"
9050 else
9051 as_dir="$ac_dir"
9052 as_dirs=
9053 while test ! -d "$as_dir"; do
9054 as_dirs="$as_dir $as_dirs"
9055 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9056 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9057 X"$as_dir" : 'X\(//\)[^/]' \| \
9058 X"$as_dir" : 'X\(//\)$' \| \
9059 X"$as_dir" : 'X\(/\)' \| \
9060 . : '\(.\)' 2>/dev/null ||
9061 echo X"$as_dir" |
9062 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9063 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9064 /^X\(\/\/\)$/{ s//\1/; q; }
9065 /^X\(\/\).*/{ s//\1/; q; }
9066 s/.*/./; q'`
9067 done
9068 test ! -n "$as_dirs" || mkdir $as_dirs
9069 fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
9070 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
9071 { (exit 1); exit 1; }; }; }
9072
9073 ac_builddir=.
9074
9075 if test "$ac_dir" != .; then
9076 ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
9077 # A "../" for each directory in $ac_dir_suffix.
9078 ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
9079 else
9080 ac_dir_suffix= ac_top_builddir=
9081 fi
9082
9083 case $srcdir in
9084 .) # No --srcdir option. We are building in place.
9085 ac_srcdir=.
9086 if test -z "$ac_top_builddir"; then
9087 ac_top_srcdir=.
9088 else
9089 ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
9090 fi ;;
9091 [\\/]* | ?:[\\/]* ) # Absolute path.
9092 ac_srcdir=$srcdir$ac_dir_suffix;
9093 ac_top_srcdir=$srcdir ;;
9094 *) # Relative path.
9095 ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
9096 ac_top_srcdir=$ac_top_builddir$srcdir ;;
9097 esac
9098
9099 # Do not use `cd foo && pwd` to compute absolute paths, because
9100 # the directories may not exist.
9101 case `pwd` in
9102 .) ac_abs_builddir="$ac_dir";;
9103 *)
9104 case "$ac_dir" in
9105 .) ac_abs_builddir=`pwd`;;
9106 [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
9107 *) ac_abs_builddir=`pwd`/"$ac_dir";;
9108 esac;;
9109 esac
9110 case $ac_abs_builddir in
9111 .) ac_abs_top_builddir=${ac_top_builddir}.;;
9112 *)
9113 case ${ac_top_builddir}. in
9114 .) ac_abs_top_builddir=$ac_abs_builddir;;
9115 [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
9116 *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
9117 esac;;
9118 esac
9119 case $ac_abs_builddir in
9120 .) ac_abs_srcdir=$ac_srcdir;;
9121 *)
9122 case $ac_srcdir in
9123 .) ac_abs_srcdir=$ac_abs_builddir;;
9124 [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
9125 *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
9126 esac;;
9127 esac
9128 case $ac_abs_builddir in
9129 .) ac_abs_top_srcdir=$ac_top_srcdir;;
9130 *)
9131 case $ac_top_srcdir in
9132 .) ac_abs_top_srcdir=$ac_abs_builddir;;
9133 [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
9134 *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
9135 esac;;
9136 esac
9137
9138
9139 case $INSTALL in
9140 [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
9141 *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
9142 esac
9143
9144 if test x"$ac_file" != x-; then
9145 { echo "$as_me:$LINENO: creating $ac_file" >&5
9146 echo "$as_me: creating $ac_file" >&6;}
9147 rm -f "$ac_file"
9148 fi
9149 # Let's still pretend it is `configure' which instantiates (i.e., don't
9150 # use $as_me), people would be surprised to read:
9151 # /* config.h. Generated by config.status. */
9152 if test x"$ac_file" = x-; then
9153 configure_input=
9154 else
9155 configure_input="$ac_file. "
9156 fi
9157 configure_input=$configure_input"Generated from `echo $ac_file_in |
9158 sed 's,.*/,,'` by configure."
9159
9160 # First look for the input files in the build tree, otherwise in the
9161 # src tree.
9162 ac_file_inputs=`IFS=:
9163 for f in $ac_file_in; do
9164 case $f in
9165 -) echo $tmp/stdin ;;
9166 [\\/$]*)
9167 # Absolute (can't be DOS-style, as IFS=:)
9168 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9169 echo "$as_me: error: cannot find input file: $f" >&2;}
9170 { (exit 1); exit 1; }; }
9171 echo "$f";;
9172 *) # Relative
9173 if test -f "$f"; then
9174 # Build tree
9175 echo "$f"
9176 elif test -f "$srcdir/$f"; then
9177 # Source tree
9178 echo "$srcdir/$f"
9179 else
9180 # /dev/null tree
9181 { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9182 echo "$as_me: error: cannot find input file: $f" >&2;}
9183 { (exit 1); exit 1; }; }
9184 fi;;
9185 esac
9186 done` || { (exit 1); exit 1; }
9187 _ACEOF
9188 cat >>$CONFIG_STATUS <<_ACEOF
9189 sed "$ac_vpsub
9190 $extrasub
9191 _ACEOF
9192 cat >>$CONFIG_STATUS <<\_ACEOF
9193 :t
9194 /@[a-zA-Z_][a-zA-Z_0-9]*@/!b
9195 s,@configure_input@,$configure_input,;t t
9196 s,@srcdir@,$ac_srcdir,;t t
9197 s,@abs_srcdir@,$ac_abs_srcdir,;t t
9198 s,@top_srcdir@,$ac_top_srcdir,;t t
9199 s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
9200 s,@builddir@,$ac_builddir,;t t
9201 s,@abs_builddir@,$ac_abs_builddir,;t t
9202 s,@top_builddir@,$ac_top_builddir,;t t
9203 s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
9204 s,@INSTALL@,$ac_INSTALL,;t t
9205 " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
9206 rm -f $tmp/stdin
9207 if test x"$ac_file" != x-; then
9208 mv $tmp/out $ac_file
9209 else
9210 cat $tmp/out
9211 rm -f $tmp/out
9212 fi
9213
9214 done
9215 _ACEOF
9216 cat >>$CONFIG_STATUS <<\_ACEOF
9217
9218 #
9219 # CONFIG_HEADER section.
9220 #
9221
9222 # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
9223 # NAME is the cpp macro being defined and VALUE is the value it is being given.
9224 #
9225 # ac_d sets the value in "#define NAME VALUE" lines.
9226 ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)'
9227 ac_dB='[ ].*$,\1#\2'
9228 ac_dC=' '
9229 ac_dD=',;t'
9230 # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
9231 ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
9232 ac_uB='$,\1#\2define\3'
9233 ac_uC=' '
9234 ac_uD=',;t'
9235
9236 for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
9237 # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
9238 case $ac_file in
9239 - | *:- | *:-:* ) # input from stdin
9240 cat >$tmp/stdin
9241 ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9242 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9243 *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9244 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9245 * ) ac_file_in=$ac_file.in ;;
9246 esac
9247
9248 test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
9249 echo "$as_me: creating $ac_file" >&6;}
9250
9251 # First look for the input files in the build tree, otherwise in the
9252 # src tree.
9253 ac_file_inputs=`IFS=:
9254 for f in $ac_file_in; do
9255 case $f in
9256 -) echo $tmp/stdin ;;
9257 [\\/$]*)
9258 # Absolute (can't be DOS-style, as IFS=:)
9259 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9260 echo "$as_me: error: cannot find input file: $f" >&2;}
9261 { (exit 1); exit 1; }; }
9262 # Do quote $f, to prevent DOS paths from being IFS'd.
9263 echo "$f";;
9264 *) # Relative
9265 if test -f "$f"; then
9266 # Build tree
9267 echo "$f"
9268 elif test -f "$srcdir/$f"; then
9269 # Source tree
9270 echo "$srcdir/$f"
9271 else
9272 # /dev/null tree
9273 { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9274 echo "$as_me: error: cannot find input file: $f" >&2;}
9275 { (exit 1); exit 1; }; }
9276 fi;;
9277 esac
9278 done` || { (exit 1); exit 1; }
9279 # Remove the trailing spaces.
9280 sed 's/[ ]*$//' $ac_file_inputs >$tmp/in
9281
9282 _ACEOF
9283
9284 # Transform confdefs.h into two sed scripts, `conftest.defines' and
9285 # `conftest.undefs', that substitutes the proper values into
9286 # config.h.in to produce config.h. The first handles `#define'
9287 # templates, and the second `#undef' templates.
9288 # And first: Protect against being on the right side of a sed subst in
9289 # config.status. Protect against being in an unquoted here document
9290 # in config.status.
9291 rm -f conftest.defines conftest.undefs
9292 # Using a here document instead of a string reduces the quoting nightmare.
9293 # Putting comments in sed scripts is not portable.
9294 #
9295 # `end' is used to avoid that the second main sed command (meant for
9296 # 0-ary CPP macros) applies to n-ary macro definitions.
9297 # See the Autoconf documentation for `clear'.
9298 cat >confdef2sed.sed <<\_ACEOF
9299 s/[\\&,]/\\&/g
9300 s,[\\$`],\\&,g
9301 t clear
9302 : clear
9303 s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
9304 t end
9305 s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
9306 : end
9307 _ACEOF
9308 # If some macros were called several times there might be several times
9309 # the same #defines, which is useless. Nevertheless, we may not want to
9310 # sort them, since we want the *last* AC-DEFINE to be honored.
9311 uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
9312 sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
9313 rm -f confdef2sed.sed
9314
9315 # This sed command replaces #undef with comments. This is necessary, for
9316 # example, in the case of _POSIX_SOURCE, which is predefined and required
9317 # on some systems where configure will not decide to define it.
9318 cat >>conftest.undefs <<\_ACEOF
9319 s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
9320 _ACEOF
9321
9322 # Break up conftest.defines because some shells have a limit on the size
9323 # of here documents, and old seds have small limits too (100 cmds).
9324 echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
9325 echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
9326 echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
9327 echo ' :' >>$CONFIG_STATUS
9328 rm -f conftest.tail
9329 while grep . conftest.defines >/dev/null
9330 do
9331 # Write a limited-size here document to $tmp/defines.sed.
9332 echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
9333 # Speed up: don't consider the non `#define' lines.
9334 echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS
9335 # Work around the forget-to-reset-the-flag bug.
9336 echo 't clr' >>$CONFIG_STATUS
9337 echo ': clr' >>$CONFIG_STATUS
9338 sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
9339 echo 'CEOF
9340 sed -f $tmp/defines.sed $tmp/in >$tmp/out
9341 rm -f $tmp/in
9342 mv $tmp/out $tmp/in
9343 ' >>$CONFIG_STATUS
9344 sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
9345 rm -f conftest.defines
9346 mv conftest.tail conftest.defines
9347 done
9348 rm -f conftest.defines
9349 echo ' fi # grep' >>$CONFIG_STATUS
9350 echo >>$CONFIG_STATUS
9351
9352 # Break up conftest.undefs because some shells have a limit on the size
9353 # of here documents, and old seds have small limits too (100 cmds).
9354 echo ' # Handle all the #undef templates' >>$CONFIG_STATUS
9355 rm -f conftest.tail
9356 while grep . conftest.undefs >/dev/null
9357 do
9358 # Write a limited-size here document to $tmp/undefs.sed.
9359 echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
9360 # Speed up: don't consider the non `#undef'
9361 echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS
9362 # Work around the forget-to-reset-the-flag bug.
9363 echo 't clr' >>$CONFIG_STATUS
9364 echo ': clr' >>$CONFIG_STATUS
9365 sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
9366 echo 'CEOF
9367 sed -f $tmp/undefs.sed $tmp/in >$tmp/out
9368 rm -f $tmp/in
9369 mv $tmp/out $tmp/in
9370 ' >>$CONFIG_STATUS
9371 sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
9372 rm -f conftest.undefs
9373 mv conftest.tail conftest.undefs
9374 done
9375 rm -f conftest.undefs
9376
9377 cat >>$CONFIG_STATUS <<\_ACEOF
9378 # Let's still pretend it is `configure' which instantiates (i.e., don't
9379 # use $as_me), people would be surprised to read:
9380 # /* config.h. Generated by config.status. */
9381 if test x"$ac_file" = x-; then
9382 echo "/* Generated by configure. */" >$tmp/config.h
9383 else
9384 echo "/* $ac_file. Generated by configure. */" >$tmp/config.h
9385 fi
9386 cat $tmp/in >>$tmp/config.h
9387 rm -f $tmp/in
9388 if test x"$ac_file" != x-; then
9389 if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
9390 { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
9391 echo "$as_me: $ac_file is unchanged" >&6;}
9392 else
9393 ac_dir=`(dirname "$ac_file") 2>/dev/null ||
9394 $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9395 X"$ac_file" : 'X\(//\)[^/]' \| \
9396 X"$ac_file" : 'X\(//\)$' \| \
9397 X"$ac_file" : 'X\(/\)' \| \
9398 . : '\(.\)' 2>/dev/null ||
9399 echo X"$ac_file" |
9400 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9401 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9402 /^X\(\/\/\)$/{ s//\1/; q; }
9403 /^X\(\/\).*/{ s//\1/; q; }
9404 s/.*/./; q'`
9405 { if $as_mkdir_p; then
9406 mkdir -p "$ac_dir"
9407 else
9408 as_dir="$ac_dir"
9409 as_dirs=
9410 while test ! -d "$as_dir"; do
9411 as_dirs="$as_dir $as_dirs"
9412 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9413 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9414 X"$as_dir" : 'X\(//\)[^/]' \| \
9415 X"$as_dir" : 'X\(//\)$' \| \
9416 X"$as_dir" : 'X\(/\)' \| \
9417 . : '\(.\)' 2>/dev/null ||
9418 echo X"$as_dir" |
9419 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9420 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9421 /^X\(\/\/\)$/{ s//\1/; q; }
9422 /^X\(\/\).*/{ s//\1/; q; }
9423 s/.*/./; q'`
9424 done
9425 test ! -n "$as_dirs" || mkdir $as_dirs
9426 fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
9427 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
9428 { (exit 1); exit 1; }; }; }
9429
9430 rm -f $ac_file
9431 mv $tmp/config.h $ac_file
9432 fi
9433 else
9434 cat $tmp/config.h
9435 rm -f $tmp/config.h
9436 fi
9437 # Compute $ac_file's index in $config_headers.
9438 _am_stamp_count=1
9439 for _am_header in $config_headers :; do
9440 case $_am_header in
9441 $ac_file | $ac_file:* )
9442 break ;;
9443 * )
9444 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
9445 esac
9446 done
9447 echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null ||
9448 $as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9449 X$ac_file : 'X\(//\)[^/]' \| \
9450 X$ac_file : 'X\(//\)$' \| \
9451 X$ac_file : 'X\(/\)' \| \
9452 . : '\(.\)' 2>/dev/null ||
9453 echo X$ac_file |
9454 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9455 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9456 /^X\(\/\/\)$/{ s//\1/; q; }
9457 /^X\(\/\).*/{ s//\1/; q; }
9458 s/.*/./; q'`/stamp-h$_am_stamp_count
9459 done
9460 _ACEOF
9461 cat >>$CONFIG_STATUS <<\_ACEOF
9462
9463 #
9464 # CONFIG_COMMANDS section.
9465 #
9466 for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
9467 ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
9468 ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
9469 ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
9470 $as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9471 X"$ac_dest" : 'X\(//\)[^/]' \| \
9472 X"$ac_dest" : 'X\(//\)$' \| \
9473 X"$ac_dest" : 'X\(/\)' \| \
9474 . : '\(.\)' 2>/dev/null ||
9475 echo X"$ac_dest" |
9476 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9477 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9478 /^X\(\/\/\)$/{ s//\1/; q; }
9479 /^X\(\/\).*/{ s//\1/; q; }
9480 s/.*/./; q'`
9481 { if $as_mkdir_p; then
9482 mkdir -p "$ac_dir"
9483 else
9484 as_dir="$ac_dir"
9485 as_dirs=
9486 while test ! -d "$as_dir"; do
9487 as_dirs="$as_dir $as_dirs"
9488 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9489 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9490 X"$as_dir" : 'X\(//\)[^/]' \| \
9491 X"$as_dir" : 'X\(//\)$' \| \
9492 X"$as_dir" : 'X\(/\)' \| \
9493 . : '\(.\)' 2>/dev/null ||
9494 echo X"$as_dir" |
9495 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9496 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9497 /^X\(\/\/\)$/{ s//\1/; q; }
9498 /^X\(\/\).*/{ s//\1/; q; }
9499 s/.*/./; q'`
9500 done
9501 test ! -n "$as_dirs" || mkdir $as_dirs
9502 fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
9503 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
9504 { (exit 1); exit 1; }; }; }
9505
9506 ac_builddir=.
9507
9508 if test "$ac_dir" != .; then
9509 ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
9510 # A "../" for each directory in $ac_dir_suffix.
9511 ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
9512 else
9513 ac_dir_suffix= ac_top_builddir=
9514 fi
9515
9516 case $srcdir in
9517 .) # No --srcdir option. We are building in place.
9518 ac_srcdir=.
9519 if test -z "$ac_top_builddir"; then
9520 ac_top_srcdir=.
9521 else
9522 ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
9523 fi ;;
9524 [\\/]* | ?:[\\/]* ) # Absolute path.
9525 ac_srcdir=$srcdir$ac_dir_suffix;
9526 ac_top_srcdir=$srcdir ;;
9527 *) # Relative path.
9528 ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
9529 ac_top_srcdir=$ac_top_builddir$srcdir ;;
9530 esac
9531
9532 # Do not use `cd foo && pwd` to compute absolute paths, because
9533 # the directories may not exist.
9534 case `pwd` in
9535 .) ac_abs_builddir="$ac_dir";;
9536 *)
9537 case "$ac_dir" in
9538 .) ac_abs_builddir=`pwd`;;
9539 [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
9540 *) ac_abs_builddir=`pwd`/"$ac_dir";;
9541 esac;;
9542 esac
9543 case $ac_abs_builddir in
9544 .) ac_abs_top_builddir=${ac_top_builddir}.;;
9545 *)
9546 case ${ac_top_builddir}. in
9547 .) ac_abs_top_builddir=$ac_abs_builddir;;
9548 [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
9549 *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
9550 esac;;
9551 esac
9552 case $ac_abs_builddir in
9553 .) ac_abs_srcdir=$ac_srcdir;;
9554 *)
9555 case $ac_srcdir in
9556 .) ac_abs_srcdir=$ac_abs_builddir;;
9557 [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
9558 *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
9559 esac;;
9560 esac
9561 case $ac_abs_builddir in
9562 .) ac_abs_top_srcdir=$ac_top_srcdir;;
9563 *)
9564 case $ac_top_srcdir in
9565 .) ac_abs_top_srcdir=$ac_abs_builddir;;
9566 [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
9567 *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
9568 esac;;
9569 esac
9570
9571
9572 { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
9573 echo "$as_me: executing $ac_dest commands" >&6;}
9574 case $ac_dest in
9575 depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do
9576 # Strip MF so we end up with the name of the file.
9577 mf=`echo "$mf" | sed -e 's/:.*$//'`
9578 # Check whether this is an Automake generated Makefile or not.
9579 # We used to match only the files named `Makefile.in', but
9580 # some people rename them; so instead we look at the file content.
9581 # Grep'ing the first line is not enough: some people post-process
9582 # each Makefile.in and add a new line on top of each file to say so.
9583 # So let's grep whole file.
9584 if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
9585 dirpart=`(dirname "$mf") 2>/dev/null ||
9586 $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9587 X"$mf" : 'X\(//\)[^/]' \| \
9588 X"$mf" : 'X\(//\)$' \| \
9589 X"$mf" : 'X\(/\)' \| \
9590 . : '\(.\)' 2>/dev/null ||
9591 echo X"$mf" |
9592 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9593 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9594 /^X\(\/\/\)$/{ s//\1/; q; }
9595 /^X\(\/\).*/{ s//\1/; q; }
9596 s/.*/./; q'`
9597 else
9598 continue
9599 fi
9600 # Extract the definition of DEPDIR, am__include, and am__quote
9601 # from the Makefile without running `make'.
9602 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
9603 test -z "$DEPDIR" && continue
9604 am__include=`sed -n 's/^am__include = //p' < "$mf"`
9605 test -z "am__include" && continue
9606 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
9607 # When using ansi2knr, U may be empty or an underscore; expand it
9608 U=`sed -n 's/^U = //p' < "$mf"`
9609 # Find all dependency output files, they are included files with
9610 # $(DEPDIR) in their names. We invoke sed twice because it is the
9611 # simplest approach to changing $(DEPDIR) to its actual value in the
9612 # expansion.
9613 for file in `sed -n "
9614 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
9615 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
9616 # Make sure the directory exists.
9617 test -f "$dirpart/$file" && continue
9618 fdir=`(dirname "$file") 2>/dev/null ||
9619 $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9620 X"$file" : 'X\(//\)[^/]' \| \
9621 X"$file" : 'X\(//\)$' \| \
9622 X"$file" : 'X\(/\)' \| \
9623 . : '\(.\)' 2>/dev/null ||
9624 echo X"$file" |
9625 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9626 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9627 /^X\(\/\/\)$/{ s//\1/; q; }
9628 /^X\(\/\).*/{ s//\1/; q; }
9629 s/.*/./; q'`
9630 { if $as_mkdir_p; then
9631 mkdir -p $dirpart/$fdir
9632 else
9633 as_dir=$dirpart/$fdir
9634 as_dirs=
9635 while test ! -d "$as_dir"; do
9636 as_dirs="$as_dir $as_dirs"
9637 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9638 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9639 X"$as_dir" : 'X\(//\)[^/]' \| \
9640 X"$as_dir" : 'X\(//\)$' \| \
9641 X"$as_dir" : 'X\(/\)' \| \
9642 . : '\(.\)' 2>/dev/null ||
9643 echo X"$as_dir" |
9644 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9645 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9646 /^X\(\/\/\)$/{ s//\1/; q; }
9647 /^X\(\/\).*/{ s//\1/; q; }
9648 s/.*/./; q'`
9649 done
9650 test ! -n "$as_dirs" || mkdir $as_dirs
9651 fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5
9652 echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;}
9653 { (exit 1); exit 1; }; }; }
9654
9655 # echo "creating $dirpart/$file"
9656 echo '# dummy' > "$dirpart/$file"
9657 done
9658 done
9659 ;;
9660 esac
9661 done
9662 _ACEOF
9663
9664 cat >>$CONFIG_STATUS <<\_ACEOF
9665
9666 { (exit 0); exit 0; }
9667 _ACEOF
9668 chmod +x $CONFIG_STATUS
9669 ac_clean_files=$ac_clean_files_save
9670
9671
9672 # configure is writing to config.log, and then calls config.status.
9673 # config.status does its own redirection, appending to config.log.
9674 # Unfortunately, on DOS this fails, as config.log is still kept open
9675 # by configure, so config.status won't be able to write to it; its
9676 # output is simply discarded. So we exec the FD to /dev/null,
9677 # effectively closing config.log, so it can be properly (re)opened and
9678 # appended to by config.status. When coming back to configure, we
9679 # need to make the FD available again.
9680 if test "$no_create" != yes; then
9681 ac_cs_success=:
9682 ac_config_status_args=
9683 test "$silent" = yes &&
9684 ac_config_status_args="$ac_config_status_args --quiet"
9685 exec 5>/dev/null
9686 $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
9687 exec 5>>config.log
9688 # Use ||, not &&, to avoid exiting from the if with $? = 1, which
9689 # would make configure fail if this is the last instruction.
9690 $ac_cs_success || { (exit 1); exit 1; }
9691 fi
9692
9693
0 dnl Process this file with aclocal, autoconf and automake.
1
2 AC_INIT(src/dillo.c)
3
4 dnl Detect the canonical host and target build environment
5 AC_CANONICAL_SYSTEM
6
7 AM_INIT_AUTOMAKE(dillo, 0.8.6)
8 AM_CONFIG_HEADER(config.h)
9
10 dnl Options
11
12 AC_ARG_WITH(jpeg-lib, [ --with-jpeg-lib=DIR Specify where to find libjpeg], LIBJPEG_LIBDIR=$withval)
13 AC_ARG_WITH(jpeg-inc, [ --with-jpeg-inc=DIR Specify where to find libjpeg's headers], LIBJPEG_INCDIR=$withval)
14
15 AC_ARG_ENABLE(efence, [ --enable-efence Try to compile and run with Electric Fence],
16 , enable_efence=no)
17 AC_ARG_ENABLE(gprof, [ --enable-gprof Try to compile and run with profiling enabled],
18 , enable_gprof=no)
19 AC_ARG_ENABLE(insure, [ --enable-insure Try to compile and run with Insure++],
20 , enable_insure=no)
21 AC_ARG_ENABLE(ansi, [ --enable-ansi Try to compile and run with ANSI flags],
22 , enable_ansi=no)
23 AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Build with support for IPv6], , )
24 AC_ARG_ENABLE(rtfl, [ --enable-rtfl Build with rtfl messages], enable_rtfl=yes)
25 AC_ARG_ENABLE(cookies,[ --disable-cookies Don't compile support for cookies],
26 , enable_cookies=yes)
27 AC_ARG_ENABLE(png, [ --disable-png Disable support for PNG images],
28 enable_png=$enableval, enable_png=yes)
29 AC_ARG_ENABLE(jpeg, [ --disable-jpeg Disable support for JPEG images],
30 enable_jpeg=$enableval, enable_jpeg=yes)
31 AC_ARG_ENABLE(gif, [ --disable-gif Disable support for GIF images],
32 enable_gif=$enableval, enable_gif=yes)
33 AC_ARG_ENABLE(ssl, [ --disable-ssl Disable ssl features (eg. https)],
34 enable_ssl=$enableval, enable_ssl=yes)
35 AC_ARG_ENABLE(dlgui, [ --disable-dlgui Disable FLTK2 GUI for downloads],
36 enable_dlgui=$enableval, enable_dlgui=yes)
37 AC_ARG_ENABLE(threaded-dns,[ --disable-threaded-dns Disable the advantage of a reentrant resolver library],
38 enable_threaded_dns=$enableval, enable_threaded_dns=yes)
39 AM_CONDITIONAL(DLGUI, test x$enable_dlgui = xyes)
40
41 AC_PROG_CC
42 AM_PROG_CC_STDC
43 AC_PROG_RANLIB
44 AC_PROG_CPP
45 AC_PROG_CXX
46
47 dnl --------------------------------------
48 dnl Check whether to add /usr/local or not
49 dnl (this is somewhat a religious problem)
50 dnl --------------------------------------
51 dnl
52 if test "`$CPP -v < /dev/null 2>&1 | grep '/usr/local/include' 2>&1`" = ""; then
53 CPPFLAGS="$CPPFLAGS -I/usr/local/include"
54 LDFLAGS="$LDFLAGS -L/usr/local/lib"
55 fi
56
57 dnl ------------------------------------
58 dnl Check for socket libs (AIX, Solaris)
59 dnl ------------------------------------
60 dnl
61 AC_CHECK_FUNCS(gethostbyname,,
62 [AC_CHECK_LIB(nsl,gethostbyname,,[AC_CHECK_LIB(socket,gethostbyname)])])
63 AC_CHECK_FUNCS(setsockopt,,[AC_CHECK_LIB(socket,setsockopt)])
64
65 dnl --------------------
66 dnl Checks for socklen_t
67 dnl --------------------
68 dnl
69 AC_MSG_CHECKING([for socklen_t])
70 ac_cv_socklen_t=""
71 AC_TRY_COMPILE([
72 #include <sys/types.h>
73 #include <sys/socket.h>
74 ],[
75 socklen_t a=0;
76 getsockname(0,(struct sockaddr*)0, &a);
77 ],
78 ac_cv_socklen_t="socklen_t",
79 AC_TRY_COMPILE([
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 ],[
83 int a=0;
84 getsockname(0,(struct sockaddr*)0, &a);
85 ],
86 ac_cv_socklen_t="int",
87 ac_cv_socklen_t="size_t"
88 )
89 )
90 AC_MSG_RESULT($ac_cv_socklen_t)
91 if test "$ac_cv_socklen_t" != "socklen_t"; then
92 AC_DEFINE_UNQUOTED(socklen_t, $ac_cv_socklen_t,
93 [Define the real type of socklen_t])
94 fi
95
96
97 dnl -----------------------------------
98 dnl Check for glib
99 dnl -----------------------------------
100 dnl
101 AM_PATH_GLIB(1.2.0, ,
102 AC_MSG_ERROR([Unable to find glib with a version >= 1.2.0. Dillo NEEDS glib]))
103
104
105 dnl -----------------------------------
106 dnl Check for Gtk+
107 dnl -----------------------------------
108 dnl
109 AM_PATH_GTK(1.2.0, ,
110 AC_MSG_ERROR(Unable to find Gtk+ with a version >= 1.2.0. Dillo NEEDS Gtk+))
111
112 dnl -----------------------------------
113 dnl Check for FLTK
114 dnl -----------------------------------
115 dnl
116 dnl For debugging and to be user friendly
117 AC_MSG_CHECKING([Hackish check for FLTK])
118 dnl Call the config sripts.
119 LIBFLTK_CXXFLAGS=`fltk-config --cxxflags`
120 LIBFLTK_LIBS=`fltk-config --ldflags`
121
122
123 dnl ----------------
124 dnl Test for libjpeg
125 dnl ----------------
126 dnl
127 if test "x$enable_jpeg" = "xyes"; then
128 AC_CHECK_HEADER(jpeglib.h, jpeg_ok=yes, jpeg_ok=no)
129
130 if test "x$jpeg_ok" = "xyes"; then
131 old_libs="$LIBS"
132 AC_CHECK_LIB(jpeg, jpeg_destroy_decompress, jpeg_ok=yes, jpeg_ok=no)
133 LIBS="$old_libs"
134 fi
135
136 if test "x$jpeg_ok" = "xyes"; then
137 LIBJPEG_LIBS="-ljpeg"
138 if test -n "$LIBJPEG_LIBDIR"; then
139 LIBJPEG_LDFLAGS="-L$LIBJPEG_LIBDIR"
140 fi
141 if test -n "$LIBJPEG_INCDIR"; then
142 LIBJPEG_CPPFLAGS="-I$LIBJPEG_INCDIR"
143 fi
144 else
145 AC_MSG_WARN([*** No libjpeg found. Disabling jpeg images.***])
146 fi
147 fi
148
149 if test "x$jpeg_ok" = "xyes"; then
150 AC_DEFINE([ENABLE_JPEG], [], [Enable JPEG images])
151 fi
152
153 dnl ------------------------------
154 dnl Test for zlib (libpng uses it)
155 dnl ------------------------------
156 dnl
157 if test "x$enable_png" = "xyes"; then
158 AC_CHECK_HEADER(zlib.h, libz_ok=yes, libz_ok=no)
159
160 if test "x$libz_ok" = "xyes"; then
161 old_libs="$LIBS"
162 AC_CHECK_LIB(z, zlibVersion, libz_ok=yes, libz_ok=no)
163 LIBS="$old_libs"
164 fi
165
166 if test "x$libz_ok" = xyes; then
167 LIBZ_LIBS="-lz"
168 else
169 AC_MSG_WARN([*** No libz found. Disabling PNG images ***])
170 fi
171 fi
172
173 dnl ---------------
174 dnl Test for libpng
175 dnl ---------------
176 dnl
177 if test "x$enable_png" = "xyes" && test "x$libz_ok" = "xyes"; then
178 AC_MSG_CHECKING([for libpng-config])
179
180 dnl Check if the user hasn't set the variable $PNG_CONFIG
181 if test -z "$PNG_CONFIG"; then
182 PNG_CONFIG=`which libpng12-config`
183 if test -z "$PNG_CONFIG"; then
184 PNG_CONFIG=`which libpng-config`
185 fi
186 if test -z "$PNG_CONFIG"; then
187 PNG_CONFIG=`which libpng10-config`
188 fi
189 fi
190
191 dnl Check if the libpng-config script was found and is executable
192 if test -n "$PNG_CONFIG" && test -x "$PNG_CONFIG"; then
193 AC_MSG_RESULT([$PNG_CONFIG])
194 png_ok="yes"
195 else
196 AC_MSG_RESULT([missing])
197 png_ok="no"
198 fi
199
200 if test "x$png_ok" = "xyes"; then
201 dnl For debugging and to be user friendly
202 AC_MSG_CHECKING([for libpng version])
203 png_version=`$PNG_CONFIG --version`
204 case $png_version in
205 1.2.*) AC_MSG_RESULT([$png_version (newer version)]) ;;
206 1.0.*) AC_MSG_RESULT([$png_version (older version)]) ;;
207 *) AC_MSG_RESULT([ERROR]) ;;
208 esac
209
210 dnl Try to use options that are supported by all libpng-config versions...
211 LIBPNG_CFLAGS=`$PNG_CONFIG --cflags`
212 LIBPNG_LIBS=`$PNG_CONFIG --ldflags`
213 case $png_version in
214 1.2.4*) LIBPNG_LIBS="$LIBPNG_LIBS `$PNG_CONFIG --libs`" ;;
215 esac
216 else
217 dnl Try to find libpng even though libpng-config wasn't found
218 AC_CHECK_HEADERS(png.h libpng/png.h, png_ok=yes && break, png_ok=no)
219
220 if test "x$png_ok" = "xyes"; then
221 old_libs="$LIBS"
222 AC_CHECK_LIB(png, png_check_sig, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
223 LIBS="$old_libs"
224
225 if test "x$png_ok" = "xyes"; then
226 LIBPNG_LIBS="-lpng -lm"
227 fi
228 fi
229
230 if test "x$png_ok" = "xno"; then
231 AC_MSG_WARN([*** No libpng found. Disabling PNG images ***])
232 fi
233 fi
234 fi
235
236 if test "x$png_ok" = "xyes"; then
237 AC_DEFINE([ENABLE_PNG], [], [Enable PNG images])
238 fi
239
240 dnl Check if support for GIF images should be compiled in
241 if test "x$enable_gif" = "xyes"; then
242 AC_DEFINE([ENABLE_GIF], [], [Enable GIF images])
243 fi
244
245 dnl --------------------------
246 dnl Test for support for SSL
247 dnl --------------------------
248 dnl
249 if test "x$enable_ssl" = "xyes"; then
250 AC_CHECK_HEADER(openssl/ssl.h, ssl_ok=yes, ssl_ok=no)
251
252 if test "x$ssl_ok" = "xyes"; then
253 old_libs="$LIBS"
254 AC_CHECK_LIB(ssl, SSL_library_init, ssl_ok=yes, ssl_ok=no, -lcrypto)
255 LIBS="$old_libs"
256 fi
257
258 if test "x$ssl_ok" = "xyes"; then
259 LIBSSL_LIBS="-lcrypto -lssl"
260 else
261 AC_MSG_WARN([*** No libssl found. Disabling ssl support.***])
262 fi
263 fi
264
265 if test "x$ssl_ok" = "xyes"; then
266 AC_DEFINE([ENABLE_SSL], [], [Enable SSL support])
267 fi
268
269
270 dnl ----------------------
271 dnl Test for POSIX threads
272 dnl ----------------------
273 dnl
274 if test -z "$LIBPTHREAD_LIBS"; then
275 case $target in
276 *-*-linux*|*-*-solaris*)
277 old_libs="$LIBS"
278 AC_CHECK_LIB(pthread, pthread_create, LIBPTHREAD_LIBS="-lpthread")
279 LIBS="$old_libs"
280 ;;
281
282 *-*-osf1*)
283 AC_MSG_CHECKING(whether pthreads work)
284 LIBPTHREAD_LIBS="-lpthread -lexc -ldb"
285 AC_MSG_WARN([*** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***])
286 ;;
287
288 *)
289 AC_MSG_CHECKING(whether threads work with -pthread)
290 LDSAVEFLAGS=$LDFLAGS
291 LDFLAGS="$LDFLAGS -pthread"
292 AC_TRY_LINK_FUNC(pthread_create, pthread_ok=yes, pthread_ok=no)
293 LDFLAGS=$LDSAVEFLAGS
294
295 if test "x$pthread_ok" = "xyes"; then
296 AC_MSG_RESULT(yes)
297 LIBPTHREAD_LDFLAGS="-pthread"
298 else
299 AC_MSG_RESULT(no. Now we will try some libraries.)
300
301 AC_SEARCH_LIBS(pthread_create, pthread,
302 LIBPTHREADS_LIBS="-lpthread",
303 AC_SEARCH_LIBS(pthread_create, pthreads,
304 LIBPTHREADS_LIBS="-lpthreads",
305 AC_SEARCH_LIBS(pthread_create, c_r,
306 LIBPTHREADS_LIBS="-lc_r", thread_ok=no)))
307
308 if test "x$thread_ok" = "xno"; then
309 AC_MSG_WARN([*** No pthreads found. ***])
310 AC_MSG_ERROR([*** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***])
311 exit 1
312 else
313 AC_MSG_WARN([found a way to link threads, but it may not work...])
314 fi
315 fi
316 ;;
317
318 esac
319 fi
320
321 dnl ------------------------------------
322 dnl Workaround for nanosleep and solaris
323 dnl ------------------------------------
324 dnl
325 case $target in
326 *-*-solaris*)
327 AC_MSG_CHECKING(whether SunOS has -lrt )
328 LDSAVEFLAGS="$LDFLAGS"
329 LDFLAGS="$LDFLAGS -lrt"
330 AC_TRY_LINK_FUNC(nanosleep, rt_ok=yes, rt_ok=no)
331 if test "x$rt_ok" = "xyes"; then
332 AC_MSG_RESULT(yes)
333 else
334 AC_MSG_RESULT(no)
335 AC_MSG_CHECKING(whether SunOS has -lposix4 )
336 LDFLAGS="$LDSAVEFLAGS -lposix4"
337 AC_TRY_LINK_FUNC(nanosleep, posix_ok=yes, posix_ok=no)
338 if test "x$posix_ok" = "xyes"; then
339 AC_MSG_RESULT(yes)
340 else
341 LDFLAGS=$LDSAVEFLAGS
342 AC_MSG_RESULT(no)
343 AC_MSG_WARN([*** Try setting LIBS or LDFLAGS manually to point to the library with nanosleep()***])
344 fi
345 fi
346 ;;
347 esac
348
349 dnl --------------------
350 dnl Command line options
351 dnl --------------------
352 dnl
353 if test "x$enable_cookies" = "xno" ; then
354 CFLAGS="$CFLAGS -DDISABLE_COOKIES"
355 fi
356 if test "x$enable_ipv6" = "xyes" ; then
357 CFLAGS="$CFLAGS -DENABLE_IPV6"
358 fi
359 if test "x$enable_efence" = "xyes" ; then
360 LIBS="-lefence $LIBS"
361 fi
362 if test "x$enable_gprof" = "xyes" ; then
363 CFLAGS="$CFLAGS -pg"
364 fi
365 if test "x$enable_insure" = "xyes" ; then
366 CC="insure -Zoi \"compiler $CC\""
367 LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0"
368 fi
369 if test "x$enable_rtfl" = "xyes" ; then
370 CFLAGS="$CFLAGS -DDBG_RTFL"
371 fi
372 if test "x$enable_threaded_dns" = "xyes" ; then
373 CFLAGS="$CFLAGS -DD_DNS_THREADED"
374 fi
375
376 dnl -----------------------
377 dnl Checks for header files
378 dnl -----------------------
379 dnl
380 AC_HEADER_STDC
381 AC_CHECK_HEADERS(fcntl.h unistd.h sys/uio.h)
382
383 dnl --------------------------
384 dnl Check for compiler options
385 dnl --------------------------
386 dnl
387 if eval "test x$GCC = xyes"; then
388 if test "`echo $CFLAGS | grep '\-D_REENTRANT' 2> /dev/null`" = ""; then
389 CFLAGS="$CFLAGS -D_REENTRANT"
390 fi
391 if test "`echo $CFLAGS | grep '\-D_THREAD_SAFE' 2> /dev/null`" = ""; then
392 CFLAGS="$CFLAGS -D_THREAD_SAFE"
393 fi
394 if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
395 CFLAGS="$CFLAGS -Wall"
396 fi
397 if test "`echo $CFLAGS | grep '\-W[^a]' 2> /dev/null`" = ""; then
398 if test "`$CC -v 2>&1 | grep 'version 3'`" != ""; then
399 CFLAGS="$CFLAGS -W -Wno-unused-parameter"
400 fi
401 fi
402 if test "`echo $CFLAGS | grep '\-Waggregate-return' 2> /dev/null`" = ""; then
403 CFLAGS="$CFLAGS -Waggregate-return"
404 fi
405
406 if eval "test x$enable_ansi = xyes"; then
407 if test "`echo $CFLAGS | grep '\-ansi' 2> /dev/null`" = ""; then
408 CFLAGS="$CFLAGS -ansi"
409 fi
410
411 if test "`echo $CFLAGS | grep '\-pedantic' 2> /dev/null`" = ""; then
412 CFLAGS="$CFLAGS -pedantic"
413 fi
414 fi
415 fi
416
417 AC_SUBST(LIBJPEG_LIBS)
418 AC_SUBST(LIBJPEG_LDFLAGS)
419 AC_SUBST(LIBJPEG_CPPFLAGS)
420 AC_SUBST(LIBPNG_LIBS)
421 AC_SUBST(LIBPNG_CFLAGS)
422 AC_SUBST(LIBZ_LIBS)
423 AC_SUBST(LIBSSL_LIBS)
424 AC_SUBST(LIBPTHREAD_LIBS)
425 AC_SUBST(LIBPTHREAD_LDFLAGS)
426 AC_SUBST(LIBFLTK_CXXFLAGS)
427 AC_SUBST(LIBFLTK_LIBS)
428 AC_SUBST(datadir)
429 AC_SUBST(src doc bin util lib)
430
431 AC_OUTPUT(Makefile dpip/Makefile dpid/Makefile dpi/Makefile doc/Makefile src/Makefile src/IO/Makefile)
432
0 #! /bin/sh
1 # depcomp - compile a program generating dependencies as side-effects
2
3 scriptversion=2005-02-09.22
4
5 # Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 # 02111-1307, USA.
21
22 # As a special exception to the GNU General Public License, if you
23 # distribute this file as part of a program that contains a
24 # configuration script generated by Autoconf, you may include it under
25 # the same distribution terms that you use for the rest of that program.
26
27 # Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
28
29 case $1 in
30 '')
31 echo "$0: No command. Try \`$0 --help' for more information." 1>&2
32 exit 1;
33 ;;
34 -h | --h*)
35 cat <<\EOF
36 Usage: depcomp [--help] [--version] PROGRAM [ARGS]
37
38 Run PROGRAMS ARGS to compile a file, generating dependencies
39 as side-effects.
40
41 Environment variables:
42 depmode Dependency tracking mode.
43 source Source file read by `PROGRAMS ARGS'.
44 object Object file output by `PROGRAMS ARGS'.
45 DEPDIR directory where to store dependencies.
46 depfile Dependency file to output.
47 tmpdepfile Temporary file to use when outputing dependencies.
48 libtool Whether libtool is used (yes/no).
49
50 Report bugs to <bug-automake@gnu.org>.
51 EOF
52 exit $?
53 ;;
54 -v | --v*)
55 echo "depcomp $scriptversion"
56 exit $?
57 ;;
58 esac
59
60 if test -z "$depmode" || test -z "$source" || test -z "$object"; then
61 echo "depcomp: Variables source, object and depmode must be set" 1>&2
62 exit 1
63 fi
64
65 # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
66 depfile=${depfile-`echo "$object" |
67 sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
68 tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
69
70 rm -f "$tmpdepfile"
71
72 # Some modes work just like other modes, but use different flags. We
73 # parameterize here, but still list the modes in the big case below,
74 # to make depend.m4 easier to write. Note that we *cannot* use a case
75 # here, because this file can only contain one case statement.
76 if test "$depmode" = hp; then
77 # HP compiler uses -M and no extra arg.
78 gccflag=-M
79 depmode=gcc
80 fi
81
82 if test "$depmode" = dashXmstdout; then
83 # This is just like dashmstdout with a different argument.
84 dashmflag=-xM
85 depmode=dashmstdout
86 fi
87
88 case "$depmode" in
89 gcc3)
90 ## gcc 3 implements dependency tracking that does exactly what
91 ## we want. Yay! Note: for some reason libtool 1.4 doesn't like
92 ## it if -MD -MP comes after the -MF stuff. Hmm.
93 "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
94 stat=$?
95 if test $stat -eq 0; then :
96 else
97 rm -f "$tmpdepfile"
98 exit $stat
99 fi
100 mv "$tmpdepfile" "$depfile"
101 ;;
102
103 gcc)
104 ## There are various ways to get dependency output from gcc. Here's
105 ## why we pick this rather obscure method:
106 ## - Don't want to use -MD because we'd like the dependencies to end
107 ## up in a subdir. Having to rename by hand is ugly.
108 ## (We might end up doing this anyway to support other compilers.)
109 ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
110 ## -MM, not -M (despite what the docs say).
111 ## - Using -M directly means running the compiler twice (even worse
112 ## than renaming).
113 if test -z "$gccflag"; then
114 gccflag=-MD,
115 fi
116 "$@" -Wp,"$gccflag$tmpdepfile"
117 stat=$?
118 if test $stat -eq 0; then :
119 else
120 rm -f "$tmpdepfile"
121 exit $stat
122 fi
123 rm -f "$depfile"
124 echo "$object : \\" > "$depfile"
125 alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
126 ## The second -e expression handles DOS-style file names with drive letters.
127 sed -e 's/^[^:]*: / /' \
128 -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
129 ## This next piece of magic avoids the `deleted header file' problem.
130 ## The problem is that when a header file which appears in a .P file
131 ## is deleted, the dependency causes make to die (because there is
132 ## typically no way to rebuild the header). We avoid this by adding
133 ## dummy dependencies for each header file. Too bad gcc doesn't do
134 ## this for us directly.
135 tr ' ' '
136 ' < "$tmpdepfile" |
137 ## Some versions of gcc put a space before the `:'. On the theory
138 ## that the space means something, we add a space to the output as
139 ## well.
140 ## Some versions of the HPUX 10.20 sed can't process this invocation
141 ## correctly. Breaking it into two sed invocations is a workaround.
142 sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
143 rm -f "$tmpdepfile"
144 ;;
145
146 hp)
147 # This case exists only to let depend.m4 do its work. It works by
148 # looking at the text of this script. This case will never be run,
149 # since it is checked for above.
150 exit 1
151 ;;
152
153 sgi)
154 if test "$libtool" = yes; then
155 "$@" "-Wp,-MDupdate,$tmpdepfile"
156 else
157 "$@" -MDupdate "$tmpdepfile"
158 fi
159 stat=$?
160 if test $stat -eq 0; then :
161 else
162 rm -f "$tmpdepfile"
163 exit $stat
164 fi
165 rm -f "$depfile"
166
167 if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
168 echo "$object : \\" > "$depfile"
169
170 # Clip off the initial element (the dependent). Don't try to be
171 # clever and replace this with sed code, as IRIX sed won't handle
172 # lines with more than a fixed number of characters (4096 in
173 # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
174 # the IRIX cc adds comments like `#:fec' to the end of the
175 # dependency line.
176 tr ' ' '
177 ' < "$tmpdepfile" \
178 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
179 tr '
180 ' ' ' >> $depfile
181 echo >> $depfile
182
183 # The second pass generates a dummy entry for each header file.
184 tr ' ' '
185 ' < "$tmpdepfile" \
186 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
187 >> $depfile
188 else
189 # The sourcefile does not contain any dependencies, so just
190 # store a dummy comment line, to avoid errors with the Makefile
191 # "include basename.Plo" scheme.
192 echo "#dummy" > "$depfile"
193 fi
194 rm -f "$tmpdepfile"
195 ;;
196
197 aix)
198 # The C for AIX Compiler uses -M and outputs the dependencies
199 # in a .u file. In older versions, this file always lives in the
200 # current directory. Also, the AIX compiler puts `$object:' at the
201 # start of each line; $object doesn't have directory information.
202 # Version 6 uses the directory in both cases.
203 stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
204 tmpdepfile="$stripped.u"
205 if test "$libtool" = yes; then
206 "$@" -Wc,-M
207 else
208 "$@" -M
209 fi
210 stat=$?
211
212 if test -f "$tmpdepfile"; then :
213 else
214 stripped=`echo "$stripped" | sed 's,^.*/,,'`
215 tmpdepfile="$stripped.u"
216 fi
217
218 if test $stat -eq 0; then :
219 else
220 rm -f "$tmpdepfile"
221 exit $stat
222 fi
223
224 if test -f "$tmpdepfile"; then
225 outname="$stripped.o"
226 # Each line is of the form `foo.o: dependent.h'.
227 # Do two passes, one to just change these to
228 # `$object: dependent.h' and one to simply `dependent.h:'.
229 sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
230 sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
231 else
232 # The sourcefile does not contain any dependencies, so just
233 # store a dummy comment line, to avoid errors with the Makefile
234 # "include basename.Plo" scheme.
235 echo "#dummy" > "$depfile"
236 fi
237 rm -f "$tmpdepfile"
238 ;;
239
240 icc)
241 # Intel's C compiler understands `-MD -MF file'. However on
242 # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
243 # ICC 7.0 will fill foo.d with something like
244 # foo.o: sub/foo.c
245 # foo.o: sub/foo.h
246 # which is wrong. We want:
247 # sub/foo.o: sub/foo.c
248 # sub/foo.o: sub/foo.h
249 # sub/foo.c:
250 # sub/foo.h:
251 # ICC 7.1 will output
252 # foo.o: sub/foo.c sub/foo.h
253 # and will wrap long lines using \ :
254 # foo.o: sub/foo.c ... \
255 # sub/foo.h ... \
256 # ...
257
258 "$@" -MD -MF "$tmpdepfile"
259 stat=$?
260 if test $stat -eq 0; then :
261 else
262 rm -f "$tmpdepfile"
263 exit $stat
264 fi
265 rm -f "$depfile"
266 # Each line is of the form `foo.o: dependent.h',
267 # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
268 # Do two passes, one to just change these to
269 # `$object: dependent.h' and one to simply `dependent.h:'.
270 sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
271 # Some versions of the HPUX 10.20 sed can't process this invocation
272 # correctly. Breaking it into two sed invocations is a workaround.
273 sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
274 sed -e 's/$/ :/' >> "$depfile"
275 rm -f "$tmpdepfile"
276 ;;
277
278 tru64)
279 # The Tru64 compiler uses -MD to generate dependencies as a side
280 # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
281 # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
282 # dependencies in `foo.d' instead, so we check for that too.
283 # Subdirectories are respected.
284 dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
285 test "x$dir" = "x$object" && dir=
286 base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
287
288 if test "$libtool" = yes; then
289 # With Tru64 cc, shared objects can also be used to make a
290 # static library. This mecanism is used in libtool 1.4 series to
291 # handle both shared and static libraries in a single compilation.
292 # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
293 #
294 # With libtool 1.5 this exception was removed, and libtool now
295 # generates 2 separate objects for the 2 libraries. These two
296 # compilations output dependencies in in $dir.libs/$base.o.d and
297 # in $dir$base.o.d. We have to check for both files, because
298 # one of the two compilations can be disabled. We should prefer
299 # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
300 # automatically cleaned when .libs/ is deleted, while ignoring
301 # the former would cause a distcleancheck panic.
302 tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
303 tmpdepfile2=$dir$base.o.d # libtool 1.5
304 tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
305 tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
306 "$@" -Wc,-MD
307 else
308 tmpdepfile1=$dir$base.o.d
309 tmpdepfile2=$dir$base.d
310 tmpdepfile3=$dir$base.d
311 tmpdepfile4=$dir$base.d
312 "$@" -MD
313 fi
314
315 stat=$?
316 if test $stat -eq 0; then :
317 else
318 rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
319 exit $stat
320 fi
321
322 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
323 do
324 test -f "$tmpdepfile" && break
325 done
326 if test -f "$tmpdepfile"; then
327 sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
328 # That's a tab and a space in the [].
329 sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
330 else
331 echo "#dummy" > "$depfile"
332 fi
333 rm -f "$tmpdepfile"
334 ;;
335
336 #nosideeffect)
337 # This comment above is used by automake to tell side-effect
338 # dependency tracking mechanisms from slower ones.
339
340 dashmstdout)
341 # Important note: in order to support this mode, a compiler *must*
342 # always write the preprocessed file to stdout, regardless of -o.
343 "$@" || exit $?
344
345 # Remove the call to Libtool.
346 if test "$libtool" = yes; then
347 while test $1 != '--mode=compile'; do
348 shift
349 done
350 shift
351 fi
352
353 # Remove `-o $object'.
354 IFS=" "
355 for arg
356 do
357 case $arg in
358 -o)
359 shift
360 ;;
361 $object)
362 shift
363 ;;
364 *)
365 set fnord "$@" "$arg"
366 shift # fnord
367 shift # $arg
368 ;;
369 esac
370 done
371
372 test -z "$dashmflag" && dashmflag=-M
373 # Require at least two characters before searching for `:'
374 # in the target name. This is to cope with DOS-style filenames:
375 # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
376 "$@" $dashmflag |
377 sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
378 rm -f "$depfile"
379 cat < "$tmpdepfile" > "$depfile"
380 tr ' ' '
381 ' < "$tmpdepfile" | \
382 ## Some versions of the HPUX 10.20 sed can't process this invocation
383 ## correctly. Breaking it into two sed invocations is a workaround.
384 sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
385 rm -f "$tmpdepfile"
386 ;;
387
388 dashXmstdout)
389 # This case only exists to satisfy depend.m4. It is never actually
390 # run, as this mode is specially recognized in the preamble.
391 exit 1
392 ;;
393
394 makedepend)
395 "$@" || exit $?
396 # Remove any Libtool call
397 if test "$libtool" = yes; then
398 while test $1 != '--mode=compile'; do
399 shift
400 done
401 shift
402 fi
403 # X makedepend
404 shift
405 cleared=no
406 for arg in "$@"; do
407 case $cleared in
408 no)
409 set ""; shift
410 cleared=yes ;;
411 esac
412 case "$arg" in
413 -D*|-I*)
414 set fnord "$@" "$arg"; shift ;;
415 # Strip any option that makedepend may not understand. Remove
416 # the object too, otherwise makedepend will parse it as a source file.
417 -*|$object)
418 ;;
419 *)
420 set fnord "$@" "$arg"; shift ;;
421 esac
422 done
423 obj_suffix="`echo $object | sed 's/^.*\././'`"
424 touch "$tmpdepfile"
425 ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
426 rm -f "$depfile"
427 cat < "$tmpdepfile" > "$depfile"
428 sed '1,2d' "$tmpdepfile" | tr ' ' '
429 ' | \
430 ## Some versions of the HPUX 10.20 sed can't process this invocation
431 ## correctly. Breaking it into two sed invocations is a workaround.
432 sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
433 rm -f "$tmpdepfile" "$tmpdepfile".bak
434 ;;
435
436 cpp)
437 # Important note: in order to support this mode, a compiler *must*
438 # always write the preprocessed file to stdout.
439 "$@" || exit $?
440
441 # Remove the call to Libtool.
442 if test "$libtool" = yes; then
443 while test $1 != '--mode=compile'; do
444 shift
445 done
446 shift
447 fi
448
449 # Remove `-o $object'.
450 IFS=" "
451 for arg
452 do
453 case $arg in
454 -o)
455 shift
456 ;;
457 $object)
458 shift
459 ;;
460 *)
461 set fnord "$@" "$arg"
462 shift # fnord
463 shift # $arg
464 ;;
465 esac
466 done
467
468 "$@" -E |
469 sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
470 sed '$ s: \\$::' > "$tmpdepfile"
471 rm -f "$depfile"
472 echo "$object : \\" > "$depfile"
473 cat < "$tmpdepfile" >> "$depfile"
474 sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
475 rm -f "$tmpdepfile"
476 ;;
477
478 msvisualcpp)
479 # Important note: in order to support this mode, a compiler *must*
480 # always write the preprocessed file to stdout, regardless of -o,
481 # because we must use -o when running libtool.
482 "$@" || exit $?
483 IFS=" "
484 for arg
485 do
486 case "$arg" in
487 "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
488 set fnord "$@"
489 shift
490 shift
491 ;;
492 *)
493 set fnord "$@" "$arg"
494 shift
495 shift
496 ;;
497 esac
498 done
499 "$@" -E |
500 sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
501 rm -f "$depfile"
502 echo "$object : \\" > "$depfile"
503 . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
504 echo " " >> "$depfile"
505 . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
506 rm -f "$tmpdepfile"
507 ;;
508
509 none)
510 exec "$@"
511 ;;
512
513 *)
514 echo "Unknown depmode $depmode" 1>&2
515 exit 1
516 ;;
517 esac
518
519 exit 0
520
521 # Local Variables:
522 # mode: shell-script
523 # sh-indentation: 2
524 # eval: (add-hook 'write-file-hooks 'time-stamp)
525 # time-stamp-start: "scriptversion="
526 # time-stamp-format: "%:y-%02m-%02d.%02H"
527 # time-stamp-end: "$"
528 # End:
0 # dillorc
1 # Sample dillo initialization file.
2 # Copy this file to ~/.dillo/dillorc and edit to your taste.
3 # Lines that start with a '#' are comments.
4
5
6 #-------------------------------------------------------------------------
7 # FIRST SECTION :)
8 #-------------------------------------------------------------------------
9
10 # Set the desired initial browser size
11 geometry=640x550
12
13 # Dicache is where the Decompressed Images are cached (not the original ones).
14 # If you have a lot of memory and a slow CPU, use YES, otherwise use NO
15 use_dicache=NO
16
17
18 #-------------------------------------------------------------------------
19 # RENDERING SECTION
20 #-------------------------------------------------------------------------
21
22 # Fontname for variable width rendering (most of the text).
23 # - some fonts may slow down rendering, some others not!
24 # - try to tune a fontname/font_factor combination.
25 # Ex. {helvetica, lucida, times, "new century schoolbook", utopia, ...}
26 vw_fontname=helvetica
27
28 # Fontname for fixed width rendering (mainly <pre> quoted text)
29 fw_fontname=courier
30
31 # All fontsizes are scaled by this value (default is 1.0)
32 #font_factor=1.2
33
34 # If you prefer oblique over italic fonts, uncoment next line
35 #use_oblique=YES
36
37 # Show tooltip popup for images?
38 # Note: We use the "title" attribute and not "alt".
39 # More info at: http://bugzilla.mozilla.org/show_bug.cgi?id=25537
40 show_tooltip=YES
41
42 # Set this to YES, if you want to limit the word wrap width to the vieport
43 # width (may be useful for iPAQ)
44 limit_text_width=NO
45
46
47 #-------------------------------------------------------------------------
48 # PARSING SECTION
49 #-------------------------------------------------------------------------
50
51 # If you prefer more accurate HTML bug diagnose, over better rendering
52 # (page authors and webmasters) set the following to "NO".
53 #
54 w3c_plus_heuristics=YES
55
56
57 #-------------------------------------------------------------------------
58 # NETWORK SECTION
59 #-------------------------------------------------------------------------
60
61 # Set the start page.
62 # Uncomment if you want to override the default start page.
63 #start_page="file:/home/user/custom.html"
64
65 # Set the home location
66 home="http://www.dillo.org/"
67
68 # Set search url to use with the search dialog.
69 # %s is replaced with urlencoded keywords, and %% by '%'.
70 search_url="http://www.google.com/search?q=%s"
71 #search_url="http://search.lycos.com/default.asp?query=%s"
72 #search_url="http://www.alltheweb.com/search?cat=web&query=%s"
73
74 # Set the proxy information for http
75 #http_proxy=http://localhost:8080/
76
77 # if you need to provide a user/password pair for the proxy,
78 # set the proxy user name here and Dillo will ask for the password later.
79 #http_proxyuser="joe"
80
81 # When using a proxy, this sets the domains to access without proxy.
82 # (separated with a single space -- see examples below)
83 #no_proxy = ".mynet.com"
84 #no_proxy = ".mynet.com .other.net .foo.bar.org"
85
86
87 #-------------------------------------------------------------------------
88 # COLORS SECTION
89 #-------------------------------------------------------------------------
90
91 # Here we can use the HTML (standard and extended) or C syntax.
92
93 # Set the background color
94 # bg_color=gray
95 # bg_color=0xd6d6c0
96 bg_color=0xdcd1ba
97
98 # Set the text color
99 text_color=black
100
101 # Set the link color
102 link_color=blue
103
104 # If your eyes suffer with white backgrounds, or you have headaches after
105 # lengthy computer sessions, and you don't need high contrast to see sharply,
106 # uncomment next line (it'll use 'bg_color' instead). -- It works!
107 #allow_white_bg=NO
108
109 # Use the same colors with all documents?
110 force_my_colors=NO
111
112 # When set to YES, visited links will always have a contrasting color,
113 # independent of the page author's setting.
114 contrast_visited_color=YES
115
116 #-------------------------------------------------------------------------
117 # USER INTERFACE SECTION
118 #-------------------------------------------------------------------------
119
120 # Size of dillo panel (used to enlarge the browsing area)
121 # tiny : recommended for iPAQ (with small_icons)
122 # small : very nice! (it's "medium" without icon titles)
123 # medium : nice!
124 # large : Traditional
125 panel_size=medium
126 small_icons=NO
127
128 # Here you can choose to hide some widgets of the dillo panel...
129 #show_back=NO
130 #show_forw=NO
131 #show_home=NO
132 #show_reload=NO
133 #show_save=NO
134 #show_stop=NO
135 #show_bookmarks=NO
136 #show_menubar=NO
137 #show_clear_url=NO
138 #show_url=NO
139 #show_search=NO
140 #show_progress_box=NO
141
142 # Start dillo windows with a hidden panel?
143 fullwindow_start=NO
144
145 # Enabling this will restrain OpenUrl and FindText, but may be required
146 # for the ION window manager.
147 transient_dialogs=NO
148
149 # When filling forms, our default behaviour is to submit on enterpress,
150 # but only when there's a single text entry (to avoid incomplete submits).
151 # OTOH, if you have to fill the same form lots of times, you may find
152 # useful to keep away from the mouse by forcing enter to submit.
153 enterpress_forces_submit=NO
154
155 # Some forms lack a submit button, and dillo can generate a custom one
156 # internally. Unfortunately there's no guarantee for it to work. :(
157 # (my experience is that forms that lack a submit rely on Javascript)
158 generate_submit=NO
159
160 #-------------------------------------------------------------------------
161 # DEBUG MESSAGES SECTION
162 #-------------------------------------------------------------------------
163
164 # Generic messsages (mainly for debugging specific parts)
165 # Uncomment the following line to disable them.
166 #show_msg=NO
167
168 # Soon we'll add the "show_debug_messages=NO" option...
169
170 #-------------------------------------------------------------------------
171 # HTML BUG MESSAGES SECTION
172 #-------------------------------------------------------------------------
173
174 # Accepted by the W3C validator but "strongly discouraged" by the SPEC.
175 # (As "TAB character inside <PRE>").
176 #show_extra_warnings=YES
177
178
179 # -----------------------------------------------------------------------
180 # dillorc ends here.
0 June 2000, --Jcid
1 Last update: Oct 2004
2
3 -------
4 CACHE
5 -------
6
7 The cache module is the main abstraction layer between
8 rendering and networking.
9
10 The capi module acts as a discriminating wrapper which either
11 calls the cache or the dpi routines depending on the type of
12 request.
13
14 Every URL must be requested using a_Capi_open_url, no matter
15 if it is a http, file, dpi or whatever type of request. The capi
16 asks the dpi module for dpi URLs and the Cache for everything
17 else.
18
19 Here we'll document non dpi requests.
20
21 The cache, at its turn, sends the requested-data from memory
22 (if cached), or opens a new network connection (if not cached).
23
24 This means that no mattering whether the answer comes from
25 memory or the net, the client requests it through the capi
26 wrapper, in a single uniform way.
27
28
29 ----------------
30 CACHE PHILOSOPHY
31 ----------------
32
33 Dillo's cache is very simple, every single resource that's
34 retrieved (URL) is kept in memory. NOTHING is saved. This is
35 mainly for three reasons:
36
37 - Dillo encourages personal privacy and it assures there'll be
38 no recorded tracks of the sites you visited.
39
40 - The Network is full of intermediate transparent proxys that
41 serve as caches.
42
43 - If you still want to have cached stuff, you can install an
44 external cache server (as WWWOFFLE), and benefit from it.
45
46
47 ---------------
48 CACHE STRUCTURE
49 ---------------
50
51 Currently, dillo's cache code is spread in different sources:
52 mainly in cache.[ch], dicache.[ch] and it uses some other
53 functions from mime.c, Url.c and web.c.
54
55 Cache.c is the principal source, and it also is the main
56 responsible for processing cache-clients (held in a queue).
57 Dicache.c is the "decompressed image cache" and it holds the
58 original data and its corresponding decompressed RGB
59 representation (more on this subject in Images.txt).
60
61 Url.c, mime.c and web.c are used for secondary tasks; as
62 assigning the right "viewer" or "decoder" for a given URL.
63
64
65 ----------------
66 A bit of history
67 ----------------
68
69 Some time ago, the cache functions, URL retrieving and
70 external protocols were a whole mess of mixed code, and it was
71 getting REALLY hard to fix, improve or extend the functionality.
72 The main idea of this "layering" is to make code-portions as
73 independent as possible so they can be understood, fixed,
74 improved or replaced without affecting the rest of the browser.
75
76 An interesting part of the process is that, as resources are
77 retrieved, the client (dillo in this case) doesn't know the
78 Content-Type of the resource at request-time. It only gets known
79 when the resource header is retrieved (think of http), and it
80 happens when the cache has the control so, the cache sets the
81 proper viewer for it! (unless the Callback function is specified
82 with the URL request).
83
84 You'll find a good example in http.c.
85
86 Note: Files don't have a header, but the file handler inside
87 dillo tries to determine the Content-Type and sends it back in
88 HTTP form!
89
90
91 -------------
92 Cache clients
93 -------------
94
95 Cache clients MUST use a_Cache_open_url to request an URL. The
96 client structure and the callback-function prototype are defined,
97 in cache.h, as follows:
98
99 struct _CacheClient {
100 gint Key; /* Primary Key for this client */
101 const char *Url; /* Pointer to a cache entry Url */
102 guchar *Buf; /* Pointer to cache-data */
103 guint BufSize; /* Valid size of cache-data */
104 CA_Callback_t Callback; /* Client function */
105 void *CbData; /* Client function data */
106 void *Web; /* Pointer to the Web structure of our client */
107 };
108
109 typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
110
111
112 Notes:
113
114 * Op is the operation that the callback is asked to perform
115 by the cache. { CA_Send | CA_Close | CA_Abort }.
116
117 * Client: The Client structure that originated the request.
118
119
120
121 --------------------------
122 Key-functions descriptions
123 --------------------------
124
125 ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии
126 int a_Cache_open_url(const char *Url, CA_Callback_t Call, void *CbData)
127
128 if Url is not cached
129 Create a cache-entry for that URL
130 Send client to cache queue
131 Initiate a new connection
132 else
133 Feed our client with cached data
134
135 ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии
136 ChainFunction_t a_Url_get_ccc_funct(const char *Url)
137
138 Scan the Url handlers for a handler that matches
139 If found
140 Return the CCC function for it
141 else
142 Return NULL
143
144 * Ex: If Url is an http request, a_Http_ccc is the matching
145 handler.
146
147 ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии
148
149 ----------------------
150 Redirections mechanism
151 (HTTP 30x answers)
152 ----------------------
153
154 This is by no means complete. It's a work in progress.
155
156 Whenever an URL is served under an HTTP 30x header, its cache
157 entry is flagged with 'CA_Redirect'. If it's a 301 answer, the
158 additional 'CA_ForceRedirect' flag is also set, if it's a 302
159 answer, 'CA_TempRedirect' is also set (this happens inside the
160 Cache_parse_header() function).
161
162 Later on, in Cache_process_queue(), when the entry is flagged
163 with 'CA_Redirect' Cache_redirect() is called.
164
165
166
167
168
169
170
171 -----------
172 Notes
173 -----------
174
175 The whole process is asynchronous and very complex. I'll try
176 to document it in more detail later (source is commented).
177 Currently I have a drawing to understand it; hope the ASCII
178 translation serves the same as the original.
179 If you're planning to understand the cache process troughly,
180 write me a note, just to assign a higher priority on further
181 improving of this doc.
182 Hope this helps!
183
184
0 Jan 2002, Jörgen Viksell - jorgen.viksell@telia.com,
1 Jorge Arellano Cid --
2 Last update: April 2005, DarkSpirit
3
4
5 ==================
6 Cookies in Dillo
7 ==================
8
9 The cookie support in Dillo aims to support cookies of the old
10 original Netscape style, as well as the kind specified in RFC 2109.
11
12 Cookies are managed outside of dillo by the cookies.dpi. It uses
13 a cookies file in netscape format so it can be used with other
14 programs like for example wget.
15
16 Between sessions cookies are saved to ~/.dillo/cookies.txt, the
17 old ~/.dillo/cookies is read too but not updated. At the moment
18 the only enforcements on the amount of cookies to save to disk is
19 max 20 per domain.
20
21 There's also a file for controlling cookies: ~/.dillo/cookiesrc.
22 Dillo initially sets it to ignore (reject) all cookies, so if you
23 want to use cookies, change it to meet your needs.
24
25 If you don't want cookies at all, you have two options:
26
27 1.- Delete ~/.dillo/cookiesrc (or leave it just as dillo creates it).
28 2. Configure Dillo with ./configure --disable-cookies. Then all the
29 cookie stuff will be skipped at compilation.
30
31 Note: "--disable-cookies" absolutely eliminates cookie support,
32 no matter what "cookiesrc" says.
33
34
35
36 =====================
37 Controlling cookies
38 =====================
39
40 There is a small and simple way to restrict urls from setting cookies
41 in Dillo. In the file ~/.dillo/cookiesrc You may specify rules
42 for different domains. The syntax looks something like this:
43
44 DEFAULT DENY
45 slashdot.org ACCEPT
46 .host.com ACCEPT_SESSION
47
48 The first line says that we should deny all cookies from all domains
49 by default.
50 The second one tells Dillo to save all cookies from slashdot.org
51 across sessions, until it expires.
52 And finally, the third says that all subdomains of host.com should be
53 allowed to set cookies. But these cookies will only be saved in
54 memory until you exit.
55
56
57 ===================
58 Cookies & Privacy
59 ===================
60
61 Cookies can be a severe threat to personal privacy. The pages you
62 visit can be tracked, logged, and associated to a peronal data-record,
63 allowing the possibility of building a detailed profile of your
64 browsing habits.
65
66 This data is sold to companies that profit from direct use of such
67 information (SPAM, Spying, etc).
68
69 If this data is cross-referenced with other databases, they can end up
70 with more information than you have about yourself.
71
72 Some people may tell you this is "paranoid". But please, take my words
73 as those of someone that has written a web browser, a cookies implementation,
74 and that has deep understanding of HTTP (RFC-2068) and cookies (RFC-2965).
75
76 Non technical persons may like to read:
77 http://www.junkbusters.com/cookies.html
78 http://www.newsfactor.com/perl/story/16455.html (about user-spying)
79
80 The dillo project is especially concerned about privacy and security
81 issues. Our advice is to avoid cookies whenever possible and at most set
82 ACCEPT_SESSION to specific, trusted sites. -- You have been warned.
83
84
85 =========================
86 DPI-Dillo comunications
87 =========================
88
89 The cookies.dpi has the state of the cookies and is the only one
90 that reads and writes to the cookies.txt file. The different
91 running dillos must ask and send cookies to it.
92
93 To minimize communications between the dpi and dillo clients,
94 every different instance of dillo reads 'cookiesrc' and only ask
95 and send cookies for allowed sites.
96
97 The cookies.dpi also needs to read 'cookiesrc'. If a site is
98 changed to deny cookies in 'cookiesrc', the cookies dpi can
99 delete the cookies for that site from the 'cookies.txt' file the
100 next time it loads and writes that file.
101
102 All the work is implemented adding only three new dpi commands,
103 two really, and a new send bloking dpi command function.
104
105 When dillo wants the cookies for a certain site, it sends the
106 'get_cookie' dpi command to the cookies.dpi, and the dpi sends
107 the answer inside a 'get_cookies_answer' dpi command.
108
109 If an allowed site sends a cookie, dillo uses the 'set_cookies'
110 dpi command to let the cookies dpi store it.
111
112
113 ==============
114 Restrictions
115 ==============
116
117 Use "dpidc stop" before making changes to cookies.txt or any
118 cookie files or you can lose your changes when the running dpi
119 rewrites them. After the cookies dpi reloads the file all dillos
120 will use the new one.
121
122 If you change the 'cookiesrc' (previously calling "dpidc stop")
123 only newly opened dillos will use the changes.
124
125
126 Thats all folks!
0 "Eliminate the guesswork and quality goes up."
1
2
3 -------
4 DILLO
5 -------
6
7 These notes are written with a view to make it less hard, not
8 easier yet ;), to get into Dillo development.
9 When I first got into it, I was totally unaware of the browser
10 internals. Now that I've made my way deep into the core of it,
11 (we rewrote it 90% and modified the rest), is time to write some
12 documentation, just to make a less steep learning curve for new
13 developers.
14
15 --Jcid
16
17
18
19 --------
20 OVERVIEW
21 --------
22
23 Dillo can be viewed as the sum of five main parts:
24
25 1.- Dillo Widget: A custom widget, gtk+ based, that holds the
26 neccesary data structures and mechanisms for graphical rendering.
27 (Described in Dw*.txt, dw*.c files among the sources.)
28
29 2.- Dillo Cache: Integrated with a signal driven Input/Output
30 engine that handles file descriptor activity, the cache acts as
31 the main abstraction layer between rendering and networking.
32 Every URL, whether cached or not, must be retrieved using
33 a_Cache_open_url (Described briefly in Cache.txt, source
34 contained in cache.c).
35 IO is described in IO.txt (recommended), source in IO/.
36
37 3.- The HTML parser: A streamed parser that joins the Dillo
38 Widget and the Cache functionality to make browsing possible
39 (Described in HtmlParser.txt, source mainly inside html.c).
40
41 4.- Image processing code: The part that handles image
42 retrieving, decoding, caching and displaying. (Described in
43 Images.txt. Sources: image.c, dw_image.c, dicache.c, gif.c,
44 jpeg.c and png.c)
45
46 5.- The dpi framework: a gateway to interface the browser with
47 external programs (Example: the bookmarks server plugin).
48 Dpi spec: http://www.dillo.org/dpi1.html
49
50
51 -------------------------
52 HOW IS THE PAGE RENDERED?
53 -------------------------
54
55 (A short description of the internal function calling process)
56
57 When the user requests a new URL, a_Interface_entry_open_url
58 is queried to do the job; it calls a_Nav_push (The highest level
59 URL dispatcher); a_Nav_push updates current browsing history and
60 calls Nav_open_url. Nav_open_url closes all open connections by
61 calling a_Interface_stop and a_Interface_stop, and then calls
62 a_Capi_open_url wich calls a_Cache_open_url (or the dpi module if
63 this gateway is used).
64
65 If Cache_search hits (due to a cached url :), the client is
66 fed with cached data, but if the URL isn't cached yet, a new CCC
67 (Concomitant Control Chain) is created and commited to fetch the
68 URL. Note that a_Cache_open_url will return the requested URL,
69 whether cached or not.
70
71 The next CCC link is dynamically assigned by examining the
72 URL's protocol. It can be:
73
74 a_Http_ccc
75 a_File_ccc
76 a_About_ccc
77 a_Plugin_ccc (not implemented yet)
78
79
80 If we have a HTTP URL, a_Http_ccc will succeed, and the http
81 module will be linked; it will create the proper HTTP query and
82 link the IO module to submit and deliver the answer.
83
84 Note that as the Content-type of the URL is not always known
85 in advance, the answering branch decides where to dispatch it to
86 upon HTTP header arrival.
87
88
89 What happens then?
90
91 Well, the html parser gets fed, and proper functions are
92 called for each tag (to parse and call the appropriate methods)
93 and the whole page is contructed in a streamed way.
94 Somewhere in the middle of it, resize and repaint functions
95 are activated and idle functions draw to screen what has been
96 processed.
97
98 (The process for images is described in Images.txt)
99
100
101
102
0 Aug 2003, Jorge Arellano Cid,
1 Ferdi Franceschini --
2 Last update: Dec 2004
3
4
5 ------
6 dpid
7 ------
8
9 -------------
10 Nomenclature:
11 -------------
12
13 dpi:
14 generic term referring to dillo's plugin system (version1).
15
16 dpi1:
17 specific term for dillo's plugin spec version 1.
18 at: http://www.dillo.org/dpi1.html
19
20 dpi program:
21 any plugin program itself.
22
23 dpi framework:
24 the code base inside and outside dillo that makes dpi1
25 working possible (it doesn't include dpi programs).
26
27 dpip:
28 dillo plugin protocol. The HTML/XML like set of command tags
29 and information that goes inside the communication sockets.
30 Note: not yet fully defined, but functional.
31 Note2: it was designed to be extensible.
32
33 dpid:
34 dillo plugin daemon.
35
36 server plugin:
37 A plugin that is capable of accepting connections on a socket. Dpid will
38 never run more than one instance of a server plugin at a time.
39
40 filter plugin:
41 Any program/script that can read or write to stdio. If you can write a
42 shell script you can write one of these (see examples at the end).
43 Warning, dpid will run multiple instances of filter plugins if requested.
44 This is safe if the plugin only writes to stdout which is what the filter
45 type dpis do at the moment.
46
47 -----------
48 About dpid:
49 -----------
50
51 * dpid is a program which manages dpi connections.
52 * dpid is a daemon that serves dillo using unix domain
53 sockets (UDS).
54 * dpid launches dpi programs and arranges socket communication
55 between the dpi program and dillo.
56
57 The concept and motivation is similar to that of inetd. The
58 plugin manager (dpid) listens for a service request on a Unix
59 domain socket and returns the socket name of a plugin that
60 handles the service. It also watches sockets of inactive plugins
61 and starts them when a connection is requested.
62
63
64 -----------------------------------------------------------
65 What's the problem with managing dpi programs inside dillo?
66 -----------------------------------------------------------
67
68 That's the other way to handle it, but it started to show some
69 problems (briefly outlined here):
70
71 * When having two or more running instances of Dillo, one
72 should prevail, and take control of dpi managing (but all
73 dillos carry the managing code).
74 * If the managing dillo exits, it must pass control to another
75 instance, or leave it void if there's no other dillo running!
76 * The need to synchronise all the running instances of
77 dillo arises.
78 * If the controlling instance finishes and quits, all the
79 dpi-program PIDs are lost.
80 * Terminating hanged dpis is hard if it's not done with signals
81 (PIDs)
82 * Forks can be expensive (Dillo had to fork its dpis).
83 * When a managing dillo exits, the new one is no longer the
84 parent of the forked dpis.
85 * If the Unix domain sockets for the dpis were to be named
86 randomly, it gets very hard to recover their names if the
87 controlling instance of dillo exits and another must "take
88 over" the managing.
89 * It increments dillo's core size.
90 * ...
91
92 That's why the managing daemon scheme was chosen.
93
94
95 ----------------------
96 What does dpid handle?
97 ----------------------
98
99 It solves all the above mentioned shortcomings and also can do:
100
101 * Multiple dillos:
102 dpid can communicate and serve more than one instance
103 of dillo.
104
105 * Multiple dillo windows:
106 two or more windows of the same dillo instance accessing dpis
107 at the same time.
108
109 * Different implementations of the same service
110 dpi programs ("dpis") are just an implementation of a
111 service. There's no problem in having more than one for the
112 same service.
113
114 * Upgrading a service:
115 to a new version or implementation without requiring
116 bringing down the dpid or patching dillo's core.
117
118
119 And finally, being aware that this design can support the
120 following functions is very helpful:
121
122 SCHEME Example
123 ------------------------------------------------------------
124 * "one demand/one response" man, preferences, ...
125 * "resident while working" downloads, mp3, ...
126 * "resident until TERM signal" bookmarks, ...
127
128 * "one client only" cd burner, ...
129 * "one client per instance" man, ...
130 * "multiple clients/one instance" downloads, cookies ...
131
132
133 --------
134 Features
135 --------
136 * Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries
137 are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in
138 "hello.filter.dpi". The ".filter" plugins simply read and write to stdio
139 and can be implemented with a shell script easily.
140 * Register/update/remove dpis from list of available dpis when a
141 <dpi cmd='register_all'> is received.
142 * dpid terminates when it receives a <dpi cmd='DpiBye'> command.
143 * dpis can be terminated with a <dpi cmd='DpiBye'> command.
144 * dpidc control program for dpid, currently allows register and stop.
145
146
147 -----
148 todo:
149 -----
150
151 These features are already designed, waiting for implementation:
152
153 * How to register/update/remove/ individual dpis?
154 * How to kill dpis? (signals)
155
156 How:
157
158 A useful and flexible way is to have a "control program" for
159 dpid (it avoids having to find its PID among others).
160
161 Let's say:
162
163 dpidc [register | upgrade | stop | ...]
164
165 It can talk to a dpid UDS that serves for that (the same that
166 dillo would use). That way we may also have a dpidc dpi! :-)
167
168 Seriously, what I like from this approach is that it is very
169 flexible and can be implemented incrementally ("dpidc register"
170 is enough to start).
171
172 It also avoids the burden of having to periodically check the
173 dpis directory structure for changes).
174
175 It also lets shell scripts an easy way to do the "dirty" work
176 of installing dpis; as is required with distros' package
177 systems.
178
179 <note>
180 How do we tell a crashed dpi? That's the question.
181 We're thinking about using the "lease" concept (as in JINI).
182 </note>
183
184
185 -----------------
186 How does it work?
187 -----------------
188
189 o on startup dpid reads dpidrc for the path to the dpi directory
190 (usually EPREFIX/lib/dillo/dpi). ~/.dillo/dpi is scanned first.
191
192 o both directories are scanned for the list of available plugins.
193 ~/.dillo/dpi overrides system-wide dpis.
194
195 o ~/.dillo/dpi_socket_dir is then checked for the name of the dpi socket
196 directory, if dpi_socket_dir does not exist it will be created.
197
198 o next it creates Unix domain sockets for the available plugins and
199 then listens for service requests on its own socket (dpid.srs)
200 and for connections to the sockets of inactive plugins.
201
202 o dpid returns the name of a plugin's socket when a client (dillo)
203 requests a service.
204
205 o if the requested plugin is a 'server' then
206 1) dpid stops watching the socket for activity
207 2) forks and starts the plugin
208 3) resumes watching the socket when the plugin exits
209
210 o if the requested plugin is a 'filter' then
211 1) dpid accepts the connection
212 2) duplicates the connection on stdio
213 3) forks and starts the plugin
214 4) continues to watch the socket for new connections
215
216
217
218
219 ---------------------------
220 dpi service process diagram
221 ---------------------------
222
223 These drawings should be worth a thousand words! :)
224
225
226 (I)
227 .--- s1 s2 s3 ... sn
228 |
229 [dpid] [dillo]
230 |
231 '--- srs
232
233 The dpid is running listening on several sockets.
234
235
236 (II)
237 .--- s1 s2 s3 ... sn
238 |
239 [dpid] [dillo]
240 | |
241 '--- srs ------------------'
242
243 dillo needs a service so it connects to the service request
244 socket of the dpid (srs) and asks for the socket name of the
245 required plugin (using dpip).
246
247
248 (III)
249 .--- s1 s2 s3 ... sn
250 | |
251 [dpid] | [dillo]
252 | | |
253 '--- srs '---------------'
254
255 then it connects to that socket (s3, still serviced by dpid!)
256
257
258 (IV)
259 .--- s1 s2 s3 ... sn
260 | |
261 .[dpid] | [dillo]
262 . | | |
263 . '--- srs '---------------'
264 .
265 .............[dpi program]
266
267 when s3 has activity (incoming data), dpid forks the dpi
268 program for it...
269
270
271 (V)
272 .--- s1 s2 (s3) ... sn
273 |
274 [dpid] [dillo]
275 | |
276 '--- srs .---------------'
277 |
278 [dpi program]
279
280 ... and lets it "to take over" the socket.
281
282 Once there's a socket channel for dpi and dillo, the whole
283 communication process takes place until the task is done. When
284 the dpi program exits, dpid resumes listening on the socket (s3).
285
286
287 -----------------------------------------------
288 How are the unix-domain-sockets for dpis named?
289 -----------------------------------------------
290
291 Let's say we have two users, "fred" and "joe".
292
293 When Fred's dillo starts its dpid, the dpid creates the
294 following directory (rwx------):
295
296 /tmp/fred-XXXXXX
297
298 using mkdtemp().
299
300 and saves that filename within "~/.dillo/dpi_socket_dir".
301
302 That way, another dillo instance of user Fred can easily find
303 the running dpid's service request socket at:
304
305 /tmp/fred-XXXXXX/dpid.srs
306
307 (because it is saved in "~/.dillo/dpi_socket_dir").
308
309 Now, we have a dpi directory per user, and its permissions are
310 locked so only the user has access, thus the following directory
311 tree structure should pose no problems:
312
313 /tmp/fred-XXXXXX/bookmarks
314 /downloads
315 /cookies
316 /ftp
317 ...
318 dpid.srs
319
320 If user Joe starts his dillo, the same happens for him:
321
322 /tmp/joe-XXXXXX/bookmarks
323 /downloads
324 /cookies
325 /ftp
326 ...
327 dpid.srs
328
329
330 What should dpid do at start time:
331
332 Check if both, ~/.dillo/dpi_socket_dir and its directory, exist
333 (it can also check the ownership and permissions).
334
335 If (both exist)
336 use them!
337 else
338 delete ~/.dillo/dpi_socket_dir
339 create another /tmp/<user>-XXXXXX directory
340 save the new directory name into ~/.dillo/dpi_socket_dir
341 (we could also add some paranoid level checks)
342
343 To some degree, this scheme solves the tmpnam issue, different
344 users of dillo at the same time, multiple dillo instances,
345 polluting /tmp (cosmetic), and reasonably accounts for an
346 eventual dillo or dpid crash.
347
348 It has worked very well so far!
349
350
351 --------------------------------
352 So, how do I make my own plugin?
353 --------------------------------
354
355 First, at least, read the "Developing a dillo plugin" section
356 of dpi1 spec! :-)
357
358 Note that the dpi1 spec may not be absolutely accurate, but the
359 main ideas remain.
360
361 Once you've got the concepts, contrast them with the drawings
362 in this document. Once it all makes sense, start playing with
363 hello.dpi, you can run it by starting dillo with
364 dillo dpi:/hello/
365 or entering
366 dpi:/hello/
367 as the url. Then try to understand how it works (use the drawings)
368 and finally look at its code.
369
370 Really, the order is not that important, what really matters is
371 to do it all.
372
373 Start modifying hello.dpi, and then some more. When you feel
374 like trying new things, review the code of the other plugins for
375 ideas.
376
377 The hardest part is to try to modify the dpi framework code
378 inside dillo; you have been warned! It already supports a lot of
379 functionality, but if you need to do some very custom stuff, try
380 extending the "chat" command.
381
382
383 ---------------------------------
384 Examples: Simple 'filter' plugins
385 ---------------------------------
386
387 For a quick and dirty introduction to dpis try the following shell scripts.
388
389 #!/bin/sh
390
391 read -d'>' dpip_tag # Read dillo's request
392
393 # Don't forget the empty line after the Content-type
394 cat <<EOF
395 <dpi cmd='start_send_page' url='dpi:/hi/hi.filter.dpi'>
396 Content-type: text
397
398 EOF
399
400 echo Hi
401
402 Of course you should use html in a real application (perl makes this easy).
403
404 A more useful example uses the "si" system info viewer:
405
406 #!/bin/sh
407 # si - System Information Viewer
408
409 read -d'>' dpip_tag
410
411 # We don't need to send the Content-type because "si --html" does this
412 # for us.
413 cat <<EOF
414 <dpi cmd='start_send_page' url='dpi:/si/si.dpi.filter'>
415 EOF
416
417 si --html
418
419 just make sure that you have si installed or you wont get far.
420
421 To try out the examples create two directories for the scripts under your home directory as follows:
422 mkdir -p ~/.dillo/dpi/hi
423 mkdir -p ~/.dillo/dpi/si
424
425 then create the scripts and put them in the dpi service directories so that you end up with
426 ~/.dillo/dpi/hi/hi.filter.dpi
427 ~/.dillo/dpi/si/si.filter.dpi
428
429 Don't forget to make them executable.
430
431 If dpid is already running register the new plugins with
432 dpidc register
433
434 You can now test them by entering
435 dpi:/hi/
436 or
437 dpi:/si/
438 as the url. Or simply passing the url to dillo on startup
439
440 dillo dpi:/si/
441
442
443 You can edit the files in place while dpid is running and reload them in
444 dillo to see the result, however if you change the file name or add a new
445 script you must run 'dpidc register'.
446
447 WARNING
448 Multiple instances of a filter plugin may be run concurrently, this could be a
449 problem if your plugin records data in a file, however it is safe if you simply
450 write to stdout. Alternatively you could write a 'server' plugin instead as
451 they are guaranteed not to run concurrently.
452 >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<
453
0 Jan 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 ================
4 Dw: Dillo Widget
5 ================
6
7 Dw is mainly the module for rendering HTML. It provides a framework
8 for widgets, based on the Gtk+ object framework, and is very similar
9 to Gtk+, so that experiences in using and extending Gtk+ help very
10 much in understanding Dw. There is some documentation at www.gtk.org
11 (and probably on your local harddisk, somewhere in /usr/doc/*gtk*),
12 you should especially have read the chapter "Writing Your Own
13 Widgets" in the tutorial.
14
15
16 Why Not Gtk+?
17 =============
18
19 There are two reasons for designing a new model instead of simply
20 using Gtk+ objects:
21
22 1. Most important, Gtk+ widgets are limited in size, because X
23 windows are so.
24 2. There are a few extensions which are due to the different needs
25 for HTML rendering compared to GUI's. (Of course, this could
26 have been solved by defining a new object derived from
27 GtkWidget.)
28
29
30 Notes On Naming
31 ===============
32
33 According to the naming standards, functions beginning with "a_Dw_"
34 may be used outside of Dw, while, as an extention, functions used
35 within Dw (e.g. p_Dw_widget_queue_resize) are prefixed with "p_Dw_".
36 [todo: This could be included in NC_design.txt.]
37
38 Non-static functions beginning with "Dw_" are only used between
39 GtkDwViewport and DwWidget (e.g. Dw_gtk_viewport_remove_dw), they
40 belong to the core of Dw. And, of course, functions only used within a
41 sub-module (e.g. a specific widget) start with "Dw_" and are static
42 (e.g. Dw_page_find_line_index).
43
44 Dw widgets and some other structures have the prefix "Dw", while Gtk+
45 widgets in Dw have the prefix "GtkDw", but functions of them begin
46 with "Dw_gtk_" or "a_Dw_gtk", respectively.
47
48
49 Basic Overview
50 ==============
51
52 Dw widgets are objects derived from DwWidget, which itself derives
53 from GtkObject. DwWidget is quite similar to GtkWidget, the main
54 difference is that Dw widgets are always windowless and that they are
55 presented in a viewport, so there is no need to limit the size. Much
56 of the functionality normally provided by the X server is simulated
57 by Dw.
58
59 The interface between Gtk+ and Dw is the Gtk+ widget GtkDwViewport,
60 which contains (at most) one top-level Dw widget.
61
62 A Few Definitions:
63
64 - world coordinates: coordinates relative to the upper left corner
65 of the whole scrolled area ("the world")
66 - viewport coordinates: coordinates relative to the upper left
67 corner of the visible area
68 - widget coordinates: coordinates relative to the upper left corner
69 of the widget
70
71 Dw widgets draw into the viewport window, and must adhere to
72 *viewport coordinates*: the "world" is only an abstract term, there
73 is no window for it. When GtkDwViewport processes expose events, they
74 are automatically delivered to the Dw widgets. Redrawing requests due
75 to scrolling of the viewport is done by the base object GtkLayout,
76 you will not find any code for this in Dw.
77
78 Mouse events also contain viewport coordinates. Dw will try to find
79 the right Dw widget to deliver the event to.
80
81 Resizing the GtkDwViewport will not resize the top-level Dw widget,
82 but the latter will get some hints, so that, e.g., the page widget
83 rewraps the lines at the appropriate width.
84
85 See DwWidget.txt for more details.
86
87
88 Embedding Gtk+ Widgets In Dw
89 ----------------------------
90 Dw Widgets may embed Gtk+ widgets, this is done by the Dw widget
91 DwEmbedGtk. For Gtk+, these embedded Gtk+ widgets are themselves
92 children of the GtkDwViewport, since Gtk+ does not care about Dw.
93
94 Of course, embedded Gtk+ widgets are again limited in size, but but
95 in position: GtkDwViewport is derived from GtkLayout which is exactly
96 designed for positioning widgets in an infinite scrolled area.
97
98
99 How To Get The Top-Level Dw Widget From A BrowserWindow
100 -------------------------------------------------------
101 The member "docwin" of BrowserWindow points on a GtkDwScrolledWindow,
102 which contains a GtkDwScrolledFrame, which contains a GtkDwViewport.
103 The member "child" of the latter points on the top-level Dw widget,
104 or may be NULL. The top-level Dw is (currently) a DwPage (HTML and
105 plain text documents) or a DwImage (images).
106
107 There is a function a_Dw_gtk_scrolled_window_get_dw for this.
108
109
110 Sizes
111 -----
112 A feature adapted from the old Dw are baselines. As well DwAllocation
113 as DwRequisition do not have a height member, but instead ascent and
114 descent, both positive or zero. (Originally I removed this, but there
115 will be a few widgets in future depending on this, e.g., math
116 formulas.)
117
118 Unlike in Gtk, sizes of zero are allowed. The upper limit for the
119 size of a widget is 2^31 (this will be enough to show the contents of
120 a small library in a web page).
121
122
123 Resizing
124 ========
125
126 From outside: When writing a new widget, you should implement the
127 signal "size_request". When the widget changes its size, it should
128 call p_Dw_widget_queue_resize, as in a_Dw_image_size. See "Incremental
129 Resizing" below for a way to increase the speed.
130
131 Even if the implementation of "size_request" gets quite expensive,
132 you do not have to check whether the size has changed, this is done
133 by a_Dw_widget_size_request.
134
135 Inside: q_Dw_widget_queue_resize will set the DW_NEEDS_RESIZE flag, a
136 further call of a_Dw_widget_size_request will only then emit the
137 "size_request" signal. Furthermore, mark_size_change and
138 mark_extremes_change are called (see below). After that, the resizing
139 is done in an idle loop, this prevents too many size requests. The
140 algorithm is quite simple: any widget with a child which needs
141 resizing, needs resizing, thus all parents up to top-level widget are
142 marked.
143
144 Incremental Resizing
145 ---------------------
146 A widget may calculate its size based on size calculations already
147 done before. In this case, a widget must exactly know the reasons, why
148 a call of size_request is necessary. To make use of this, a widget
149 must implement the following:
150
151 1. There is a member in DwWidget, called parent_ref, which is
152 totally under control of the parent widget (and so sometimes not
153 used at all). It is necessary to define how parent_ref is used
154 by a specific parent widget, and it has to be set to the correct
155 value whenever necessary.
156
157 2. The widget must implement mark_size_change and
158 mark_extremes_change, these methods are called in two cases:
159
160 a) directly after q_Dw_widget_queue_resize, with the argument
161 ref was passed to q_Dw_widget_queue_resize, and
162 b) if a child widget has called q_Dw_widget_queue_resize,
163 with the value of the parent_ref member of this child.
164
165 This way, a widget can exactly keep track on size changes, and so
166 implement resizing in a faster way. A good example on how to use this
167 is the DwPage widget, see DwPage.txt for details.
168
169
170 Anchors and Scrolling
171 =====================
172
173 Anchors
174 -------
175 todo: This section is out of sync with the actual code.
176
177 To set the anchor a page is viewed at, you can use one of the
178 following functions:
179
180 - void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport,
181 gchar *anchor)
182
183 Scroll directly to an anchor. The anchor does not need to exist
184 already, see below.
185
186 - void a_Dw_gtk_viewport_queue_anchor (GtkDwViewport *viewport,
187 gchar *anchor)
188
189 Set the anchor for the next top-level DwWidget (the next call
190 of a_Dw_gtk_viewport_add_dw).
191
192 There are wrappers, a_Dw_gtk_scrolled_window_queue_anchor and
193 a_Dw_gtk_scrolled_window_set_anchor.
194
195 After a_Dw_gtk_viewport_set_anchor has been called (indirectly by
196 Nav_open_url, or by a_Dw_gtk_viewport_add_dw), changes of anchor
197 positions (e.g., if widgets change there size or the anchor was not
198 known before) will correct the viewport adjustment (in function
199 p_Dw_gtk_viewport_update_anchor), but only as long as the user did not
200 change it directly. Look at Dw_gtk_scrolled_window_init for details
201 about the latter.
202
203 Use p_Dw_widget_set_anchor to add anchors to a widget, see
204 DwWidget.txt.
205
206 Scrolling
207 ---------
208 Here is an overview on more functions for scrolling:
209
210 - To scroll to a given position, there are two possibilities:
211 a_Dw_gtk_viewport_set_scrolling_position simply scrolls to this
212 position, while Dw_gtk_viewport_scroll_to has more facilities:
213 you specify a rectangle you want to see, and the way how it is
214 seen (at the border, centered, or just scroll as much as
215 necessary, that it is seen). If you have a widget, you can also
216 use Dw_widget_scroll_to. There is also a wrapper for
217 GtkDwScrolledWindow,
218 a_Dw_gtk_scrolled_window_set_scrolling_position, and two
219 functions for getting the position,
220 a_Dw_gtk_scrolled_window_get_scrolling_position_x, and
221 a_Dw_gtk_scrolled_window_get_scrolling_position_y.
222
223 - If you have a region, and want to display it, use
224 a_Dw_iterator_scroll_to. For example, the findtext module makes
225 use of it. There are equivalents for DwExtIterator and
226 DwWordIterator. See comments on and in the function for more
227 informations.
228
229 - If you just want to determine where some content is allocated,
230 represented by an iterator, you can use
231 a_Dw_iterator_get_allocation. There are equivalents for
232 DwExtIterator and DwWordIterator.
233
234
235 The Objects
236 ===========
237
238 This is the hierarchy of all objects of Dw:
239
240 (GtkObject)
241 +-DwWidget
242 | +----DwBullet
243 | +----DwContainer
244 | | `----DwPage
245 | +----DwEmbedGtk
246 | +----DwHruler
247 | `----DwImage
248 `----(GtkWidget)
249 `----(GtkContainer)
250 +----(GtkBin)
251 | +----(GtkScrolledWindow)
252 | | `----GtkDwScrolledWindow
253 | `----GtkDwScrolledFrame
254 `----(GtkLayout)
255 `----GtkDwViewport
256
257 Objects in parentheses are part of Gtk+, not of Dw.
258
259
260 DwBullet
261 --------
262 Simple widget used for unnumbered list (<ul>).
263
264
265 DwContainer
266 -----------
267 The base object for Dw widgets which contain other Dw widgets. As in
268 Gtk+, containers are responsible for storing the children, there is
269 no common data structure. There are a few signals:
270
271 - void add (DwContainer *container,
272 DwWidget *child);
273
274 Currently not used, but may be in future.
275
276 - void remove (DwContainer *container,
277 DwWidget *widget);
278
279 *Recognize* that a widget is destroyed, i.e., an implementation
280 should remove *the pointer* from the list or so, but not
281 destroy the child widget. It is called by Dw_widget_shutdown.
282
283 - void forall (DwContainer *container,
284 DwCallback callback,
285 gpointer callback_data);
286
287 Process callback for all children, in the form
288 (*callback)(child, callback_data).
289
290 The include_internals of the Gtk+ equivalent was not adapted,
291 since it is used for more sophisticated purposes not needed in
292 Dw.
293
294
295 DwEmbedGtk
296 ----------
297 This Dw widget is used to embed Gtk+ widgets into Dw container
298 widgets. The Gtk+ widget is set by a_Dw_embed_gtk_add_gtk, and can
299 simply be removed by destroying it.
300
301 If the DwEmbedGtk contains no Gtk+ widget, it always returns 0x0x0 as
302 size, so, for speed reasons, first add the Gtk+ widget into the
303 DwEmbedGtk, and then the DwEmbedGtk into the other Dw widget, as at
304 the end of Html_tag_open_input.
305
306
307 DwHruler
308 --------
309 Simple widget used for the <hr> tag.
310
311
312 DwImage
313 -------
314 Widget for displaying image. See DwImage.txt for details.
315
316
317 DwPage
318 ------
319 A widget for displaying texts. See DwPage.txt for details.
320
321
322 DwTable
323 -------
324 A container widget for rendering tables. See DwTable.txt for details.
325
326
327 DwWidget
328 --------
329 The base object for all Dw widgets. See DwWidget.txt for details.
330
331
332 GtkDwScrolledWindow
333 -------------------
334 Adds a few functionalities to GtkScrolledWindow: it creates the
335 GtkDwScrolledFrame and the GtkDwViewport, connects some signals, and
336 provides some wrappers for using the GtkDwViewport.
337
338
339 GtkDwScrolledFrame
340 ------------------
341 General purpose scrolled widget containing another scrolled widget,
342 adding a border and a focus frame. Furthermore, it processes key
343 presses and mouse drags (button 2, as in Gimp) to move the viewport.
344
345 There are two signals (except "set_scroll_adjustments"),
346 "user_hchanged" and "user_vchanged", which are emitted when the user
347 changed the viewport adjustments horizontally/vertically by using the
348 keys or button 2 dragging.
349
350
351 GtkDwViewport
352 -------------
353 The interface between Gtk+ and Dw. It is responsible for displaying
354 Dw Widgets and processing their events. It is derived from GtkLayout,
355 to make embedding Gtk+ widgets into Dw widgets simpler, see the
356 documentation of GtkLayout in the Gtk+ tutorial for details.
357
358 GtkDwViewport contains at most one top-level Dw Widget, if it exists.
359 The Gtk+ methods of GtkDwViewport are more or less mapped on the
360 methods of the DwWidget. In detail:
361
362 - Dw_gtk_viewport_size_allocate will call a_Dw_widget_set_width,
363 a_Dw_widget_set_ascent (with allocation->height) and
364 a_Dw_widget_set_descent (with zero as argument), and then allocate
365 the Dw widget at the size returned by a_Dw_widget_size_request.
366
367 - Dw_gtk_viewport_draw and Dw_gtk_viewport_expose will call
368 a_Dw_widget_draw, which will emit the "draw" signal.
369
370 - Handling of mouse events is mostly done in Dw_widget_mouse_event,
371 see DwWidget.txt for details. Note that the functions return
372 FALSE, if the event was not processed, so that they are delivered
373 to the parent widget(s) of the GtkDwViewport, this scheme e.g.
374 prevents dragging of the viewport (done by GtkScrolledFrame) when
375 pressing mouse button 2 on a link.
376
377 You may call gtk_container_set_border_width for a border around the
378 scrolled area.
0 Jan 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =======
4 DwImage
5 =======
6
7 A widget for displaying images and handling image maps.
8
9
10 Image Maps
11 ==========
12
13 Client Side Image Maps
14 ----------------------
15 You must first create a list of image maps: Allocate a DwImageMapList,
16 and initialize it by calling a_Dw_image_map_list_init. Adding a map is
17 done by a_Dw_image_map_list_add_map. a_Dw_image_map_list_add_shape
18 adds a shape to the last map. For the meaning of the link argument,
19 see Section "Signals".
20
21 Image maps are referred by a URL (not only by a name). But currently,
22 the image map list is stored in DilloHtmlLB and there is no
23 possibility to parse documents without rendering, so images can only
24 use maps defined in the same document.
25
26 To use a map in an image, call a_Dw_image_set_usemap with the image,
27 the map list, and the URL of the map. Passing the whole list makes it
28 possible to use maps parsed after the image is created.
29
30
31 Server Side Image Maps
32 ----------------------
33 To use images for server side image maps, you must call
34 a_Dw_image_set_ismap and the style must contain a valid link
35 element. See section "Signals" for more details.
36
37
38 Signals
39 =======
40
41 There are five signals, which can be connected to process actions with
42 links. All have at least three arguments:
43
44 - link is the link element of the DwStyle (server side image maps)
45 or DwImageMapShape (client side image maps). The value is an
46 integer, which is currently only used for hypertext links. But
47 generally, it depends on the signal callback how this value is
48 used.
49
50 - x and y are, when server side image maps are used, the relative
51 coordinates of the mouse pointer, otherwise always -1.
52
53 Note that, unlike by DwPage before, no cursors are set. Instead, the
54 signal callback function has to do this.
55
56 The signals:
57
58 - void link_entered (DwImage *image,
59 gint link, gint x, gint y)
60
61 Emitted when the link the mouse pointer is over has
62 changed. "Changed" includes link, x and y, so this signal is also
63 emitted each time the pointer is moved within a server side image
64 map. If the pointer is outside of a link, all arguments have the
65 value -1.
66
67
68 - void link_pressed (DwImage *image,
69 gint link, gint x, gint y,
70 GdkEventButton *event)
71
72 Emitted when the user presses a mouse button _inside_ a link,
73 this signal is never emitted with link = -1. You can use the
74 event to get information about the button, shift keys, etc.
75
76
77 - void link_released (DwImage *image,
78 gint link, gint x, gint y,
79 GdkEventButton *event)
80
81 - void link_clicked (DwImage *image,
82 gint link, gint x, gint y,
83 GdkEventButton *event)
84
85 Analogue to link_pressed.
86
87 - void void (*image_pressed) (DwImage *page,
88 GdkEventButton *event)
89
90 Emitted when the user presses the mouse button on an image which
91 has no related map. In some cases, it may be neccessary to
92 suppress event processing by a_Dw_widget_set_button_sensitive().
93
94
95 Future Extentions
96 =================
97
98 These are some ideas for a different design, which will solve several
99 problems (image transparency, memory usage when implementing a global
100 size factor):
101
102 1. Instead of a guchar array, a new data type, DilloImageBuffer,
103 should be used (DICacheEntry::ImageBuffer and DwImage::buffer). Any
104 access is done by function calls. Copying the lines (in
105 a_Image_write) is done by a_Image_buffer_copy_line, etc. The call to
106 Image_line becomes obsolete, since DilloImageBuffer will deal with
107 different types: indexed, RGB, gray, RGBA, gray-alpha(?). This may
108 be useful for a more efficient implementation of DilloImageBuffer.
109
110 2. The modules Png, Jpeg, Gif, Image and DICache deal with the
111 original image size (read by the decoders), while DwImage gets the
112 "viewed" size (original size, multiplied by the global image
113 scaling factor) from DilloImageBuffer.
114
115 3. For DwImage, there are further methods which replace the current
116 direct access. Note to worth:
117
118 - Scaled buffers are shared, reference counters are used. Copying
119 rows will automatically also affect the related scaled buffers.
120
121 - There are two methods for drawing, one called after expose events
122 (Dw_image_draw) and another called by a_Dw_image_draw_row. The
123 exact rules, how the buffer is scaled (this can, e.g., differ by a
124 pixel or so), is hidden by DilloImageBuffer.
125
126 - As noted above, all DwImage code is based on the viewed size.
127
128 4. The global image scaling factor is used in two places. The HTML
129 parser must apply it on the WIDTH and HEIGHT attributes of the
130 <IMG> tag and DilloImageBuffer must use it to scale the inherent
131 size of an image. There are two modes, which the user can switch
132 by a dillorc option:
133
134 (i) The original image data is preserved, and an additional scaled
135 buffer is created:
136
137 +-------+ +------------------+ additional
138 | Image | -- copy --> | DilloImageBuffer | --> scaled
139 +-------+ rows | (original size) | buffers
140 +------------------+
141 | ^
142 | |
143 scale requests
144 each row for scaled
145 | buffers
146 | |
147 v |
148 +------------------+
149 | DilloImageBuffer |
150 | (viewed size) |
151 +------------------+
152 ^
153 |
154 +---------+
155 | DwImage |
156 +---------+
157
158 (ii) The original data gets lost just after scaling:
159
160 +-------+ +------------------+
161 | Image | -- copy --> | DilloImageBuffer | --> scaled
162 +-------+ rows | (viewed size) | buffers
163 +------------------+
164 ^
165 |
166 +---------+
167 | DwImage |
168 +---------+
169
170 (ii) uses generally less memory, while in some cases leads to a
171 lower rendering quality, as in this example:
172
173 "foo.png" has a size of 100x100 pixels, the image scaling factor is
174 50%, and the following HTML sniplet is rendered:
175
176 <img src="foo.png">
177 <img src="foo.png" width=200 height=200>
178
179 The first image is displayed at a size of 50x50, the second at
180 100x100. (i) will, for the second, use the original buffer, but
181 (ii) will enlarge the buffer, which was scaled down before, so
182 resulting in a "pixelized" image.
183
184 5. Any implementation of DilloImageBuffer will handle transparency
185 independent of the background, i.e., the background colour will not
186 be used anymore. The first implementation may be based on GdkRGB
187 and (when needed) dithered clipping bitmaps. Better implementations
188 may then be developed in the future.
189
190 6. Background images: The modules Image and DICache do no longer
191 access the DwImage directly, all calls are replaced by an
192 Interface, which is then implemented by DwImage and an appropriate
193 structure for background images (part of DwStyle). The interface is
194 in C realized by a struct of function pointers, and a generic
195 pointer on DwImage, etc.
196
197 7. If someone really needs it, animated GIF's may be considered.
0 Nov 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 ======
4 DwPage
5 ======
6
7 A widget for displaying texts. It is (currently) the main widget for
8 rendering HTML documents.
9
10
11 Signals
12 =======
13
14 DwPage defines the same signals as DwImage, except "image_pressed",
15 with the exception that the coordinates are always -1. See
16 DwImage.txt for more details.
17
18
19 Collapsing Spaces
20 =================
21
22 The idea behind this is that every text box has a specific vertical
23 space around and that they are combined to one space, according to
24 rules stated below. A rule is either a paragraph within a DwPage
25 widget, or a DwPage within a DwPage widget, in a single line; the
26 latter is used for indented boxes and list items.
27
28 The rules:
29
30 1. If a box is following another, the space between them is the
31 maximum of both box spaces:
32
33 +---------+
34 |/////////|
35 |/////////| +---------+
36 +---------+ |/////////|
37 | A | |/////////|
38 +---------+ +---------+
39 |/////////| | A |
40 |/////////| +---------+
41 +---------+ are combined like this: |/////////|
42 |XXXXXXXXX|
43 +---------+ +---------+
44 |\\\\\\\\\| | B |
45 +---------+ +---------+
46 | B | |\\\\\\\\\|
47 +---------+ +---------+
48 |\\\\\\\\\|
49 +---------+
50
51 2. a) If one box is the first box within another, the upper space
52 of these boxes collapse. b) The analogue is the case for the
53 last box:
54
55 +---------+ If B and C are put into A,
56 |/////////| the result is:
57 |/////////|
58 +---------+ +---------+ +---------+
59 | A | <--+-- |\\\\\\\\\| |/////////|
60 +---------+ ¦ +---------+ |XXXXXXXXX|
61 |/////////| | | B | +---------+
62 |/////////| | +---------+ | B |
63 +---------+ | |\\\\\\\\\| +---------+
64 | +---------+ |\\\\\\\\\|
65 | |\\\\\\\\\|
66 | +---------+ |\\\\\\\\\|
67 `-- |\\\\\\\\\| +---------+
68 |\\\\\\\\\| | C |
69 |\\\\\\\\\| +---------+
70 +---------+ |\\\\\\\\\|
71 | C | |XXXXXXXXX|
72 +---------+ |XXXXXXXXX|
73 |\\\\\\\\\| +---------+
74 |\\\\\\\\\|
75 |\\\\\\\\\|
76 +---------+
77
78 For achieving this, there are some features of DwPage:
79
80 - Consequent breaks are automatically combined, according to
81 rule 1. See the code of a_Dw_page_add_break for details.
82
83 - If a break is added as the first word of the DwPage within
84 another DwPage, collapsing according to rule 2a is done
85 automatically. See the code of a_Dw_page_add_break.
86
87 - To collapse spaces according to rule 2b,
88 a_Dw_page_hand_over_break must be called for the *inner*
89 widget. The HTML parser does this in Html_eventually_pop_dw.
90
91
92 Collapsing Margins
93 ==================
94
95 Collapsing margins, as defined in the CSS2 specification, are,
96 supported in addition to collapsing spaces. Also, spaces and margins
97 collapse themselves. I.e., the space between two paragraphs is the
98 maximum of the space calculated as described in "Collapsing Spaces"
99 and the space calculated according to the rules for collapsing margins.
100
101 (This is an intermediate hybrid state, collapsing spaces are used in
102 the current version of dillo, while I implemented collapsing margins
103 for CSS and integrated it already into the main trunk. For a pure
104 CSS-based dillo, collapsing spaces will not be needed anymore, and may
105 be removed for simplicity.)
106
107
108 Some Internals
109 ==============
110
111 There are two lists, words and lines. The word list is quite static;
112 only new words may be added. A word is either text, a widget, a break
113 or an anchor. Anchors are stored in the text, because it may be
114 necessary to correct the scroller positions at rewrapping.
115
116 Lines refer to the word list (first and last), they are completely
117 redundant, i.e., they can be rebuilt from the words. Lines can be
118 rewrapped either completely or partially (see "Incremental Resizing"
119 below). For the latter purpose, several values are accumulated in the
120 lines. See the file "dw_page.h" for details.
121
122
123 Incremental Resizing
124 --------------------
125 DwPage makes use of incremental resizing as described in Dw.txt,
126 section "Resizing". The parent_ref is, for children of a DwPage,
127 simply the number of the line.
128
129 Generally, there are three cases which may change the size of the
130 widget:
131
132 1. The available size of the widget has changed, e.g., because the
133 user has changed the size of the browser window. In this case,
134 it is necessary to rewrap all the lines.
135
136 2. A child widget has changed its size. In this case, only a rewrap
137 down from the line where this widget is located is necessary.
138
139 (This case is very important for tables. Tables are quite at the
140 bottom, so that a partial rewrap is relevant. Otherwise, tables
141 change their size quite often, so that this is necessary for a
142 fast, non-blocking rendering)
143
144 3. A word (or widget, break etc.) is added to the page. This makes
145 it possible to reuse the old size by simply adjusting the
146 current width and height, so no rewrapping is necessary.
147
148 The state of the size calculation is stored in wrap_ref within DwPage,
149 which has the value -1 if no rewrapping of lines necessary, or
150 otherwise the line from which a rewrap is necessary.
151
0 Apr 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =======
4 DwStyle
5 =======
6
7 Styles of Dillo Widgets
8
9
10 Note
11 ====
12
13 DwStyle has derived from DwPageAttr, and its current structure is very
14 similar to it. In the future, there will be some changes and extensions.
15 Namely:
16
17 - image maps will be handled differently (done),
18 - margins, borders, paddings (done),
19 - background colors/images, and
20 - cursors and tooltips will perhaps move into DwStyle.
21
22 Furthermore, widgets will probably refer to different styles for
23 different states.
24
25
26 Overview
27 ========
28
29 DwStyle provides some resources and attributes for drawing widgets, as
30 well as for parts of a widget (e.g., DwPage uses DwStyle's for its
31 words). Creating a style is done by filling a DwStyle with the
32 attributes (except the ref_count), and calling Dw_style_new:
33
34 DwStyle style_attrs, *style;
35
36 style_attrs.foo = bar;
37 // etc.
38 style = a_Dw_style_new (&style_attrs, random_window);
39 // do something with style
40
41 After this, the attributes of style should not be changed anymore,
42 since styles are often shared between different widgets etc. (see
43 below). Most times, you simply copy the attributes of another style
44 and modify them:
45
46 style_attrs = *another_style;
47 style_attrs.foo = bar;
48 style = a_Dw_style_new (&style_attrs, random_window);
49
50 The font structure can be created by Dw_style_font_new, in a similar
51 way (the GdkFont in font_attrs will be ignored), and colors by
52 Dw_style_color_new, passing 0xrrggbb as an argument. Note that fonts
53 and colors are only intended to be used in conjunction with DwStyle.
54
55
56 Lengths and Percentages
57 =======================
58
59 DwStyleLength is a simple data type for lengths and percentages:
60
61 - A length refers to an absolute measurement. It is used to
62 represent the HTML type %Pixels; and the CSS type <length>.
63
64 For CSS lenghts, there are two units: (i) pixels and absolute
65 units, which have to be converted to pixels (a pixel is, unlike
66 in the CSS specification, treated as absolute unit), and (ii) the
67 relative units "em" and "ex" (see below).
68
69 - A percentage refers to a value relative to another value. It is
70 used for the HTML type %Length; (except %Pixels;), and the CSS
71 type <percentage>.
72
73 - A relative length can be used in lists of HTML MultiLengths.
74
75 Since many values in CSS may be either lengths or percentages, a
76 single type is very useful.
77
78 Useful macros and functions
79 ---------------------------
80 Macros for creating lengths:
81
82 DW_STYLE_CREATE_LENGTH (n) Returns a length of n pixels.
83
84 DW_STYLE_CREATE_EX_LENGTH (n) Returns a length of n times the
85 'x-height'
86
87 DW_STYLE_CREATE_EM_LENGTH (n) Returns a length of n times the
88 'font-size'
89
90 DW_STYLE_CREATE_PERCENTAGE (n) Returns a percentage, n is relative
91 to 1, not to 100.
92
93 DW_STYLE_CREATE_RELATIVE (n) Returns a relative length.
94
95 DW_STYLE_UNDEF_LENGTH Used to indicate unspecified sizes,
96 errors, and the end of a list of
97 lengths.
98
99 Furthermore, there are some functions in html.c:
100
101 DwStyleLength Html_parse_length (gchar *attr);
102
103 Returns a length or a percentage, or DW_STYLE_UNDEF_LENGTH in
104 case of an error.
105
106 DwStyleLength* Html_parse_multi_length (gchar *attr);
107
108 Returns a vector of lengths/percentages. The caller has to free
109 the result when it is not longer used.
110
111 Macros for examining lengths:
112
113 DW_STYLE_IS_LENGTH (l) Returns TRUE if l is a length.
114
115 DW_STYLE_IS_PERCENTAGE (l) Returns TRUE if l is a percentage.
116
117 DW_STYLE_IS_RELATIVE (l) Returns TRUE if l is a relative
118 length.
119
120 DW_STYLE_GET_LENGTH (l, f) Returns the value of a length in
121 pixels, as an integer. f is the
122 font, this is used if l is based on
123 font sizes.
124
125 DW_STYLE_GET_PERCENTAGE (l) Returns the value of a percentage,
126 relative to 1, as a float.
127
128 DW_STYLE_GET_RELATIVE (l) Returns the value of a relative
129 length, as a float.
130
131
132 Representation
133 --------------
134 Notes:
135
136 1. This is not part of the interface and may change! Use the
137 macros described above.
138 2. Negative numbers may not work yet.
139
140 DwStyleLength is represented by an integer (n is the number of bits of
141 an integer):
142
143 - Undefined lengths are represented by 0.
144
145 - Lenghts in pixel:
146
147 +---+ - - - +---+---+---+---+
148 | int value | 0 | 1 |
149 +---+ - - - +---+---+---+---+
150 n-1 3 2 1 0
151
152 - Lengths in in x-height:
153
154 +---+ - - - +---+---+---+---+
155 | real value | 0 | 1 | 1 |
156 +---+ - - - +---+---+---+---+
157 n-1 3 2 1 0
158
159 - Lengths in in font-size:
160
161 +---+ - - - +---+---+---+---+
162 | real value | 1 | 1 | 1 |
163 +---+ - - - +---+---+---+---+
164 n-1 3 2 1 0
165
166 - Percentages:
167
168 +---+ - - - +---+---+---+---+
169 | real value | 0 | 1 | 0 |
170 +---+ - - - +---+---+---+---+
171 n-1 3 2 1 0
172
173 - Relative lengths:
174
175 +---+ - - - +---+---+---+---+
176 | real value | 1 | 1 | 0 |
177 +---+ - - - +---+---+---+---+
178 n-1 3 2 1 0
179
180 A "real value" is a fixed point number consisting of (m is the number
181 of bits of the value, not the whole integer):
182
183 +---+ - - - +---+---+ - - - +---+
184 | integer part | rest |
185 +---+ - - - +---+---+ - - - +---+
186 m 16 15 0
187
188 For *internal* use, there are two converting macros,
189 DW_STYLE_REAL_TO_FLOAT and DW_STYLE_FLOAT_TO_REAL.
190
191
192 DwStyle Boxes
193 =============
194
195 The CSS Box Model
196 -----------------
197 For borders, margins etc., DwStyle uses the box model defined by
198 CSS2. DwStyle contains some members defining these attributes. A
199 widget must use these values for any calculation of sizes. There are
200 some helper functions (see dw_style.h). A DwStyle box looks quite
201 similar to a CSS box:
202
203
204 ,-- margin.left
205 | ,-- border.left
206 | | ,-- padding.left
207 |---+---+---|
208 +---------------------------------------+ ---
209 | | | margin.top
210 | +-------------------------------+ | -+-
211 | | Border | | | border.top
212 | | +-----------------------+ | | -+-
213 | | | Padding | | | | padding.top
214 new widget | | | +---------------+ | | | ---
215 allocation -->| | | | | | | |
216 | | | | Content | | | |
217 former widget ------------>| | | | |
218 allocation | | | +---------------+ | | | ---
219 | | | | | | | margin.bottom
220 | | +-----------------------+ | | -+-
221 | | | | | border.bottom
222 | +-------------------------------+ | -+-
223 | | | padding.bottom
224 +---------------------------------------+ ---
225 |---+---+---|
226 padding.right --' | |
227 border.right --' |
228 margin.right --'
229
230 Background colors
231 -----------------
232 The background color is stored in style->background_color, which be
233 NULL (the background color of the parent widget is shining through).
234
235 For toplevel widgets, this color is set as the background color of the
236 viewport, for other widgets, a filled rectangle is drawn, covering the
237 content and padding. (This is compliant with CSS2, the background
238 color of the toplevel element covers the whole canvas.)
239
240 Drawing
241 -------
242 There is a new function Dw_widget_draw_widget_box, which should be
243 called at the beginning of Dw_foo_draw. For parts referring to styles
244 (e.g., words in a page), Dw_widget_draw_box should be used.
245
246
247 Notes on Memory Management
248 ==========================
249
250 Memory management is done by reference counting, a_Dw_style_new
251 returns a pointer to DwStyle with an increased reference counter, so
252 you should care about calling Dw_style_unref if it is not used
253 anymore. You do *not* need to care about the reference counters of
254 fonts and styles.
255
256 In detail:
257
258 - a_Dw_style_ref is called in
259
260 * a_Dw_widget_set_style, to assign a style to a widget,
261
262 * a_Dw_page_add_text, a_Dw_page_add_widget,
263 a_Dw_page_add_anchor, to assign a style to a word,
264
265 * and Html_push_tag (often the reference counter is again
266 decreased shortly after this).
267
268 - a_Dw_unref_style is called in:
269
270 * Dw_page_destroy, Dw_widget_destroy, Html_cleanup_tag,
271 Html_pop_tag, Html_close,
272
273 * a_Dw_widget_set_style, Html_set_top_font (and several
274 Html_tag_open_... functions), these functions overwrite an
275 existing style.
276
277
278 HTML Stack
279 ==========
280
281 (This is not DwStyle specific, but may be useful if you are working on
282 the HTML parser.)
283
284 The topmost element of the HTML stack contains a (reference to a)
285 style which will be used to add the text to the current page. If you
286 use a style only for a short while (see Html_tag_open_frame for an
287 example), you may use it this way:
288
289 style_attrs = *html->stack[html->stack_top].style;
290 style_attrs.foo = bar;
291 style = a_Dw_style_new (&style_attrs, random_window);
292
293 Do not forget to unref it afterwards. A good choice for random_window
294 is html->bw->main_window->window.
295
296 In many cases, you want to set the style for the content of an element
297 (e.g., <A>). Then you must store it in the stack:
298
299 DwStyle style_attrs, *old_style;
300
301 old_style = html->stack[html->stack_top].style;
302 style_attrs = *old_style;
303 style_attrs.foo = bar;
304 html->stack[html->stack_top].style =
305 a_Dw_style_new (&style_attrs, random_window);
306 a_Dw_style_unref (old_style);
307
308 The macro HTML_SET_TOP_ATTR can be used for single attributes, for
309 changing more attributes, this code should be copied for efficiency.
0 May 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =======
4 DwTable
5 =======
6
7 A container widget for rendering tables.
8
9
10 The DwTable Widget
11 ==================
12
13 DwTable is a container widget for rendering tables. It aligns other
14 DwWidgets (normally DwPage), according to the following rules:
15
16 1. All columns have have the same width W, except:
17
18 - W is less than the minimal column width, or
19 - W is greater than the maximal column width.
20
21 Furthermore, W is
22
23 - less than all minimal widths of columns not having W as
24 width, and
25 - greater than all maximal widths of columns not having W as
26 width.
27
28 2. The table tries to use exactly the whole available width, except
29 if it is not possible, because the it is less/greater than the
30 minimal/maximal table width.
31
32 This is simple to implement for columns with COLSPAN == 1, using
33 a_Dw_get_extremes for getting the minimal and maximal widths. For
34 arbitrary COLSPAN values, an approach described in "Subtables" is
35 used to get optimal results (as described above) in most cases, while
36 the rendering remains fast.
37
38
39 Subtables
40 =========
41
42 A table is divided into subtables, which do not (in most cases) share
43 spanning cells, until single columns are left. Cells spanning the
44 whole width are removed before dividing further. Example:
45
46 +---+-------+---+
47 | A | B | C |
48 +---+-------+---+
49 | D | E |
50 +---+-------+---+
51 | F | G | H |
52 +---+-------+---+
53 ' ' ` `
54 ' ' ` `
55 +---+-------+ +---+
56 | A | B | | C |
57 +---+-------+ +---+
58 removed --> | D | | E |
59 +---+-------+ +---+
60 | F | G | | H |
61 +---+-------+ +---+
62 ' ' ` ` final
63 ' ' ` `
64 +---+ +-------+
65 | A | | B | <-.
66 +---+ +-------+ >- removed
67 | F | | G | <-'
68 +---+ +-------+
69 final ' ' ` `
70 ' ' ` `
71 [empty] [empty]
72 final final
73
74 There is a structure, DwTableSub, for holding all the information. It
75 is rebuilt when new cells are added. Do not confuse this with nested
76 tables, these are represented by the Dw widget hierarchy.
77
78 If table cells overlap horizontally, they are (virtually) divided. The
79 minimal and maximal widths are apportioned to the other columns
80 (resulting in a non optimal layout):
81
82 +-------+---+---+
83 | A | B | C |
84 +---+---+---+---+
85 | D | E |
86 +---+-----------+
87 ' ' ` `
88 ' ' ` `
89 +-------+ +---+---+
90 | A | | B | C |
91 +---+---+ +---+---+
92 | D |1/3| | 2/3 E |
93 | | E | | |
94 +---+---+ +-------+
95
96 Example for a non-optimal case
97 ------------------------------
98 The HTML document fragment
99
100 <table>
101 <tr>
102 <td colspan="2">Text
103 <td>LongText
104 <tr>
105 <td>Text
106 <td colspan="2">LongText
107 </table>
108
109 will result in:
110
111 | 0 | 1 | 2 |
112
113 +------------+----------+
114 | Text | LongText |
115 +------+-----+----------+
116 | Text | LongText |
117 +------+----------------+
118
119 The width of column 1 is determined by the half of the minimal width
120 of the LongText. An optimal rendering would be something like:
121
122 ,- 1
123 | 0 || 2 |
124
125 +-------+----------+
126 | Text | LongText |
127 +------++----------+
128 | Text | LongText |
129 +------+-----------+
130
131
132 Algorithms
133 ==========
134
135 Calculating extremes
136 --------------------
137 The extremes of all subtables are calculated by
138 Dw_table_sub_get_extremes and stored in DwTableSub:
139
140 minimal/maximal width (sub table) =
141 - for single column: maximum of all minimal/maximal widths
142 - otherwise: maximum of
143 1. all minimal/maximal widths of cells spanning
144 the whole width, and
145 2. the sum of the minimal/maximal widths of the
146 sub-subtables
147
148 In step 1, the width argument is used to adjust the maximum
149 and minimum width of the whole subtable and mark it as fixed.
150
151 todo: describe percentages.
152
153 Calculating column widths
154 -------------------------
155 The calculation is based on a fixed width, which is, at the top, the
156 width set by a_Dw_widget_set_width. This is corrected by the minimal and
157 maximal width of the whole table, and, if given, the width attribute
158 of the table. At each level, the available width is always between the
159 minimal and the maximal width of the subtable.
160
161 For single columns, the width is the passed fixed width. Otherwise:
162
163 1. Calculate relative widths, they effect the minimal and maximal
164 widths. (Temporally, not permanently!)
165
166 2. The sum of these corrected minima may be greater as the fixed
167 width of the subtable. In this case, decrease them again to
168 match exactly the fixed width, then use them as sub-subtable
169 fixed widths and finish. Otherwise, continue:
170
171 3. If the extremes of the spanning widths of the subtable are
172 greater than the sum of sub-subtables extremes, adjust the
173 extremes of sub-subtables which are not fixed, i.e., where no
174 width argument (either percentage or fixed) freezes the width.
175
176 4. Use an iteration on the subtables, to determine the column
177 widths, see Dw_table_sub_calc_col_widths for details.
178
179 5. After this, apply this recursively on all subtables and pass the
180 subtable width as fixed width.
181
182
183 Borders, Paddings, Spacing
184 ==========================
185
186 Currently, DwTable supports only the separated borders model (see CSS
187 specification). Borders, paddings, spacing is done by creating DwStyle
188 structures with values equivalent to following CSS:
189
190 TABLE {
191 border: outset <table-border>;
192 border-collapse: separate;
193 border-spacing: <table-cellspacing>
194 background-color: <table-bgcolor>
195 }
196
197 TD TH {
198 border: inset <table-border>;
199 padding: <table-cellspacing>
200 background-color: <td/th-bgcolor>
201 }
202
203 Here, <foo-bar> refers to the attribute bar of the tag foo. See
204 Html_open_table and Html_open_table_cell for more details.
0 Jan 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 ========
4 DwWidget
5 ========
6
7 The base object for all Dw widgets.
8
9
10 Structures
11 ==========
12
13 DwRectangle
14 -----------
15 A replacement for GdkRectangle, the only difference is the use of 32
16 instead of 16 bit integers.
17
18
19 DwAllocation, DwRequisition
20 ---------------------------
21 Similar to GtkAllocation and GtkRequisition. Instead of a height, you
22 have two members, ascent and descent.
23
24
25 DwExtremes
26 ----------
27 A structure containing the minimal and maximal width of a widget. See
28 get_extremes below for details.
29
30
31 DwWidget
32 --------
33 Some members you may use:
34
35 parent The parent widget. NULL for toplevel widgets.
36
37 flags See below.
38
39 style The style attached to the widget, this must
40 always be defined, but is not created
41 automatically. Instead, this has to be done
42 immediately after creating the widget
43 (e.g., in a_Web_dispatch_by_type). This style
44 contains attributes and resources for general
45 drawing. See DwStyle.txt for details.
46
47 These members should only be used within the "core" (GtkDw*, DwWidget
48 and DwEmbedGtk):
49
50 viewport The viewport containing the widget. Defined
51 for all widgets.
52
53 allocation,
54 requisition,
55 extremes,
56 user_requisition These are used to optimize several wrappers,
57 see below.
58
59 anchors_table See notes on achors.
60
61 Flags
62 -----
63 Flags can be set and unset with DW_WIDGET_SET_FLAGS and
64 DW_WIDGET_UNSET_FLAGS. For reading flags use the macros DW_WIDGET_...
65
66 DW_NEEDS_RESIZE
67 DW_EXTREMES_CHANGED Denotes that the widget must be resized. Used
68 only internally. See Dw.txt and the source
69 for more details.
70
71 DW_NEEDS_ALLOCATE Set to overide the optimation in
72 a_Dw_widget_size_allocate. Used only
73 internally.
74
75 DW_REALIZED Set when the widget is realized. Should be
76 used to prevent crashes in certain
77 situations.
78
79 Following flags describe characteristics of widgets and are typically
80 set in the init function:
81
82 DW_USES_HINTS A widget with this flag set has a complex way
83 to deal with sizes, and should
84
85 - implement the functions set_width,
86 set_ascent, set_descent, and
87 get_extremes, and
88 - deal completely with width and height
89 in widget->style.
90
91 Examples are DwPage and DwTable. Other
92 widgets, like DwImage and DwHRuler, are much
93 simpler and don't have to set this flag. For
94 these widgets, much of the size calculation
95 is done by the parent widget.
96
97 This flag is unset by default.
98
99 DW_HAS_CONTENT If this flag is set, more space is reserved
100 for the widget in some circumstances. E.g.,
101 if an image has a width of 100%, it makes
102 sense to use more space within a table cell,
103 as compared to a horizontal ruler, which does
104 not "have a content".
105
106 This flag is set by default.
107
108
109 Signal Prototypes
110 =================
111
112 - void size_request (DwWidget *widget,
113 DwRequisition *requisition);
114
115 Similar to Gtk.
116
117 void get_extremes (DwWidget *widget,
118 DwExtremes *extremes);
119
120 Return the maximal and minimal width of the widget, equivalent
121 to the requisition width after calling set_width with zero and
122 infinitive, respectively. This is important for fast table
123 rendering. Simple widgets do not have to implement this; the
124 default is the requisition width for both values.
125
126 - void size_allocate (DwWidget *widget,
127 DwAllocation *allocation);
128
129 Similar in Gtk. Note: allocation has world coordinates.
130
131 - void set_width (DwWidget *widget,
132 guint32 width);
133
134 - void set_height (DwWidget *widget,
135 guint32 height);
136
137 These are hints by the caller, which *may* influence the size
138 returned by size_request. The implementation should call
139 Dw_widget_queue_resize if necessary. In most cases, these
140 signals do not have to be implemented. Currently, only the
141 DwPage widget uses this to determine the width for rewrapping
142 text (note that the resulting width returned by
143 Dw_page_size_request may be _bigger_) and relative sizes of the
144 children.
145
146 - void draw (DwWidget *widget,
147 DwRectangle *area,
148 GdkEventExpose *event);
149
150 Draw the widget's content in the specified area. It may either
151 be caused by an expose event, or by an internal drawing request
152 (e.g., followed by resizing of widgets). In the first case, you
153 get the *original* expose event as third argument. In the
154 latter, event is NULL. The area argument has widget
155 coordinates. A DwContainer is responsible for drawing its
156 children.
157
158 (Since DwWidget's are always windowless, there was no need for
159 two signals, "draw" and "expose_event".)
160
161 - void realize (DwWidget *widget);
162
163 Create all necessary X resources. Called when either the
164 viewport (top-level widgets) or, respectively, the parent Dw
165 widget is realized, or an widget is added to an already
166 realized Dw widget/viewport.
167
168 - void unrealize (DwWidget *widget);
169
170 Remove created X resources.
171
172 - gint button_press_event (DwWidget *widget,
173 guint32 x,
174 guint32 y,
175 GdkEventButton *event);
176
177 This signal is emitted when the user presses a mouse button in
178 a DwWidget. x and y are the coordinates relative to the origin
179 of the widget, event is the *original* event, which may, e.g.,
180 be used to determine the number of the pressed button, the state
181 of the shift keys, etc. The implementation of this signal
182 should return TRUE, if the event has been processed, otherwise
183 FALSE.
184
185 A DwContainer is *not* responsible for delivering button press
186 events to its children. Instead, Dw first emits the
187 button_press_event signal for the most inner widgets and
188 continues this for the parents, until TRUE is returned.
189
190 - gint button_release_event (DwWidget *widget,
191 guint32 x,
192 guint32 y,
193 GdkEventButton *event);
194
195 Compare button_press_event.
196
197 - gint motion_notify_event (DwWidget *widget,
198 guint32 x,
199 guint32 y,
200 GdkEventMotion *event);
201
202 Compare button_press_event. event may be NULL when the call was
203 caused by something different than a "real" motion notify event.
204 E.g., either when widgets are moved (not yet implemented), or the
205 viewport.
206
207 - gint enter_notify_event (DwWidget *widget,
208 DwWidget *last_widget,
209 GdkEventMotion *event);
210
211 These "events" are simulated based on motion nofify events.
212 event is the *original* event (may also be NULL in some cases).
213 last_widget is the widget in which the pointer was in before.
214
215 - gint leave_notify_event (DwWidget *widget,
216 DwWidget *next_widget,
217 GdkEventMotion *event);
218
219 Compare enter_notify_event. next_widget is the widget the
220 pointer is now in.
221
222
223 Useful Internal Functions and Macros
224 ====================================
225
226 - gint Dw_widget_intersect (DwWidget *widget,
227 DwRectangle *area,
228 DwRectangle *intersection);
229
230 Calculates the intersection of widget->allocation and area,
231 returned in intersection (in widget coordinates!). Typically
232 used by containers when drawing their children. Returns whether
233 intersection is not empty.
234
235 - gint32 Dw_widget_x_viewport_to_world (DwWidget *widget,
236 gint16 viewport_x);
237
238 - gint32 Dw_widget_y_viewport_to_world (DwWidget *widget,
239 gint16 viewport_y);
240
241 - gint16 Dw_widget_x_world_to_viewport (DwWidget *widget,
242 gint32 world_x);
243
244 - gint16 Dw_widget_y_world_to_viewport (DwWidget *widget,
245 gint32 world_y);
246
247 These functions convert between world and viewport coordinates.
248
249 - void Dw_widget_queue_draw (DwWidget *widget);
250
251 - void Dw_widget_queue_draw_area (DwWidget *widget,
252 gint32 x,
253 gint32 y,
254 guint32 width,
255 guint32 height);
256
257 - void Dw_widget_queue_clear (DwWidget *widget);
258
259 - void Dw_widget_queue_clear_area (DwWidget *widget,
260 gint32 x,
261 gint32 y,
262 guint32 width,
263 guint32 height);
264
265 Equivalents to the Gtk+ functions. They (currently) result in a
266 call of gtk_widget_xxx_area with the viewport as first
267 argument. x and y are widget coordinates.
268
269 - void Dw_widget_queue_resize (DwWidget *widget,
270 gint ref,
271 gboolean extremes_changed);
272
273 Similar to gtk_widget_queue_resize. Call this function when the
274 widget has changed its size. The next call to
275 Dw_xxx_size_request should then return the new size.
276
277 See Dw.txt for explanation on how to use the ref argument,
278 extremes_changed specifies whether the extremes have changed
279 (the latter is often not the case for an implementations of
280 set_{width|ascent|descent}).
281
282 - void Dw_widget_set_anchor (DwWidget *widget,
283 gchar *name,
284 int pos);
285
286 Add an anchor to a widget. The name will not be copied, it has
287 to be stored elsewhere (DwPage e.g. stores it in the DwPageWord
288 structure).
289
290 - void a_Dw_widget_set_cursor (DwWidget *widget,
291 GdkCursor *cursor)
292
293 Set the cursor for a DwWidget. cursor has to be stored
294 elsewhere, it is not copied (and not destroyed). If cursor is
295 NULL, the cursor of the parent widget is used.
296
297 (This will probably be changed in the future and replaced by a
298 common style mechanism.)
299
300 - DW_WIDGET_WINDOW (widget)
301
302 Returns the window a widget should draw into.
303
304
305 External Functions
306 ==================
307
308 - void a_Dw_widget_set_usize (DwWidget *widget,
309 guint32 width,
310 guint32 ascent,
311 guint32 descent);
312
313 Override the "desired" size of a widget. Further calls of
314 a_Dw_widget_request_size will return these values, except those
315 specified as -1. A possible use shows Html_add_widget in
316 html.c.
317
318 (This will probably be removed. Instead DwStyle should be used.)
319
320
321 - void a_Dw_widget_set_bg_color (DwWidget *widget,
322 gint32 color);
323
324 Set the background color of a widget. This works currently only
325 for the top-level widget. In this case, the background color of
326 the GtkDwViewport is changed. In future, background colors for
327 all widgets will be needed, e.g., for table cells (will be
328 DwPage's), this will (probably) be based on filled rectangles.
329
330 - void a_Dw_widget_scroll_to (DwWidget *widget,
331 int pos)
332
333 Scroll viewport to pos (vertical widget coordinate).
334
335 There are furthermore wrappers for the signals, in some cases they
336 are optimized and/or provide further functionality. In (almost) no
337 case should you emit the signals directly. See dw_widget.c for more
338 details.
0 October 2001, --Jcid
1 Last update: Dec 2004
2
3 ---------------
4 THE HTML PARSER
5 ---------------
6
7
8 Dillo's parser is more than just a HTML parser, it does XHTML
9 and plain text also. It has parsing 'modes' that define its
10 behaviour while working:
11
12 typedef enum {
13 DILLO_HTML_PARSE_MODE_INIT,
14 DILLO_HTML_PARSE_MODE_STASH,
15 DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
16 DILLO_HTML_PARSE_MODE_BODY,
17 DILLO_HTML_PARSE_MODE_VERBATIM,
18 DILLO_HTML_PARSE_MODE_PRE
19 } DilloHtmlParseMode;
20
21
22 The parser works upon a token-grained basis, i.e., the data
23 stream is parsed into tokens and the parser is fed with them. The
24 process is simple: whenever the cache has new data, it gets
25 passed to Html_write, which groups data into tokens and calls the
26 appropriate functions for the token type (TAG, SPACE or WORD).
27
28 Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser
29 doesn't try to split the data stream into tokens anymore, it
30 simply collects until the closing tag.
31
32 ------
33 TOKENS
34 ------
35
36 * A chunk of WHITE SPACE --> Html_process_space
37
38
39 * TAG --> Html_process_tag
40
41 The tag-start is defined by two adjacent characters:
42
43 first : '<'
44 second: ALPHA | '/' | '!' | '?'
45
46 Note: comments are discarded ( <!-- ... --> )
47
48
49 The tag's end is not as easy to find, nor to deal with!:
50
51 1) The HTML 4.01 sec. 3.2.2 states that "Attribute/value
52 pairs appear before the final '>' of an element's start tag",
53 but it doesn't define how to discriminate the "final" '>'.
54
55 2) '<' and '>' should be escaped as '&lt;' and '&gt;' inside
56 attribute values.
57
58 3) The XML SPEC for XHTML states:
59 AttrValue ::== '"' ([^<&"] | Reference)* '"' |
60 "'" ([^<&'] | Reference)* "'"
61
62 Current parser honors the XML SPEC.
63
64 As it's a common mistake for human authors to mistype or
65 forget one of the quote marks of an attribute value; the
66 parser solves the problem with a look-ahead technique
67 (otherwise the parser could skip significative amounts of
68 well written HTML).
69
70
71
72 * WORD --> Html_process_word
73
74 A word is anything that doesn't start with SPACE, and that's
75 outside of a tag, up to the first SPACE or tag start.
76
77 SPACE = ' ' | \n | \r | \t | \f | \v
78
79
80 -----------------
81 THE PARSING STACK
82 -----------------
83
84 The parsing state of the document is kept in a stack:
85
86 struct _DilloHtml {
87 [...]
88 DilloHtmlState *stack;
89 gint stack_top; /* Index to the top of the stack [0 based] */
90 gint stack_max;
91 [...]
92 };
93
94 struct _DilloHtmlState {
95 char *tag;
96 DwStyle *style, *table_cell_style;
97 DilloHtmlParseMode parse_mode;
98 DilloHtmlTableMode table_mode;
99 gint list_level;
100 gint list_number;
101 DwWidget *page, *table;
102 gint32 current_bg_color;
103 };
104
105
106 Basically, when a TAG is processed, a new state is pushed into
107 the 'stack' and its 'style' is set to reflect the desired
108 appearance (details in DwStyle.txt).
109
110 That way, when a word is processed later (added to the Dw), all
111 the information is within the top state.
112
113 Closing TAGs just pop the stack.
114
115
0
1 This is the updated base of a paper I wrote with Horst.
2 It provides a good introduction to Dillo's internals.
3 (Highly recommended if you plan to patch or develop in Dillo)
4
5 It may not be exactly accurate (it's quite old), but it explains
6 the theory behind in some detail, so it's more than recommended
7 reading material.
8 --Jcid
9
10
11 -----------------------------------------------------
12 Parallel network programming of the Dillo web browser
13 -----------------------------------------------------
14
15 Jorge Arellano-Cid <jcid@dillo.org>
16 Horst H. von Brand <vonbrand@inf.utfsm.cl>
17
18
19 --------
20 Abstract
21 --------
22
23 Network programs face several delay sources when sending or
24 retrieving data. This is particularly problematic in programs
25 which interact directly with the user, most notably web browsers.
26 We present a hybrid approach using threads communicated through
27 pipes and signal driven I/O, which allows a non-blocking main
28 thread and overlapping waiting times.
29
30
31 ------------
32 Introduction
33 ------------
34
35 The Dillo project didn't start from scratch but mainly working
36 on the code base of gzilla (a light web browser written by Raph
37 Levien). As the project went by, the code of the whole source was
38 standardized, and the networking engine was replaced with a new,
39 faster design. Later, the parser was changed, the streamed
40 handling of data and its error control was put under the control
41 of the CCC (Concomitant Control Chain), and the old widget system
42 was replaced with a new one (Dw). The source code is currently
43 regarded as "very stable beta", and is available at
44 <http://www.dillo.org>. Dillo is a project licensed under the GNU
45 General Public License.
46
47 This paper covers basic design aspects of the hybrid approach
48 that the Dillo web browser uses to solve several latency
49 problems. After introducing the main delay-sources, the main
50 points of the hybrid design will be addressed.
51
52
53 -------------
54 Delay sources
55 -------------
56
57 Network programs face several delay-sources while sending or
58 retrieving data. In the particular case of a web browser, they
59 are found in:
60
61 DNS querying:
62 The time required to solve a name.
63
64 Initiating the TCP connection:
65 The three way handshake of the TCP protocol.
66
67 Sending the query:
68 The time spent uploading queries to the remote server.
69
70 Retrieving data:
71 The time spent expecting and receiving the query answer.
72
73 Closing the TCP connection:
74 The four packet-sending closing sequence of the TCP protocol.
75
76 In a WAN context, every single item of this list has an
77 associated delay that is non deterministic and often measured in
78 seconds. If we add several connections per browsed page (each one
79 requiring at least the 4 last steps), the total latency can be
80 considerable.
81
82
83 -----------------------------------
84 The traditional (blocking) approach
85 -----------------------------------
86
87 The main problems with the blocking approach are:
88
89 When issuing an operation that can't be completed
90 immediately, the process is put to sleep waiting for
91 completion, and the program doesn't do any other
92 processing in the meantime.
93
94 When waiting for a specific socket operation to complete,
95 packets that belong to other connections may be arriving,
96 and have to wait for service.
97
98 Web browsers handle many small transactions,
99 if waiting times are not overlapped
100 the latency perceived by the user can be very annoying.
101
102 If the user interface is just put to sleep during network
103 operations, the program becomes unresponsive, confusing
104 and perhaps alarming the user.
105
106 Not overlapping waiting times and processing makes
107 graphical rendering (which is arguably the central function
108 of a browser) unnecessarily slow.
109
110
111 ---------------------
112 Dillo's hybrid design
113 ---------------------
114
115 Dillo uses threads and signal driven I/O extensively to
116 overlap waiting times and computation. Handling the user
117 interface in a thread that never blocks gives a good interactive
118 ``feel.'' The use of GTK+, a sophisticated widget framework for
119 graphical user interfaces, helped very much to accomplish this
120 goal. All the interface, rendering and I/O engine was built upon
121 its facilities.
122
123 The design is said to be ``hybrid'' because it uses threads
124 for DNS querying and reading local files, and signal driven I/O
125 for TCP connections. The threaded DNS scheme is potentially
126 concurrent (this depends on underlying hardware), while the I/O
127 handling (both local files and remote connections) is
128 definitively parallel.
129
130 To simplify the structure of the browser, local files are
131 encapsulated into HTTP streams and presented to the rest of the
132 browser as such, in exactly the same way a remote connection is
133 handled. To create this illusion, a thread is launched. This
134 thread opens a pipe to the browser, it then synthesizes an
135 appropriate HTTP header, sends it together with the file to the
136 browser proper. In this way, all the browser sees is a handle,
137 the data on it can come from a remote connection or from a local
138 file.
139
140 To handle a remote connection is more complex. In this case,
141 the browser asks the cache manager for the URL. The name in the
142 URL has to be resolved through the DNS engine, a socket TCP
143 connection must be established, the HTTP request has to be sent,
144 and finally the result retrieved. Each of the steps mentioned
145 could give rise to errors, which have to be handled and somehow
146 communicated to the rest of the program. For performance reasons,
147 it is critical that responses are cached locally, so the remote
148 connection doesn't directly hand over the data to the browser;
149 the response is passed to the cache manager which then relays it
150 to the rest of the browser. The DNS engine caches DNS responses,
151 and either answers them from the cache or by querying the DNS.
152 Querying is done in a separate thread, so that the rest of the
153 browser isn't blocked by long waits here.
154
155 The activities mentioned do not happen strictly in the order
156 stated above. It is even possible that several URLs are being
157 handled at the same time, in order to overlap waiting and
158 downloading. The functions called directly from the user
159 interface have to return quickly to maintain interactive
160 response. Sometimes they return connection handlers that haven't
161 been completely set up yet. As stated, I/O is signal-driven, when
162 one of the descriptors is ready for data transfer (reading or
163 writing), it wakes up the I/O engine.
164
165 Data transfer between threads inside the browser is handled by
166 pipes, shared memory is little used. This almost obviates the
167 need for explicit synchronization, which is one of the main areas
168 of complexity and bugs in concurrent programs. Dillo handles its
169 threads in a way that its developers can think of it as running
170 on a single thread of control. This is accomplished by making the
171 DNS engine call-backs happen within the main thread, and by
172 isolating file loading with pipes.
173
174 Using threads in this way has three big advantages:
175
176 The browser doesn't block when one of its child threads
177 blocks. In particular, the user interface is responsive
178 even while resolving a name or downloading a file.
179
180 Developers don't need to deal with complex concurrent
181 concerns. Concurrency is hard to handle, and few developers
182 are adept at this. This gives access a much larger pool of
183 potential developers, something which can be critical
184 in an open-source development project.
185
186 By making the code mostly sequential, debugging the code
187 with traditional tools like gdb is possible. Debugging
188 parallel programs is very hard, and appropriate tools are
189 hard to come by.
190
191 Because of simplicity and portability concerns, DNS querying
192 is done in a separate thread. The standard C library doesn't
193 provide a function for making DNS queries that don't block. The
194 alternative is to implement a new, custom DNS querying function
195 that doesn't block. This is certainly a complex task, integrating
196 this mechanism into the thread structure of the program is much
197 simpler.
198
199 Using a thread and a pipe to read a local file adds a
200 buffering step to the process (and a certain latency), but it has
201 a couple of significative advantages:
202
203 By handling local files in the same way as remote
204 connections, a significant amount of code is reused.
205
206 A preprocessing step of the file data can be added easily,
207 if needed. In fact, the file is encapsulated into an HTTP
208 data stream.
209
210
211 -----------
212 DNS queries
213 -----------
214
215 Dillo handles DNS queries with threads, letting a child thread
216 wait until the DNS server answers the request. When the answer
217 arrives, a call-back function is called, and the program resumes
218 what it was doing at DNS-request time. The interesting thing is
219 that the call-back happens in the main thread, while the child
220 thread simply exits when done. This is implemented through a
221 server-channel design.
222
223
224 The server channel
225 ------------------
226
227 There is one thread for each channel, and each channel can
228 have multiple clients. When the program requests an IP address,
229 the server first looks for a cached match; if it hits, the client
230 call-back is invoked immediately, but if not, the client is put
231 into a queue, a thread is spawned to query the DNS, and a GTK+
232 idle client is set to poll the channel 5~times per second for
233 completion, and when it finally succeeds, every client of that
234 channel is serviced.
235
236 This scheme allows all the further processing to continue on
237 the same thread it began: the main thread.
238
239
240 ------------------------
241 Handling TCP connections
242 ------------------------
243
244 Establishing a TCP connection requires the well known
245 three-way handshake packet-sending sequence. Depending on network
246 traffic and several other issues, significant delay can occur at
247 this phase.
248
249 Dillo handles the connection by a non blocking socket scheme.
250 Basically, a socket file descriptor of AF_INET type is requested
251 and set to non-blocking I/O. When the DNS server has resolved the
252 name, the socket connection process begins by calling connect(2);
253 {We use the Unix convention of identifying the manual section
254 where the concept is described, in this case
255 section 2 (system calls).}
256 which returns immediately with an EINPROGRESS error.
257
258 After the connection reaches the EINPROGRESS ``state,'' the
259 socket waits in background until connection succeeds (or fails),
260 when that happens, a callback function is awaked to perform the
261 following steps: set the I/O engine to send the query and expect
262 its answer (both in background).
263
264 The advantage of this scheme is that every required step is
265 quickly done without blocking the browser. Finally, the socket
266 will generate a signal whenever I/O is possible.
267
268
269 ----------------
270 Handling queries
271 ----------------
272
273 In the case of a HTTP URL, queries typically translate into a
274 short transmission (the HTTP query) and a lengthy retrieval
275 process. Queries are not always short though, specially when
276 requesting forms (all the form data is attached within the
277 query), and also when requesting CGI programs.
278
279 Regardless of query length, query sending is handled in
280 background. The thread that was initiated at TCP connecting time
281 has all the transmission framework already set up; at this point,
282 packet sending is just a matter of waiting for the
283 write signal (G_IO_OUT) to come and then sending the data. When
284 the socket gets ready for transmission, the data is sent using
285 IO_write.
286
287
288 --------------
289 Receiving data
290 --------------
291
292 Although conceptually similar to sending queries, retrieving
293 data is very different as the data received can easily exceed the
294 size of the query by many orders of magnitude (for example when
295 downloading images or files). This is one of the main sources of
296 latency, the retrieval can take several seconds or even minutes
297 when downloading large files.
298
299 The data retrieving process for a single file, that began by
300 setting up the expecting framework at TCP connecting time, simply
301 waits for the read signal (G_IO_IN). When it happens, the
302 low-level I/O engine gets called, the data is read into
303 pre-allocated buffers and the appropriate call-backs are
304 performed. Technically, whenever a G_IO_IN event is generated,
305 data is received from the socket file descriptor, by using the
306 IO_read function. This iterative process finishes upon EOF (or on
307 an error condition).
308
309
310 ----------------------
311 Closing the connection
312 ----------------------
313
314 Closing a TCP connection requires four data segments, not an
315 impressive amount but twice the round trip time, which can be
316 substantial. When data retrieval finishes, socket closing is
317 triggered. There's nothing but a IO_close_fd call on the socket's
318 file descriptor. This process was originally designed to split
319 the four segment close into two partial closes, one when query
320 sending is done and the other when all data is in. This scheme is
321 not currently used because the write close also stops the reading
322 part.
323
324
325 The low-level I/O engine
326 ------------------------
327
328 Dillo I/O is carried out in the background. This is achieved
329 by using low level file descriptors and signals. Anytime a file
330 descriptor shows activity, a signal is raised and the signal
331 handler takes care of the I/O.
332
333 The low-level I/O engine ("I/O engine" from here on) was
334 designed as an internal abstraction layer for background file
335 descriptor activity. It is intended to be used by the cache
336 module only; higher level routines should ask the cache for its
337 URLs. Every operation that is meant to be carried out in
338 background should be handled by the I/O engine. In the case of
339 TCP sockets, they are created and submitted to the I/O engine for
340 any further processing.
341
342 The submitting process (client) must fill a request structure
343 and let the I/O engine handle the file descriptor activity, until
344 it receives a call-back for finally processing the data. This is
345 better understood by examining the request structure:
346
347 typedef struct {
348 gint Key; /* Primary Key (for klist) */
349 gint Op; /* IORead | IOWrite | IOWrites */
350 gint FD; /* Current File Descriptor */
351 gint Flags; /* Flag array */
352 glong Status; /* Number of bytes read, or -errno code */
353
354 void *Buf; /* Buffer place */
355 size_t BufSize; /* Buffer length */
356 void *BufStart; /* PRIVATE: only used inside IO.c! */
357
358 void *ExtData; /* External data reference (not used by IO.c) */
359 void *Info; /* CCC Info structure for this IO */
360 GIOChannel *GioCh; /* IO channel */
361 guint watch_id; /* glib's event source id */
362 } IOData_t;
363
364 To request an I/O operation, this structure must be filled and
365 passed to the I/O engine.
366
367 'Op' and 'Buf' and 'BufSize' MUST be provided.
368
369 'ExtData' MAY be provided.
370
371 'Status', 'FD' and 'GioCh' are set by I/O engine internal
372 routines.
373
374 When there is new data in the file descriptor, 'IO_callback'
375 gets called (by glib). Only after the I/O engine finishes
376 processing the data are the upper layers notified.
377
378
379 The I/O engine transfer buffer
380 ------------------------------
381
382 The 'Buf' and 'BufSize' fields of the request structure
383 provide the transfer buffer for each operation. This buffer must
384 be set by the client (to increase performance by avoiding copying
385 data).
386
387 On reads, the client specifies the amount and where to place
388 the retrieved data; on writes, it specifies the amount and source
389 of the data segment that is to be sent. Although this scheme
390 increases complexity, it has proven very fast and powerful. For
391 instance, when the size of a document is known in advance, a
392 buffer for all the data can be allocated at once, eliminating the
393 need for multiple memory reallocations. Even more, if the size is
394 known and the data transfer is taking the form of multiple small
395 chunks of data, the client only needs to update 'Buf' and
396 BufSize' to point to the next byte in its large preallocated
397 reception buffer (by adding the chunk size to 'Buf'). On the
398 other hand, if the size of the transfer isn't known in advance,
399 the reception buffer can remain untouched until the connection
400 closes, but the client must then accomplish the usual buffer
401 copying and reallocation.
402
403 The I/O engine also lets the client specify a full length
404 transfer buffer when sending data. It doesn't matter (from the
405 client's point of view) if the data fits in a single packet or
406 not, it's the I/O engine's job to divide it into smaller chunks
407 if needed and to perform the operation accordingly.
408
409
410 ------------------------------------------
411 Handling multiple simultaneous connections
412 ------------------------------------------
413
414 The previous sections describe the internal work for a single
415 connection, the I/O engine handles several of them in parallel.
416 This is the normal downloading behavior of a web page. Normally,
417 after retrieving the main document (HTML code), several
418 references to other files (typically images) and sometimes even
419 to other sites (mostly advertising today) are found inside the
420 page. In order to parse and complete the page rendering, those
421 other documents must be fetched and displayed, so it is not
422 uncommon to have multiple downloading connections (every one
423 requiring the whole fetching process) happening at the same time.
424
425 Even though socket activity can reach a hectic pace, the
426 browser never blocks. Note also that the I/O engine is the one
427 that directs the execution flow of the program by triggering a
428 call-back chain whenever a file descriptor operation succeeds or
429 fails.
430
431 A key point for this multiple call-back chained I/O engine is
432 that every single function in the chain must be guaranteed to
433 return quickly. Otherwise, the whole system blocks until it
434 returns.
435
436
437 -----------
438 Conclusions
439 -----------
440
441 Dillo is currently in very stable beta state. It already shows
442 impressive performance, and its interactive ``feel'' is much
443 better than that of other web browsers.
444
445 The modular structure of Dillo, and its reliance on GTK1 allow
446 it to be very small. Not every feature of HTML-4.01 has been
447 implemented yet, but no significant problems are foreseen in
448 doing this.
449
450 The fact that Dillo's central I/O engine is written using
451 advanced features of POSIX and TCP/IP networking makes its
452 performance possible, but on the other hand this also means that
453 only a fraction of the interested hackers are able to work on it.
454
455 A simple code base is critical when trying to attract hackers
456 to work on a project like this one. Using the GTK+ framework
457 helped both in creating the graphical user interface and in
458 handling the concurrency inside the browser. By having threads
459 communicate through pipes the need for explicit synchronization
460 is almost completely eliminated, and with it most of the
461 complexity of concurrent programming disappears.
462
463 A clean, strictly applied layering approach based on clear
464 abstractions is vital in each programming project. A good,
465 supportive framework is of much help here.
466
467
0 February 2001, --Jcid
1
2 ------
3 IMAGES
4 ------
5
6 * When a image tag is found within a HTML page, Html_tag_open_img
7 handles it by:
8
9 - Parsing & getting attribute values.
10 - Creating a new image structure (DilloImage) and its
11 associated widget (DwImage).
12 i.e. If 'Image' is the var for the structure, then
13 'Image->dw' is the widget.
14 - Requesting the image to be feeded by the cache.
15 - Sending some info to the browser interface.
16
17 * The cache can either request the image data from the net, or
18 feed it directly from the dicache (decompressed image cache).
19
20 * Both processes are somewhat different because the first one
21 requires to decode the image data into RGB format, and the second
22 one has the whole data already decoded.
23
24 * Regardless of the RGB-data feeding method, the decoded data is
25 passed to the widget (DwImage) and drawn by GdkRGB in a streamed
26 way.
27 Note that INDEXED images are also decoded into RGB format
28 before sending them to GdkRGB.
29
30
31 ---------------------
32 Fetching from the net
33 ---------------------
34
35 * a_Cache_open_url initiates the resource request, and when
36 finally the answer arrives, the HTTP header is examined for MIME
37 type and either the GIF or PNG or JPEG decoder is set to handle
38 the incoming data stream.
39 Decoding functions: a_Gif_image, a_Jpeg_image and a_Png_image.
40
41 * The decoding function calls the following dicache methods as
42 the data is processed (listed in order):
43
44 a_Dicache_set_parms
45 a_Dicache_set_cmap (only for indexed-GIF images)
46 a_Dicache_write
47 a_Dicache_close
48
49 * The dicache methods call the necessary functions to connect
50 with the widget code. This is done by calling image.c functions:
51
52 a_Image_set_parms
53 a_Image_set_cmap
54 a_Image_write
55 a_Image_close
56
57 * The functions in image.c make the required a_Dw_image_...
58 calls.
59
60
61 -------------------------
62 Fetching from the dicache
63 -------------------------
64
65 * a_Cache_open_url tests the dicache for the image, and directly
66 enqueues a cache client for it, without asking the network for
67 the data. When the client queue is processed (a bit later), the
68 decoder is selected based on the cache entry type.
69
70 * When the decoder is called, it tests the dicache for the image;
71 if the image is found, then it sets a_Dicache_callback as the
72 handling function and gets out of the way (no image decoding is
73 needed).
74
75 * Later on, the DwImage buffer is set to reference the
76 dicache-entry's buffer and the rest of the functions calls is
77 driven by a_Dicache_callback.
78
79
80 -----------
81 Misc. notes
82 -----------
83
84 * Repeated images generate new cache clients, but only one of
85 them (the first) is handled with a_Dicache_* functions, the rest
86 is done with a_Dicache_callback..
87
88 * The cache-client callback is set when the Content-type of the
89 image is got. It can be: a_Png_image, a_Gif_image or a_Jpeg_image
90 Those are called 'decoders' because their main function is to
91 translate the original data into RGB format.
92
93 * Later on, the decoder can substitute itself, if it finds the
94 image has been already decoded, with a_Dicache_callback function.
95 This avoids decoding it twice.
96
97 * The dicache-entry and the Image structure hold bit arrays that
98 represent which rows had been decoded.
99
100 * The image processing can be found in the following sources:
101
102 - image.[ch]
103 - dicache.[ch]
104 - gif.[ch], png.[ch], jpeg.[ch]
105 - dw_image.[ch]
106
107 * Bear in mind that there are three data structures for image
108 code:
109
110 - DilloImage (image.h)
111 - DwImage (dw_image.h)
112 - DICacheEntry (dicache.h)
113
114
0 EXTRA_DIST = \
1 Cache.txt \
2 Cookies.txt \
3 Dillo.txt \
4 Dw.txt \
5 DwImage.txt \
6 DwPage.txt \
7 DwStyle.txt \
8 DwTable.txt \
9 DwWidget.txt \
10 HtmlParser.txt \
11 IO.txt \
12 Images.txt \
13 NC_design.txt \
14 Selection.txt \
15 Dpid.txt \
16 README
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15 srcdir = @srcdir@
16 top_srcdir = @top_srcdir@
17 VPATH = @srcdir@
18 pkgdatadir = $(datadir)/@PACKAGE@
19 pkglibdir = $(libdir)/@PACKAGE@
20 pkgincludedir = $(includedir)/@PACKAGE@
21 top_builddir = ..
22 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
23 INSTALL = @INSTALL@
24 install_sh_DATA = $(install_sh) -c -m 644
25 install_sh_PROGRAM = $(install_sh) -c
26 install_sh_SCRIPT = $(install_sh) -c
27 INSTALL_HEADER = $(INSTALL_DATA)
28 transform = $(program_transform_name)
29 NORMAL_INSTALL = :
30 PRE_INSTALL = :
31 POST_INSTALL = :
32 NORMAL_UNINSTALL = :
33 PRE_UNINSTALL = :
34 POST_UNINSTALL = :
35 build_triplet = @build@
36 host_triplet = @host@
37 target_triplet = @target@
38 subdir = doc
39 DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
40 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
41 am__aclocal_m4_deps = $(top_srcdir)/configure.in
42 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
43 $(ACLOCAL_M4)
44 mkinstalldirs = $(install_sh) -d
45 CONFIG_HEADER = $(top_builddir)/config.h
46 CONFIG_CLEAN_FILES =
47 SOURCES =
48 DIST_SOURCES =
49 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
50 ACLOCAL = @ACLOCAL@
51 AMDEP_FALSE = @AMDEP_FALSE@
52 AMDEP_TRUE = @AMDEP_TRUE@
53 AMTAR = @AMTAR@
54 AUTOCONF = @AUTOCONF@
55 AUTOHEADER = @AUTOHEADER@
56 AUTOMAKE = @AUTOMAKE@
57 AWK = @AWK@
58 CC = @CC@
59 CCDEPMODE = @CCDEPMODE@
60 CFLAGS = @CFLAGS@
61 CPP = @CPP@
62 CPPFLAGS = @CPPFLAGS@
63 CXX = @CXX@
64 CXXDEPMODE = @CXXDEPMODE@
65 CXXFLAGS = @CXXFLAGS@
66 CYGPATH_W = @CYGPATH_W@
67 DEFS = @DEFS@
68 DEPDIR = @DEPDIR@
69 DLGUI_FALSE = @DLGUI_FALSE@
70 DLGUI_TRUE = @DLGUI_TRUE@
71 ECHO_C = @ECHO_C@
72 ECHO_N = @ECHO_N@
73 ECHO_T = @ECHO_T@
74 EGREP = @EGREP@
75 EXEEXT = @EXEEXT@
76 GLIB_CFLAGS = @GLIB_CFLAGS@
77 GLIB_CONFIG = @GLIB_CONFIG@
78 GLIB_LIBS = @GLIB_LIBS@
79 GTK_CFLAGS = @GTK_CFLAGS@
80 GTK_CONFIG = @GTK_CONFIG@
81 GTK_LIBS = @GTK_LIBS@
82 INSTALL_DATA = @INSTALL_DATA@
83 INSTALL_PROGRAM = @INSTALL_PROGRAM@
84 INSTALL_SCRIPT = @INSTALL_SCRIPT@
85 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
86 LDFLAGS = @LDFLAGS@
87 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
88 LIBFLTK_LIBS = @LIBFLTK_LIBS@
89 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
90 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
91 LIBJPEG_LIBS = @LIBJPEG_LIBS@
92 LIBOBJS = @LIBOBJS@
93 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
94 LIBPNG_LIBS = @LIBPNG_LIBS@
95 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
96 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
97 LIBS = @LIBS@
98 LIBSSL_LIBS = @LIBSSL_LIBS@
99 LIBZ_LIBS = @LIBZ_LIBS@
100 LTLIBOBJS = @LTLIBOBJS@
101 MAKEINFO = @MAKEINFO@
102 OBJEXT = @OBJEXT@
103 PACKAGE = @PACKAGE@
104 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
105 PACKAGE_NAME = @PACKAGE_NAME@
106 PACKAGE_STRING = @PACKAGE_STRING@
107 PACKAGE_TARNAME = @PACKAGE_TARNAME@
108 PACKAGE_VERSION = @PACKAGE_VERSION@
109 PATH_SEPARATOR = @PATH_SEPARATOR@
110 RANLIB = @RANLIB@
111 SET_MAKE = @SET_MAKE@
112 SHELL = @SHELL@
113 STRIP = @STRIP@
114 VERSION = @VERSION@
115 ac_ct_CC = @ac_ct_CC@
116 ac_ct_CXX = @ac_ct_CXX@
117 ac_ct_RANLIB = @ac_ct_RANLIB@
118 ac_ct_STRIP = @ac_ct_STRIP@
119 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
120 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
121 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
122 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
123 am__include = @am__include@
124 am__leading_dot = @am__leading_dot@
125 am__quote = @am__quote@
126 am__tar = @am__tar@
127 am__untar = @am__untar@
128 bindir = @bindir@
129 build = @build@
130 build_alias = @build_alias@
131 build_cpu = @build_cpu@
132 build_os = @build_os@
133 build_vendor = @build_vendor@
134 datadir = @datadir@
135 exec_prefix = @exec_prefix@
136 host = @host@
137 host_alias = @host_alias@
138 host_cpu = @host_cpu@
139 host_os = @host_os@
140 host_vendor = @host_vendor@
141 includedir = @includedir@
142 infodir = @infodir@
143 install_sh = @install_sh@
144 libdir = @libdir@
145 libexecdir = @libexecdir@
146 localstatedir = @localstatedir@
147 mandir = @mandir@
148 mkdir_p = @mkdir_p@
149 oldincludedir = @oldincludedir@
150 prefix = @prefix@
151 program_transform_name = @program_transform_name@
152 sbindir = @sbindir@
153 sharedstatedir = @sharedstatedir@
154 sysconfdir = @sysconfdir@
155 target = @target@
156 target_alias = @target_alias@
157 target_cpu = @target_cpu@
158 target_os = @target_os@
159 target_vendor = @target_vendor@
160 EXTRA_DIST = \
161 Cache.txt \
162 Cookies.txt \
163 Dillo.txt \
164 Dw.txt \
165 DwImage.txt \
166 DwPage.txt \
167 DwStyle.txt \
168 DwTable.txt \
169 DwWidget.txt \
170 HtmlParser.txt \
171 IO.txt \
172 Images.txt \
173 NC_design.txt \
174 Selection.txt \
175 Dpid.txt \
176 README
177
178 all: all-am
179
180 .SUFFIXES:
181 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
182 @for dep in $?; do \
183 case '$(am__configure_deps)' in \
184 *$$dep*) \
185 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
186 && exit 0; \
187 exit 1;; \
188 esac; \
189 done; \
190 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \
191 cd $(top_srcdir) && \
192 $(AUTOMAKE) --gnu doc/Makefile
193 .PRECIOUS: Makefile
194 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
195 @case '$?' in \
196 *config.status*) \
197 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
198 *) \
199 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
200 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
201 esac;
202
203 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
204 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
205
206 $(top_srcdir)/configure: $(am__configure_deps)
207 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
208 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
209 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
210 uninstall-info-am:
211 tags: TAGS
212 TAGS:
213
214 ctags: CTAGS
215 CTAGS:
216
217
218 distdir: $(DISTFILES)
219 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
220 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
221 list='$(DISTFILES)'; for file in $$list; do \
222 case $$file in \
223 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
224 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
225 esac; \
226 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
227 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
228 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
229 dir="/$$dir"; \
230 $(mkdir_p) "$(distdir)$$dir"; \
231 else \
232 dir=''; \
233 fi; \
234 if test -d $$d/$$file; then \
235 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
236 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
237 fi; \
238 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
239 else \
240 test -f $(distdir)/$$file \
241 || cp -p $$d/$$file $(distdir)/$$file \
242 || exit 1; \
243 fi; \
244 done
245 check-am: all-am
246 check: check-am
247 all-am: Makefile
248 installdirs:
249 install: install-am
250 install-exec: install-exec-am
251 install-data: install-data-am
252 uninstall: uninstall-am
253
254 install-am: all-am
255 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
256
257 installcheck: installcheck-am
258 install-strip:
259 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
260 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
261 `test -z '$(STRIP)' || \
262 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
263 mostlyclean-generic:
264
265 clean-generic:
266
267 distclean-generic:
268 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
269
270 maintainer-clean-generic:
271 @echo "This command is intended for maintainers to use"
272 @echo "it deletes files that may require special tools to rebuild."
273 clean: clean-am
274
275 clean-am: clean-generic mostlyclean-am
276
277 distclean: distclean-am
278 -rm -f Makefile
279 distclean-am: clean-am distclean-generic
280
281 dvi: dvi-am
282
283 dvi-am:
284
285 html: html-am
286
287 info: info-am
288
289 info-am:
290
291 install-data-am:
292
293 install-exec-am:
294
295 install-info: install-info-am
296
297 install-man:
298
299 installcheck-am:
300
301 maintainer-clean: maintainer-clean-am
302 -rm -f Makefile
303 maintainer-clean-am: distclean-am maintainer-clean-generic
304
305 mostlyclean: mostlyclean-am
306
307 mostlyclean-am: mostlyclean-generic
308
309 pdf: pdf-am
310
311 pdf-am:
312
313 ps: ps-am
314
315 ps-am:
316
317 uninstall-am: uninstall-info-am
318
319 .PHONY: all all-am check check-am clean clean-generic distclean \
320 distclean-generic distdir dvi dvi-am html html-am info info-am \
321 install install-am install-data install-data-am install-exec \
322 install-exec-am install-info install-info-am install-man \
323 install-strip installcheck installcheck-am installdirs \
324 maintainer-clean maintainer-clean-generic mostlyclean \
325 mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \
326 uninstall-info-am
327
328 # Tell versions [3.59,3.63) of GNU make to not export all variables.
329 # Otherwise a system limit (for SysV at least) may be exceeded.
330 .NOEXPORT:
0
1 _________________________________________________________________
2
3 Naming&Coding design
4 _________________________________________________________________
5
6 Dillo's code is divided into modules. For instance: bookmark, cache,
7 dicache, gif.
8
9 Lets think of a module named "menu", then:
10 * Every internal routine of the module, should start with "Menu_"
11 prefix.
12 * "Menu_" prefixed functions are not meant to be called from outside
13 the module.
14 * If the function is to be exported to other modules (i.e. it will
15 be called from the outside), it should be wrapped with an "a_"
16 prefix.
17
18 For instance: if the function name is "Menu_create", then it's an
19 internal function, but if we need to call it from the outside, then it
20 should be renamed to "a_Menu_create".
21
22 Why the "a_" prefix?
23 Because of historical reasons.
24 And it reads better "a_Menu_create" than "d_Menu_create" cause the
25 first one suggests "a Menu create" function!
26
27 Another way of understanding this is thinking of "a_" prefixed
28 functions as Dillo's internal library, and the rest ("Menu_" prefixed
29 in our example) as a private module-library.
30
31 Indentation: Source code must be indented with 3 blank spaces, no Tabs.
32 Why?
33 Because different editors expand or treat tabs in several ways; 8
34 spaces being the most common, but that makes code really wide and
35 we'll try to keep it within the 80 columns bounds (printer friendly).
36
37 Function commenting:
38
39 Every single function of the module should start with a short comment
40 that explains it's purpose; three lines must be enough, but if you
41 think it requires more, enlarge it.
42
43 /*
44 * Try finding the url in the cache. If it hits, send the contents
45 * to the caller. If it misses, set up a new connection.
46 */
47 int a_Cache_open_url(const char *url, void *Data)
48 {
49 ...
50 ...
51 ...
52 }
53
54 We also have the BUG: and todo: tags.
55 Use them within source code comments to spot hidden issues. For
56 instance:
57
58 /* BUG: this counter is not accurate */
59 ++i;
60
61 /* todo: get color from the right place */
62 a = color;
63 _________________________________________________________________
64
65 What do we get with this?
66
67 * A clear module API for Dillo; every function prefixed "a_" is to
68 be used outside the module.
69 * A way for identifying where the function came from (the
70 capitalized word is the module name).
71 * An inner ADT (Abstract data type) for the module. That can be
72 isolated, tested and replaced independently.
73 * A two stage instance for bug-fixing. You can change the exported
74 function algorithms while someone else fixes the internal
75 module-ADT!
76 * A coding standard ;)
77 _________________________________________________________________
78
79 Naming&Coding design by Jorge Arellano Cid
0
1 OK, here we are:
2
3
4 --------------------------------------------------------------------------
5 FILE DESCRIPTION STATE
6 --------------------------------------------------------------------------
7 NC_design.txt Naming&Coding design (Be sure to Current
8 read it before any other doc)
9 Dillo.txt General overview of the program Current
10 IO.txt Extensive introduction Current
11 Cache.txt Informative description Current
12 Images.txt Image handling and processing Current
13 HtmlParser.txt A versatile parser Current
14 Dw.txt The New Dillo Widget (Overview) Current
15 DwWidget.txt The base object of Dw Current
16 DwImage.txt Dillo Widget image handling Incomplete
17 DwPage.txt Dillo Widget page (shortly) Incomplete
18 DwStyle.txt Styles of Dillo Widgets Pending
19 DwTable.txt Tables in dillo Current
20 Selection.txt Selections, and link activation Current (?)
21 Cookies.txt Explains how to enable cookies Current
22 Dpid.txt Dillo plugin daemon Current
23 --------------------------------------------------------------------------
24 [This documents cover dillo's internal working. They're NOT a user manual]
25 --------------------------------------------------------------------------
26
27
28 * Ah!, there's a small program (srch) within the src/ dir. It searches
29 tokens within the whole code (*.[ch]). It has proven very useful.
30 Ex: ./srch a_Image_write
31 ./srch todo:
32
33 * Please submit your patches with 'diff -pru'.
34
35
36 Happy coding!
37 --Jcid
0 Apr 2003, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =========
4 Selection
5 =========
6
7 The selection module (selection.[ch]) handles selections, as well as
8 activation of links, which is closely related.
9
10
11 General Overview
12 ================
13
14 The selection module defines a structure "Selection", which is
15 associated to GtkDwViewport, and so to a widget tree. The selection
16 state is controlled by "abstract events", which are sent by single
17 widgets by calling one of the following functions:
18
19 a_Selection_button_press for button press events,
20 a_Selection_button_release for button release events, and
21 a_Selection_button_motion for motion events (with pressed mouse
22 button).
23
24 The widget must construct simple iterators (DwIterator), which will be
25 transferred to extended iterators (DwExtIterator), see below for more
26 details. All event handling functions have the same signature, the
27 arguments in detail are:
28
29 - DwIterator *it the iterator pointing on the item under
30 the mouse pointer,
31 - gint char_pos the exact (character) position within
32 the iterator,
33 - gint link if this item is associated with a link,
34 its number (see DwImage, section
35 "signals" for the meaning), otherwise
36 -1,
37 - GdkEventButton *event the event itself; only the button is
38 used,
39 - gboolean within_content TRUE, if there is some selectable
40 content unter the mouse cursor; if set
41 to FALSE, the "full screen" feature is
42 used on double click.
43
44 In some cases, char_pos would be difficult to determine. E.g., when
45 the DwPage widget decides that the user is pointing on a position
46 _at_the_end_ of an image (DwImage), it constructs a simple iterator
47 pointing on this image widget. In a simple iterator, that fact that
48 the pointer is at the end, would be represented by char_pos == 1. But
49 when transferring this simple iterator into an extended iterator, this
50 simple iterator is discarded and instead the stack has an iterator
51 pointing to text at the top. As a result, only the first letter of the
52 ALT text would be copied.
53
54 To avoid this problem, widgets should in this case pass SELECTION_EOW
55 (end of word) as char_pos, which is then automatically reduced to the
56 actual length of the extended(!) iterator.
57
58 The return value is the same as in DwWidget event handling methods.
59 I.e., in most cases, they should simply return it. The events
60 "link_pressed", "link_released" and "link_clicked" (but not
61 "link_entered") are emitted by these functions, so that widgets which
62 let the selection module handle links, should only emit "link_entered"
63 for themselves. (See DwImage.txt for a description of this.)
64
65
66 Selection State
67 ===============
68
69 Selection interferes with handling the activation of links, so the
70 latter is also handled by the selection module. Details are based on
71 following guidelines:
72
73 1. It should be simple to select links and to start selection in
74 links. The rule to distinguish between link activation and
75 selection is that the selection starts as soon as the user leaves
76 the link. (This is, IMO, a useful feature. Even after drag and
77 drop has been implemented in dillo, this should be somehow
78 preserved.)
79
80 2. The selection should stay as long as possible, i.e., the old
81 selection is only cleared when a new selection is started.
82
83 The latter leads to a model with two states: the selection state and
84 the link handling state.
85
86 The general selection works, for events not pointing on links, like
87 this (numbers in parantheses after the event denote the button, "n"
88 means arbitrary button):
89
90 motion(1)
91 ,-----.
92 | |
93 press(1) on non-link V |
94 NONE -----------------------> SELECTING <----------------.
95 ^ | |
96 | | release(1) |
97 | | | press(1)
98 | no V yes |
99 `----------------------- Anything selected? --------> SELECTED
100
101 The selected region is represented by two DwExtIterators.
102
103 Links are handled by a different state machine:
104
105 ,-----------------------------.
106 | |
107 | Switch to selection
108 | (SELECTING) for n == 1.
109 | ^
110 | | no
111 | | yes
112 | Still the same link? --.
113 | ^ |
114 | | |
115 | | motion(n) |
116 V press(n) on links | |
117 NONE ---------------------> PRESSED(n) <-----'
118 ^ |
119 | | release(n)
120 | |
121 | V yes
122 | Still the same link? -----------------.
123 | | |
124 | | no V
125 | V Send "clicked" signal.
126 | Switch to selection |
127 | (SELECTED) for n == 1. |
128 | | |
129 |`----------------------------' |
130 | |
131 `----------------------------------------------------------'
132
133 Switching to selection simply means that the selection state will
134 eventually be SELECTED/SELECTING, with the original and the actual
135 position making up the selection region. This happens for button 1,
136 events with buttons other than 1 do not affect selection at all.
137
138
139 TODO
140 ====
141
142 * a_Selection_button_motion currently always assumes that button 1 has
143 been pressed (since otherwise it would not do anything). This should
144 be made a bit cleaner.
145
146 * The selection should be cleared, when the user selects something
147 somewhere else (perhaps switched into "non-active" mode, as some
148 Gtk+ widgets do).
0 AM_CFLAGS = @GLIB_CFLAGS@
1 AM_CXXFLAGS = @GLIB_CFLAGS@
2
3 bookmarksdir = $(libdir)/dillo/dpi/bookmarks
4 downloadsdir = $(libdir)/dillo/dpi/downloads
5 ftpdir = $(libdir)/dillo/dpi/ftp
6 httpsdir = $(libdir)/dillo/dpi/https
7 hellodir = $(libdir)/dillo/dpi/hello
8 filedir = $(libdir)/dillo/dpi/file
9 cookiesdir = $(libdir)/dillo/dpi/cookies
10 datauridir = $(libdir)/dillo/dpi/datauri
11 bookmarks_PROGRAMS = bookmarks.dpi
12 downloads_PROGRAMS = downloads.dpi
13 ftp_PROGRAMS = ftp.filter.dpi
14 https_PROGRAMS = https.filter.dpi
15 hello_PROGRAMS = hello.filter.dpi
16 file_PROGRAMS = file.dpi
17 cookies_PROGRAMS = cookies.dpi
18 datauri_PROGRAMS = datauri.filter.dpi
19
20 bookmarks_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
21 if DLGUI
22 downloads_dpi_LDADD = @GLIB_LIBS@ @LIBFLTK_LIBS@ ../dpip/libDpip.a
23 else
24 downloads_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
25 endif
26 ftp_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
27 https_filter_dpi_LDADD = @GLIB_LIBS@ @LIBSSL_LIBS@ ../dpip/libDpip.a
28 hello_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
29 file_dpi_LDADD = @GLIB_LIBS@ @LIBPTHREAD_LIBS@ ../dpip/libDpip.a
30 cookies_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
31 datauri_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
32
33 file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
34
35 bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
36 if DLGUI
37 downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
38 else
39 downloads_dpi_SOURCES = downloads-old.c dpiutil.c dpiutil.h
40 endif
41 ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
42 https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
43 hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
44 file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
45 cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
46 datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
47
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(bookmarks_dpi_SOURCES) $(cookies_dpi_SOURCES) $(datauri_filter_dpi_SOURCES) $(downloads_dpi_SOURCES) $(file_dpi_SOURCES) $(ftp_filter_dpi_SOURCES) $(hello_filter_dpi_SOURCES) $(https_filter_dpi_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 bookmarks_PROGRAMS = bookmarks.dpi$(EXEEXT)
42 downloads_PROGRAMS = downloads.dpi$(EXEEXT)
43 ftp_PROGRAMS = ftp.filter.dpi$(EXEEXT)
44 https_PROGRAMS = https.filter.dpi$(EXEEXT)
45 hello_PROGRAMS = hello.filter.dpi$(EXEEXT)
46 file_PROGRAMS = file.dpi$(EXEEXT)
47 cookies_PROGRAMS = cookies.dpi$(EXEEXT)
48 datauri_PROGRAMS = datauri.filter.dpi$(EXEEXT)
49 subdir = dpi
50 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
51 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
52 am__aclocal_m4_deps = $(top_srcdir)/configure.in
53 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
54 $(ACLOCAL_M4)
55 mkinstalldirs = $(install_sh) -d
56 CONFIG_HEADER = $(top_builddir)/config.h
57 CONFIG_CLEAN_FILES =
58 am__installdirs = "$(DESTDIR)$(bookmarksdir)" \
59 "$(DESTDIR)$(cookiesdir)" "$(DESTDIR)$(datauridir)" \
60 "$(DESTDIR)$(downloadsdir)" "$(DESTDIR)$(filedir)" \
61 "$(DESTDIR)$(ftpdir)" "$(DESTDIR)$(hellodir)" \
62 "$(DESTDIR)$(httpsdir)"
63 bookmarksPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
64 cookiesPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
65 datauriPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
66 downloadsPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
67 filePROGRAMS_INSTALL = $(INSTALL_PROGRAM)
68 ftpPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
69 helloPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
70 httpsPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
71 PROGRAMS = $(bookmarks_PROGRAMS) $(cookies_PROGRAMS) \
72 $(datauri_PROGRAMS) $(downloads_PROGRAMS) $(file_PROGRAMS) \
73 $(ftp_PROGRAMS) $(hello_PROGRAMS) $(https_PROGRAMS)
74 am_bookmarks_dpi_OBJECTS = bookmarks.$(OBJEXT) dpiutil.$(OBJEXT)
75 bookmarks_dpi_OBJECTS = $(am_bookmarks_dpi_OBJECTS)
76 bookmarks_dpi_DEPENDENCIES = ../dpip/libDpip.a
77 am_cookies_dpi_OBJECTS = cookies.$(OBJEXT) dpiutil.$(OBJEXT)
78 cookies_dpi_OBJECTS = $(am_cookies_dpi_OBJECTS)
79 cookies_dpi_DEPENDENCIES = ../dpip/libDpip.a
80 am_datauri_filter_dpi_OBJECTS = datauri.$(OBJEXT) dpiutil.$(OBJEXT)
81 datauri_filter_dpi_OBJECTS = $(am_datauri_filter_dpi_OBJECTS)
82 datauri_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
83 am__downloads_dpi_SOURCES_DIST = downloads-old.c dpiutil.c dpiutil.h \
84 downloads.cc
85 @DLGUI_FALSE@am_downloads_dpi_OBJECTS = downloads-old.$(OBJEXT) \
86 @DLGUI_FALSE@ dpiutil.$(OBJEXT)
87 @DLGUI_TRUE@am_downloads_dpi_OBJECTS = downloads.$(OBJEXT) \
88 @DLGUI_TRUE@ dpiutil.$(OBJEXT)
89 downloads_dpi_OBJECTS = $(am_downloads_dpi_OBJECTS)
90 @DLGUI_FALSE@downloads_dpi_DEPENDENCIES = ../dpip/libDpip.a
91 @DLGUI_TRUE@downloads_dpi_DEPENDENCIES = ../dpip/libDpip.a
92 am_file_dpi_OBJECTS = file.$(OBJEXT) dpiutil.$(OBJEXT)
93 file_dpi_OBJECTS = $(am_file_dpi_OBJECTS)
94 file_dpi_DEPENDENCIES = ../dpip/libDpip.a
95 am_ftp_filter_dpi_OBJECTS = ftp.$(OBJEXT) dpiutil.$(OBJEXT)
96 ftp_filter_dpi_OBJECTS = $(am_ftp_filter_dpi_OBJECTS)
97 ftp_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
98 am_hello_filter_dpi_OBJECTS = hello.$(OBJEXT) dpiutil.$(OBJEXT)
99 hello_filter_dpi_OBJECTS = $(am_hello_filter_dpi_OBJECTS)
100 hello_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
101 am_https_filter_dpi_OBJECTS = https.$(OBJEXT) dpiutil.$(OBJEXT)
102 https_filter_dpi_OBJECTS = $(am_https_filter_dpi_OBJECTS)
103 https_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
104 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
105 depcomp = $(SHELL) $(top_srcdir)/depcomp
106 am__depfiles_maybe = depfiles
107 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
108 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
109 CCLD = $(CC)
110 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
111 CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
112 $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
113 CXXLD = $(CXX)
114 CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
115 -o $@
116 SOURCES = $(bookmarks_dpi_SOURCES) $(cookies_dpi_SOURCES) \
117 $(datauri_filter_dpi_SOURCES) $(downloads_dpi_SOURCES) \
118 $(file_dpi_SOURCES) $(ftp_filter_dpi_SOURCES) \
119 $(hello_filter_dpi_SOURCES) $(https_filter_dpi_SOURCES)
120 DIST_SOURCES = $(bookmarks_dpi_SOURCES) $(cookies_dpi_SOURCES) \
121 $(datauri_filter_dpi_SOURCES) \
122 $(am__downloads_dpi_SOURCES_DIST) $(file_dpi_SOURCES) \
123 $(ftp_filter_dpi_SOURCES) $(hello_filter_dpi_SOURCES) \
124 $(https_filter_dpi_SOURCES)
125 ETAGS = etags
126 CTAGS = ctags
127 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
128 ACLOCAL = @ACLOCAL@
129 AMDEP_FALSE = @AMDEP_FALSE@
130 AMDEP_TRUE = @AMDEP_TRUE@
131 AMTAR = @AMTAR@
132 AUTOCONF = @AUTOCONF@
133 AUTOHEADER = @AUTOHEADER@
134 AUTOMAKE = @AUTOMAKE@
135 AWK = @AWK@
136 CC = @CC@
137 CCDEPMODE = @CCDEPMODE@
138 CFLAGS = @CFLAGS@
139 CPP = @CPP@
140 CPPFLAGS = @CPPFLAGS@
141 CXX = @CXX@
142 CXXDEPMODE = @CXXDEPMODE@
143 CXXFLAGS = @CXXFLAGS@
144 CYGPATH_W = @CYGPATH_W@
145 DEFS = @DEFS@
146 DEPDIR = @DEPDIR@
147 DLGUI_FALSE = @DLGUI_FALSE@
148 DLGUI_TRUE = @DLGUI_TRUE@
149 ECHO_C = @ECHO_C@
150 ECHO_N = @ECHO_N@
151 ECHO_T = @ECHO_T@
152 EGREP = @EGREP@
153 EXEEXT = @EXEEXT@
154 GLIB_CFLAGS = @GLIB_CFLAGS@
155 GLIB_CONFIG = @GLIB_CONFIG@
156 GLIB_LIBS = @GLIB_LIBS@
157 GTK_CFLAGS = @GTK_CFLAGS@
158 GTK_CONFIG = @GTK_CONFIG@
159 GTK_LIBS = @GTK_LIBS@
160 INSTALL_DATA = @INSTALL_DATA@
161 INSTALL_PROGRAM = @INSTALL_PROGRAM@
162 INSTALL_SCRIPT = @INSTALL_SCRIPT@
163 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
164 LDFLAGS = @LDFLAGS@
165 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
166 LIBFLTK_LIBS = @LIBFLTK_LIBS@
167 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
168 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
169 LIBJPEG_LIBS = @LIBJPEG_LIBS@
170 LIBOBJS = @LIBOBJS@
171 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
172 LIBPNG_LIBS = @LIBPNG_LIBS@
173 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
174 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
175 LIBS = @LIBS@
176 LIBSSL_LIBS = @LIBSSL_LIBS@
177 LIBZ_LIBS = @LIBZ_LIBS@
178 LTLIBOBJS = @LTLIBOBJS@
179 MAKEINFO = @MAKEINFO@
180 OBJEXT = @OBJEXT@
181 PACKAGE = @PACKAGE@
182 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
183 PACKAGE_NAME = @PACKAGE_NAME@
184 PACKAGE_STRING = @PACKAGE_STRING@
185 PACKAGE_TARNAME = @PACKAGE_TARNAME@
186 PACKAGE_VERSION = @PACKAGE_VERSION@
187 PATH_SEPARATOR = @PATH_SEPARATOR@
188 RANLIB = @RANLIB@
189 SET_MAKE = @SET_MAKE@
190 SHELL = @SHELL@
191 STRIP = @STRIP@
192 VERSION = @VERSION@
193 ac_ct_CC = @ac_ct_CC@
194 ac_ct_CXX = @ac_ct_CXX@
195 ac_ct_RANLIB = @ac_ct_RANLIB@
196 ac_ct_STRIP = @ac_ct_STRIP@
197 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
198 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
199 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
200 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
201 am__include = @am__include@
202 am__leading_dot = @am__leading_dot@
203 am__quote = @am__quote@
204 am__tar = @am__tar@
205 am__untar = @am__untar@
206 bindir = @bindir@
207 build = @build@
208 build_alias = @build_alias@
209 build_cpu = @build_cpu@
210 build_os = @build_os@
211 build_vendor = @build_vendor@
212 datadir = @datadir@
213 exec_prefix = @exec_prefix@
214 host = @host@
215 host_alias = @host_alias@
216 host_cpu = @host_cpu@
217 host_os = @host_os@
218 host_vendor = @host_vendor@
219 includedir = @includedir@
220 infodir = @infodir@
221 install_sh = @install_sh@
222 libdir = @libdir@
223 libexecdir = @libexecdir@
224 localstatedir = @localstatedir@
225 mandir = @mandir@
226 mkdir_p = @mkdir_p@
227 oldincludedir = @oldincludedir@
228 prefix = @prefix@
229 program_transform_name = @program_transform_name@
230 sbindir = @sbindir@
231 sharedstatedir = @sharedstatedir@
232 sysconfdir = @sysconfdir@
233 target = @target@
234 target_alias = @target_alias@
235 target_cpu = @target_cpu@
236 target_os = @target_os@
237 target_vendor = @target_vendor@
238 AM_CFLAGS = @GLIB_CFLAGS@
239 AM_CXXFLAGS = @GLIB_CFLAGS@
240 bookmarksdir = $(libdir)/dillo/dpi/bookmarks
241 downloadsdir = $(libdir)/dillo/dpi/downloads
242 ftpdir = $(libdir)/dillo/dpi/ftp
243 httpsdir = $(libdir)/dillo/dpi/https
244 hellodir = $(libdir)/dillo/dpi/hello
245 filedir = $(libdir)/dillo/dpi/file
246 cookiesdir = $(libdir)/dillo/dpi/cookies
247 datauridir = $(libdir)/dillo/dpi/datauri
248 bookmarks_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
249 @DLGUI_FALSE@downloads_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
250 @DLGUI_TRUE@downloads_dpi_LDADD = @GLIB_LIBS@ @LIBFLTK_LIBS@ ../dpip/libDpip.a
251 ftp_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
252 https_filter_dpi_LDADD = @GLIB_LIBS@ @LIBSSL_LIBS@ ../dpip/libDpip.a
253 hello_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
254 file_dpi_LDADD = @GLIB_LIBS@ @LIBPTHREAD_LIBS@ ../dpip/libDpip.a
255 cookies_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
256 datauri_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
257 file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
258 bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
259 @DLGUI_FALSE@downloads_dpi_SOURCES = downloads-old.c dpiutil.c dpiutil.h
260 @DLGUI_TRUE@downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
261 ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
262 https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
263 hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
264 file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
265 cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
266 datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
267 all: all-am
268
269 .SUFFIXES:
270 .SUFFIXES: .c .cc .o .obj
271 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
272 @for dep in $?; do \
273 case '$(am__configure_deps)' in \
274 *$$dep*) \
275 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
276 && exit 0; \
277 exit 1;; \
278 esac; \
279 done; \
280 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dpi/Makefile'; \
281 cd $(top_srcdir) && \
282 $(AUTOMAKE) --gnu dpi/Makefile
283 .PRECIOUS: Makefile
284 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
285 @case '$?' in \
286 *config.status*) \
287 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
288 *) \
289 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
290 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
291 esac;
292
293 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
294 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
295
296 $(top_srcdir)/configure: $(am__configure_deps)
297 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
298 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
299 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
300 install-bookmarksPROGRAMS: $(bookmarks_PROGRAMS)
301 @$(NORMAL_INSTALL)
302 test -z "$(bookmarksdir)" || $(mkdir_p) "$(DESTDIR)$(bookmarksdir)"
303 @list='$(bookmarks_PROGRAMS)'; for p in $$list; do \
304 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
305 if test -f $$p \
306 ; then \
307 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
308 echo " $(INSTALL_PROGRAM_ENV) $(bookmarksPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bookmarksdir)/$$f'"; \
309 $(INSTALL_PROGRAM_ENV) $(bookmarksPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bookmarksdir)/$$f" || exit 1; \
310 else :; fi; \
311 done
312
313 uninstall-bookmarksPROGRAMS:
314 @$(NORMAL_UNINSTALL)
315 @list='$(bookmarks_PROGRAMS)'; for p in $$list; do \
316 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
317 echo " rm -f '$(DESTDIR)$(bookmarksdir)/$$f'"; \
318 rm -f "$(DESTDIR)$(bookmarksdir)/$$f"; \
319 done
320
321 clean-bookmarksPROGRAMS:
322 -test -z "$(bookmarks_PROGRAMS)" || rm -f $(bookmarks_PROGRAMS)
323 install-cookiesPROGRAMS: $(cookies_PROGRAMS)
324 @$(NORMAL_INSTALL)
325 test -z "$(cookiesdir)" || $(mkdir_p) "$(DESTDIR)$(cookiesdir)"
326 @list='$(cookies_PROGRAMS)'; for p in $$list; do \
327 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
328 if test -f $$p \
329 ; then \
330 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
331 echo " $(INSTALL_PROGRAM_ENV) $(cookiesPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(cookiesdir)/$$f'"; \
332 $(INSTALL_PROGRAM_ENV) $(cookiesPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(cookiesdir)/$$f" || exit 1; \
333 else :; fi; \
334 done
335
336 uninstall-cookiesPROGRAMS:
337 @$(NORMAL_UNINSTALL)
338 @list='$(cookies_PROGRAMS)'; for p in $$list; do \
339 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
340 echo " rm -f '$(DESTDIR)$(cookiesdir)/$$f'"; \
341 rm -f "$(DESTDIR)$(cookiesdir)/$$f"; \
342 done
343
344 clean-cookiesPROGRAMS:
345 -test -z "$(cookies_PROGRAMS)" || rm -f $(cookies_PROGRAMS)
346 install-datauriPROGRAMS: $(datauri_PROGRAMS)
347 @$(NORMAL_INSTALL)
348 test -z "$(datauridir)" || $(mkdir_p) "$(DESTDIR)$(datauridir)"
349 @list='$(datauri_PROGRAMS)'; for p in $$list; do \
350 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
351 if test -f $$p \
352 ; then \
353 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
354 echo " $(INSTALL_PROGRAM_ENV) $(datauriPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(datauridir)/$$f'"; \
355 $(INSTALL_PROGRAM_ENV) $(datauriPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(datauridir)/$$f" || exit 1; \
356 else :; fi; \
357 done
358
359 uninstall-datauriPROGRAMS:
360 @$(NORMAL_UNINSTALL)
361 @list='$(datauri_PROGRAMS)'; for p in $$list; do \
362 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
363 echo " rm -f '$(DESTDIR)$(datauridir)/$$f'"; \
364 rm -f "$(DESTDIR)$(datauridir)/$$f"; \
365 done
366
367 clean-datauriPROGRAMS:
368 -test -z "$(datauri_PROGRAMS)" || rm -f $(datauri_PROGRAMS)
369 install-downloadsPROGRAMS: $(downloads_PROGRAMS)
370 @$(NORMAL_INSTALL)
371 test -z "$(downloadsdir)" || $(mkdir_p) "$(DESTDIR)$(downloadsdir)"
372 @list='$(downloads_PROGRAMS)'; for p in $$list; do \
373 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
374 if test -f $$p \
375 ; then \
376 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
377 echo " $(INSTALL_PROGRAM_ENV) $(downloadsPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(downloadsdir)/$$f'"; \
378 $(INSTALL_PROGRAM_ENV) $(downloadsPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(downloadsdir)/$$f" || exit 1; \
379 else :; fi; \
380 done
381
382 uninstall-downloadsPROGRAMS:
383 @$(NORMAL_UNINSTALL)
384 @list='$(downloads_PROGRAMS)'; for p in $$list; do \
385 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
386 echo " rm -f '$(DESTDIR)$(downloadsdir)/$$f'"; \
387 rm -f "$(DESTDIR)$(downloadsdir)/$$f"; \
388 done
389
390 clean-downloadsPROGRAMS:
391 -test -z "$(downloads_PROGRAMS)" || rm -f $(downloads_PROGRAMS)
392 install-filePROGRAMS: $(file_PROGRAMS)
393 @$(NORMAL_INSTALL)
394 test -z "$(filedir)" || $(mkdir_p) "$(DESTDIR)$(filedir)"
395 @list='$(file_PROGRAMS)'; for p in $$list; do \
396 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
397 if test -f $$p \
398 ; then \
399 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
400 echo " $(INSTALL_PROGRAM_ENV) $(filePROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(filedir)/$$f'"; \
401 $(INSTALL_PROGRAM_ENV) $(filePROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(filedir)/$$f" || exit 1; \
402 else :; fi; \
403 done
404
405 uninstall-filePROGRAMS:
406 @$(NORMAL_UNINSTALL)
407 @list='$(file_PROGRAMS)'; for p in $$list; do \
408 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
409 echo " rm -f '$(DESTDIR)$(filedir)/$$f'"; \
410 rm -f "$(DESTDIR)$(filedir)/$$f"; \
411 done
412
413 clean-filePROGRAMS:
414 -test -z "$(file_PROGRAMS)" || rm -f $(file_PROGRAMS)
415 install-ftpPROGRAMS: $(ftp_PROGRAMS)
416 @$(NORMAL_INSTALL)
417 test -z "$(ftpdir)" || $(mkdir_p) "$(DESTDIR)$(ftpdir)"
418 @list='$(ftp_PROGRAMS)'; for p in $$list; do \
419 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
420 if test -f $$p \
421 ; then \
422 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
423 echo " $(INSTALL_PROGRAM_ENV) $(ftpPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ftpdir)/$$f'"; \
424 $(INSTALL_PROGRAM_ENV) $(ftpPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ftpdir)/$$f" || exit 1; \
425 else :; fi; \
426 done
427
428 uninstall-ftpPROGRAMS:
429 @$(NORMAL_UNINSTALL)
430 @list='$(ftp_PROGRAMS)'; for p in $$list; do \
431 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
432 echo " rm -f '$(DESTDIR)$(ftpdir)/$$f'"; \
433 rm -f "$(DESTDIR)$(ftpdir)/$$f"; \
434 done
435
436 clean-ftpPROGRAMS:
437 -test -z "$(ftp_PROGRAMS)" || rm -f $(ftp_PROGRAMS)
438 install-helloPROGRAMS: $(hello_PROGRAMS)
439 @$(NORMAL_INSTALL)
440 test -z "$(hellodir)" || $(mkdir_p) "$(DESTDIR)$(hellodir)"
441 @list='$(hello_PROGRAMS)'; for p in $$list; do \
442 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
443 if test -f $$p \
444 ; then \
445 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
446 echo " $(INSTALL_PROGRAM_ENV) $(helloPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(hellodir)/$$f'"; \
447 $(INSTALL_PROGRAM_ENV) $(helloPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(hellodir)/$$f" || exit 1; \
448 else :; fi; \
449 done
450
451 uninstall-helloPROGRAMS:
452 @$(NORMAL_UNINSTALL)
453 @list='$(hello_PROGRAMS)'; for p in $$list; do \
454 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
455 echo " rm -f '$(DESTDIR)$(hellodir)/$$f'"; \
456 rm -f "$(DESTDIR)$(hellodir)/$$f"; \
457 done
458
459 clean-helloPROGRAMS:
460 -test -z "$(hello_PROGRAMS)" || rm -f $(hello_PROGRAMS)
461 install-httpsPROGRAMS: $(https_PROGRAMS)
462 @$(NORMAL_INSTALL)
463 test -z "$(httpsdir)" || $(mkdir_p) "$(DESTDIR)$(httpsdir)"
464 @list='$(https_PROGRAMS)'; for p in $$list; do \
465 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
466 if test -f $$p \
467 ; then \
468 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
469 echo " $(INSTALL_PROGRAM_ENV) $(httpsPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(httpsdir)/$$f'"; \
470 $(INSTALL_PROGRAM_ENV) $(httpsPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(httpsdir)/$$f" || exit 1; \
471 else :; fi; \
472 done
473
474 uninstall-httpsPROGRAMS:
475 @$(NORMAL_UNINSTALL)
476 @list='$(https_PROGRAMS)'; for p in $$list; do \
477 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
478 echo " rm -f '$(DESTDIR)$(httpsdir)/$$f'"; \
479 rm -f "$(DESTDIR)$(httpsdir)/$$f"; \
480 done
481
482 clean-httpsPROGRAMS:
483 -test -z "$(https_PROGRAMS)" || rm -f $(https_PROGRAMS)
484 bookmarks.dpi$(EXEEXT): $(bookmarks_dpi_OBJECTS) $(bookmarks_dpi_DEPENDENCIES)
485 @rm -f bookmarks.dpi$(EXEEXT)
486 $(LINK) $(bookmarks_dpi_LDFLAGS) $(bookmarks_dpi_OBJECTS) $(bookmarks_dpi_LDADD) $(LIBS)
487 cookies.dpi$(EXEEXT): $(cookies_dpi_OBJECTS) $(cookies_dpi_DEPENDENCIES)
488 @rm -f cookies.dpi$(EXEEXT)
489 $(LINK) $(cookies_dpi_LDFLAGS) $(cookies_dpi_OBJECTS) $(cookies_dpi_LDADD) $(LIBS)
490 datauri.filter.dpi$(EXEEXT): $(datauri_filter_dpi_OBJECTS) $(datauri_filter_dpi_DEPENDENCIES)
491 @rm -f datauri.filter.dpi$(EXEEXT)
492 $(LINK) $(datauri_filter_dpi_LDFLAGS) $(datauri_filter_dpi_OBJECTS) $(datauri_filter_dpi_LDADD) $(LIBS)
493 downloads.dpi$(EXEEXT): $(downloads_dpi_OBJECTS) $(downloads_dpi_DEPENDENCIES)
494 @rm -f downloads.dpi$(EXEEXT)
495 $(CXXLINK) $(downloads_dpi_LDFLAGS) $(downloads_dpi_OBJECTS) $(downloads_dpi_LDADD) $(LIBS)
496 file.dpi$(EXEEXT): $(file_dpi_OBJECTS) $(file_dpi_DEPENDENCIES)
497 @rm -f file.dpi$(EXEEXT)
498 $(LINK) $(file_dpi_LDFLAGS) $(file_dpi_OBJECTS) $(file_dpi_LDADD) $(LIBS)
499 ftp.filter.dpi$(EXEEXT): $(ftp_filter_dpi_OBJECTS) $(ftp_filter_dpi_DEPENDENCIES)
500 @rm -f ftp.filter.dpi$(EXEEXT)
501 $(LINK) $(ftp_filter_dpi_LDFLAGS) $(ftp_filter_dpi_OBJECTS) $(ftp_filter_dpi_LDADD) $(LIBS)
502 hello.filter.dpi$(EXEEXT): $(hello_filter_dpi_OBJECTS) $(hello_filter_dpi_DEPENDENCIES)
503 @rm -f hello.filter.dpi$(EXEEXT)
504 $(LINK) $(hello_filter_dpi_LDFLAGS) $(hello_filter_dpi_OBJECTS) $(hello_filter_dpi_LDADD) $(LIBS)
505 https.filter.dpi$(EXEEXT): $(https_filter_dpi_OBJECTS) $(https_filter_dpi_DEPENDENCIES)
506 @rm -f https.filter.dpi$(EXEEXT)
507 $(LINK) $(https_filter_dpi_LDFLAGS) $(https_filter_dpi_OBJECTS) $(https_filter_dpi_LDADD) $(LIBS)
508
509 mostlyclean-compile:
510 -rm -f *.$(OBJEXT)
511
512 distclean-compile:
513 -rm -f *.tab.c
514
515 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bookmarks.Po@am__quote@
516 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookies.Po@am__quote@
517 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datauri.Po@am__quote@
518 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/downloads-old.Po@am__quote@
519 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/downloads.Po@am__quote@
520 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpiutil.Po@am__quote@
521 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@
522 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ftp.Po@am__quote@
523 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hello.Po@am__quote@
524 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/https.Po@am__quote@
525
526 .c.o:
527 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
528 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
529 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
530 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
531 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
532
533 .c.obj:
534 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
535 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
536 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
537 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
538 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
539
540 .cc.o:
541 @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
542 @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
543 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
544 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
545 @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
546
547 .cc.obj:
548 @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
549 @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
550 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
551 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
552 @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
553 uninstall-info-am:
554
555 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
556 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
557 unique=`for i in $$list; do \
558 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
559 done | \
560 $(AWK) ' { files[$$0] = 1; } \
561 END { for (i in files) print i; }'`; \
562 mkid -fID $$unique
563 tags: TAGS
564
565 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
566 $(TAGS_FILES) $(LISP)
567 tags=; \
568 here=`pwd`; \
569 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
570 unique=`for i in $$list; do \
571 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
572 done | \
573 $(AWK) ' { files[$$0] = 1; } \
574 END { for (i in files) print i; }'`; \
575 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
576 test -n "$$unique" || unique=$$empty_fix; \
577 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
578 $$tags $$unique; \
579 fi
580 ctags: CTAGS
581 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
582 $(TAGS_FILES) $(LISP)
583 tags=; \
584 here=`pwd`; \
585 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
586 unique=`for i in $$list; do \
587 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
588 done | \
589 $(AWK) ' { files[$$0] = 1; } \
590 END { for (i in files) print i; }'`; \
591 test -z "$(CTAGS_ARGS)$$tags$$unique" \
592 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
593 $$tags $$unique
594
595 GTAGS:
596 here=`$(am__cd) $(top_builddir) && pwd` \
597 && cd $(top_srcdir) \
598 && gtags -i $(GTAGS_ARGS) $$here
599
600 distclean-tags:
601 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
602
603 distdir: $(DISTFILES)
604 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
605 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
606 list='$(DISTFILES)'; for file in $$list; do \
607 case $$file in \
608 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
609 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
610 esac; \
611 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
612 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
613 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
614 dir="/$$dir"; \
615 $(mkdir_p) "$(distdir)$$dir"; \
616 else \
617 dir=''; \
618 fi; \
619 if test -d $$d/$$file; then \
620 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
621 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
622 fi; \
623 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
624 else \
625 test -f $(distdir)/$$file \
626 || cp -p $$d/$$file $(distdir)/$$file \
627 || exit 1; \
628 fi; \
629 done
630 check-am: all-am
631 check: check-am
632 all-am: Makefile $(PROGRAMS)
633 installdirs:
634 for dir in "$(DESTDIR)$(bookmarksdir)" "$(DESTDIR)$(cookiesdir)" "$(DESTDIR)$(datauridir)" "$(DESTDIR)$(downloadsdir)" "$(DESTDIR)$(filedir)" "$(DESTDIR)$(ftpdir)" "$(DESTDIR)$(hellodir)" "$(DESTDIR)$(httpsdir)"; do \
635 test -z "$$dir" || $(mkdir_p) "$$dir"; \
636 done
637 install: install-am
638 install-exec: install-exec-am
639 install-data: install-data-am
640 uninstall: uninstall-am
641
642 install-am: all-am
643 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
644
645 installcheck: installcheck-am
646 install-strip:
647 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
648 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
649 `test -z '$(STRIP)' || \
650 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
651 mostlyclean-generic:
652
653 clean-generic:
654
655 distclean-generic:
656 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
657
658 maintainer-clean-generic:
659 @echo "This command is intended for maintainers to use"
660 @echo "it deletes files that may require special tools to rebuild."
661 clean: clean-am
662
663 clean-am: clean-bookmarksPROGRAMS clean-cookiesPROGRAMS \
664 clean-datauriPROGRAMS clean-downloadsPROGRAMS \
665 clean-filePROGRAMS clean-ftpPROGRAMS clean-generic \
666 clean-helloPROGRAMS clean-httpsPROGRAMS mostlyclean-am
667
668 distclean: distclean-am
669 -rm -rf ./$(DEPDIR)
670 -rm -f Makefile
671 distclean-am: clean-am distclean-compile distclean-generic \
672 distclean-tags
673
674 dvi: dvi-am
675
676 dvi-am:
677
678 html: html-am
679
680 info: info-am
681
682 info-am:
683
684 install-data-am: install-bookmarksPROGRAMS install-cookiesPROGRAMS \
685 install-datauriPROGRAMS install-downloadsPROGRAMS \
686 install-filePROGRAMS install-ftpPROGRAMS install-helloPROGRAMS \
687 install-httpsPROGRAMS
688
689 install-exec-am:
690
691 install-info: install-info-am
692
693 install-man:
694
695 installcheck-am:
696
697 maintainer-clean: maintainer-clean-am
698 -rm -rf ./$(DEPDIR)
699 -rm -f Makefile
700 maintainer-clean-am: distclean-am maintainer-clean-generic
701
702 mostlyclean: mostlyclean-am
703
704 mostlyclean-am: mostlyclean-compile mostlyclean-generic
705
706 pdf: pdf-am
707
708 pdf-am:
709
710 ps: ps-am
711
712 ps-am:
713
714 uninstall-am: uninstall-bookmarksPROGRAMS uninstall-cookiesPROGRAMS \
715 uninstall-datauriPROGRAMS uninstall-downloadsPROGRAMS \
716 uninstall-filePROGRAMS uninstall-ftpPROGRAMS \
717 uninstall-helloPROGRAMS uninstall-httpsPROGRAMS \
718 uninstall-info-am
719
720 .PHONY: CTAGS GTAGS all all-am check check-am clean \
721 clean-bookmarksPROGRAMS clean-cookiesPROGRAMS \
722 clean-datauriPROGRAMS clean-downloadsPROGRAMS \
723 clean-filePROGRAMS clean-ftpPROGRAMS clean-generic \
724 clean-helloPROGRAMS clean-httpsPROGRAMS ctags distclean \
725 distclean-compile distclean-generic distclean-tags distdir dvi \
726 dvi-am html html-am info info-am install install-am \
727 install-bookmarksPROGRAMS install-cookiesPROGRAMS install-data \
728 install-data-am install-datauriPROGRAMS \
729 install-downloadsPROGRAMS install-exec install-exec-am \
730 install-filePROGRAMS install-ftpPROGRAMS install-helloPROGRAMS \
731 install-httpsPROGRAMS install-info install-info-am install-man \
732 install-strip installcheck installcheck-am installdirs \
733 maintainer-clean maintainer-clean-generic mostlyclean \
734 mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
735 tags uninstall uninstall-am uninstall-bookmarksPROGRAMS \
736 uninstall-cookiesPROGRAMS uninstall-datauriPROGRAMS \
737 uninstall-downloadsPROGRAMS uninstall-filePROGRAMS \
738 uninstall-ftpPROGRAMS uninstall-helloPROGRAMS \
739 uninstall-httpsPROGRAMS uninstall-info-am
740
741 # Tell versions [3.59,3.63) of GNU make to not export all variables.
742 # Otherwise a system limit (for SysV at least) may be exceeded.
743 .NOEXPORT:
0 /*
1 * Bookmarks server (chat version).
2 *
3 * NOTE: this code illustrates how to make a dpi-program.
4 *
5 * Copyright 2002-2006 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 */
13
14 /* Todo: this server is not assembling the received packets.
15 * This means it currently expects dillo to send full dpi tags
16 * within the socket; if that fails, everything stops.
17 * This is not hard to fix, mainly is a matter of expecting the
18 * final '>' of a tag.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/un.h>
32 #include <time.h>
33 #include <netdb.h>
34 #include <fcntl.h>
35 #include <signal.h>
36 #include "../dpip/dpip.h"
37 #include "dpiutil.h"
38
39 #include <glib.h>
40
41 /* This one is tricky, some sources state it should include the byte
42 * for the terminating NULL, and others say it shouldn't. */
43 # define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
44 + strlen ((ptr)->sun_path))
45
46 #define DOCTYPE \
47 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
48
49 /*
50 * Notes on character escaping:
51 * - Basically things are saved unescaped and escaped when in memory.
52 * - &<>"' are escaped in titles and sections and saved unescaped.
53 * - ' is escaped as %27 in URLs and saved escaped.
54 */
55 typedef struct {
56 gint key;
57 gint section;
58 gchar *url;
59 gchar *title;
60 } BmRec;
61
62 typedef struct {
63 gint section;
64 gchar *title;
65
66 gint o_sec; /* private, for normalization */
67 } BmSec;
68
69
70 /*
71 * Local data
72 */
73 static char *Header = "Content-type: text/html\n\n";
74 static char *BmFile = NULL;
75 static time_t BmFileTimeStamp = 0;
76 static GSList *B_bms = NULL;
77 static gint bm_key = 0;
78
79 static GSList *B_secs = NULL;
80 static gint sec_key = 0;
81
82 static int MODIFY_PAGE_NUM = 1;
83
84
85 /*
86 * Forward declarations
87 */
88
89
90 /* -- HTML templates ------------------------------------------------------- */
91
92 char *mainpage_header =
93 DOCTYPE
94 "<html>\n"
95 "<head>\n"
96 "<title>Bookmarks</title>\n"
97 "</head>\n"
98 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
99 "<table border='1' cellpadding='0' width='100%'>\n"
100 " <tr><td>\n"
101 " <table width='100%' bgcolor='#b4b4b4'>\n"
102 " <tr>\n"
103 " <td>&nbsp;Bookmarks::</td>\n"
104 " <td width='100%' align='right'>\n"
105 " [<a href='dpi:/bm/modify'>modify</a>]\n"
106 " </td></tr>\n"
107 " </table></td></tr>\n"
108 "</table>\n"
109 "<br>\n";
110
111 char *modifypage_header =
112 DOCTYPE
113 "<html>\n"
114 "<head>\n"
115 "<title>Bookmarks</title>\n"
116 "</head>\n"
117 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
118 "<table border='1' cellpadding='0' width='100%'>\n"
119 " <tr><td>\n"
120 " <table width='100%' bgcolor='#b4b4b4'>\n"
121 " <tr>\n"
122 " <td>&nbsp;Bookmarks :: modify</td></tr>\n"
123 " </table></td></tr> \n"
124 "</table> \n"
125 "\n"
126 "<form>\n"
127 "<table width='100%' border='1' cellpadding='0'>\n"
128 " <tr><td>\n"
129 " <table width='100%' bgcolor='teal'>\n"
130 " <tr>\n"
131 " <td><b>Select&nbsp;an&nbsp;operation&nbsp;</b></td>\n"
132 " <td><select name='operation'>\n"
133 " <option value='none' selected>--\n"
134 " <option value='delete'>Delete\n"
135 " <option value='move'>Move\n"
136 " <option value='modify'>Modify\n"
137 " <option value='add_sec'>Add Section\n"
138 " <option value='add_url'>Add URL\n"
139 " </select></td>\n"
140 " <td><b>,&nbsp;mark&nbsp;its&nbsp;operands,&nbsp;and&nbsp;</b></td>\n"
141 " <td><input type='submit' name='submit' value='submit.'></td>\n"
142 " <td width='100%'></td>\n"
143 " </tr>\n"
144 " </table></td></tr>\n"
145 "</table>\n";
146
147 char *mainpage_sections_header =
148 "<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
149 " <tr valign='top'>\n"
150 " <td>\n"
151 " <table bgcolor='#b4b4b4' border='2' cellpadding='4' cellspacing='1'>\n"
152 " <tr><td>\n"
153 " <table width='100%' bgcolor='#b4b4b4'>\n"
154 " <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
155
156 char *modifypage_sections_header =
157 "<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
158 " <tr valign='top'>\n"
159 " <td>\n"
160 " <table bgcolor='#b4b4b4' border='1'>\n"
161 " <tr><td>\n"
162 " <table width='100%' bgcolor='#b4b4b4'>\n"
163 " <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
164
165 char *mainpage_sections_item =
166 " <tr><td align='center'>\n"
167 " <a href='#s%d'>%s</a></td></tr>\n";
168
169 char *modifypage_sections_item =
170 " <tr><td>\n"
171 " <table width='100%%' bgcolor='#b4b4b4'cellspacing='0' cellpadding='0'>\n"
172 " <tr align='center'>"
173 " <td width='1%%'><input type='checkbox' name='s%d'></td>\n"
174 " <td><a href='#s%d'>%s</a></td></tr></table></td></tr>\n";
175
176 char *mainpage_sections_footer =
177 " </table>\n";
178
179 char *modifypage_sections_footer =
180 " </table>\n";
181
182 char *mainpage_middle1 =
183 " </td>\n"
184 " <td width='100%'>\n";
185
186 char *modifypage_middle1 =
187 " </td>\n"
188 " <td width='100%'>\n";
189
190 char *mainpage_section_card_header =
191 " <a name='s%d'></a>\n"
192 " <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
193 " <tr>\n"
194 " <td bgcolor='#bf0c0c'><font color='white'><b>\n"
195 " &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
196 " <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
197
198 char *modifypage_section_card_header =
199 " <a name='s%d'></a>\n"
200 " <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
201 " <tr>\n"
202 " <td bgcolor='#bf0c0c'><font color='white'><b>\n"
203 " &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
204 " <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
205
206 char *mainpage_section_card_item =
207 " <tr><td colspan='2'>\n"
208 " <a href='%s'>%s</a> </td></tr>\n";
209
210 char *modifypage_section_card_item =
211 " <tr>\n"
212 " <td colspan='2'><input type='checkbox' name='url%d'>\n"
213 " <a href='%s'>%s</a></td></tr>\n";
214
215 char *mainpage_section_card_footer =
216 " </table>\n"
217 " <hr>\n";
218
219 char *modifypage_section_card_footer =
220 " </table>\n"
221 " <hr>\n";
222
223 char *mainpage_footer =
224 " </td>\n"
225 " </tr>\n"
226 "</table>\n"
227 "</body>\n"
228 "</html>\n";
229
230 char *modifypage_footer =
231 " </td>\n"
232 " </tr>\n"
233 "</table>\n"
234 "</form>\n"
235 "</body>\n"
236 "</html>\n";
237
238 /* ------------------------------------------------------------------------- */
239 char *modifypage_add_section_page =
240 DOCTYPE
241 "<html>\n"
242 "<head>\n"
243 "<title>Bookmarks</title>\n"
244 "</head>\n"
245 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
246 "<table border='1' cellpadding='0' width='100%'>\n"
247 " <tr><td colspan='2'>\n"
248 " <table bgcolor='#b4b4b4' width='100%'>\n"
249 " <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add section\n"
250 " </td></tr></table></td></tr>\n"
251 "</table>\n"
252 "<br>\n"
253 "<form>\n"
254 " <input type='hidden' name='operation' value='add_section'>\n"
255 "<table border='1' width='100%'>\n"
256 " <tr>\n"
257 " <td bgcolor='olive'><b>New&nbsp;section:</b></td>\n"
258 " <td bgcolor='white' width='100%'></td></tr>\n"
259 "</table>\n"
260 "<table width='100%' cellpadding='10'>\n"
261 "<tr><td>\n"
262 " <table width='100%' bgcolor='teal'>\n"
263 " <tr>\n"
264 " <td>Title:</td>\n"
265 " <td><input type='text' name='title' size='64'></td></tr>\n"
266 " </table>\n"
267 " </td></tr>\n"
268 "</table>\n"
269 "<table width='100%' cellpadding='4' border='0'>\n"
270 "<tr><td bgcolor='#a0a0a0'>\n"
271 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
272 "</table>\n"
273 "</form>\n"
274 "</body>\n"
275 "</html>\n"
276 "\n";
277
278 /* ------------------------------------------------------------------------- */
279 char *modifypage_update_header =
280 DOCTYPE
281 "<html>\n"
282 "<head>\n"
283 "<title>Bookmarks</title>\n"
284 "</head>\n"
285 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
286 "<table border='1' cellpadding='0' width='100%'>\n"
287 " <tr><td colspan='2'>\n"
288 " <table bgcolor='#b4b4b4' width='100%'>\n"
289 " <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: update\n"
290 " </td></tr></table></td></tr>\n"
291 "</table>\n"
292 "<br>\n"
293 "<form>\n"
294 "<input type='hidden' name='operation' value='modify2'>\n";
295
296 char *modifypage_update_title =
297 "<table border='1' width='100%%'>\n"
298 " <tr>\n"
299 " <td bgcolor='olive'><b>%s</b></td>\n"
300 " <td bgcolor='white' width='100%%'></td></tr>\n"
301 "</table>\n";
302
303 char *modifypage_update_item_header =
304 "<table width='100%' cellpadding='10'>\n";
305
306 char *modifypage_update_item =
307 "<tr><td>\n"
308 " <table width='100%%' bgcolor='teal'>\n"
309 " <tr>\n"
310 " <td>Title:</td>\n"
311 " <td><input type='text' name='title%d' size='64'\n"
312 " value='%s'></td></tr>\n"
313 " <tr>\n"
314 " <td>URL:</td>\n"
315 " <td>%s</td></tr>\n"
316 " </table>\n"
317 " </td></tr>\n";
318
319 char *modifypage_update_item2 =
320 "<tr><td>\n"
321 " <table width='100%%' bgcolor='teal'>\n"
322 " <tr>\n"
323 " <td>Title:</td>\n"
324 " <td><input type='text' name='s%d' size='64'\n"
325 " value='%s'></td></tr>\n"
326 " </table>\n"
327 " </td></tr>\n";
328
329 char *modifypage_update_item_footer =
330 "</table>\n";
331
332 char *modifypage_update_footer =
333 "<table width='100%' cellpadding='4' border='0'>\n"
334 "<tr><td bgcolor='#a0a0a0'>\n"
335 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
336 "</table>\n"
337 "</form>\n"
338 "</body>\n"
339 "</html>\n";
340
341 /* ------------------------------------------------------------------------- */
342 char *modifypage_add_url =
343 DOCTYPE
344 "<html>\n"
345 "<head>\n"
346 "<title>Bookmarks</title>\n"
347 "</head>\n"
348 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
349 "<table border='1' cellpadding='0' width='100%'>\n"
350 " <tr><td colspan='2'>\n"
351 " <table bgcolor='#b4b4b4' width='100%'>\n"
352 " <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add url\n"
353 " </td></tr></table></td></tr>\n"
354 "</table>\n"
355 "<br>\n"
356 "<form>\n"
357 "<input type='hidden' name='operation' value='add_url2'>\n"
358 "<table border='1' width='100%'>\n"
359 " <tr>\n"
360 " <td bgcolor='olive'><b>Add&nbsp;url:</b></td>\n"
361 " <td bgcolor='white' width='100%'></td></tr>\n"
362 "</table>\n"
363 "<table width='100%' cellpadding='10'>\n"
364 "<tr><td>\n"
365 " <table width='100%' bgcolor='teal'>\n"
366 " <tr>\n"
367 " <td>Title:</td>\n"
368 " <td><input type='text' name='title' size='64'></td></tr>\n"
369 " <tr>\n"
370 " <td>URL:</td>\n"
371 " <td><input type='text' name='url' size='64'></td></tr>\n"
372 " </table>\n"
373 " </td></tr>\n"
374 "</table>\n"
375 "<table width='100%' cellpadding='4' border='0'>\n"
376 "<tr><td bgcolor='#a0a0a0'>\n"
377 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
378 "</table>\n"
379 "</form>\n"
380 "</body>\n"
381 "</html>\n";
382
383
384 /* ------------------------------------------------------------------------- */
385
386 /*
387 * Return a new string with spaces changed with &nbsp;
388 */
389 static char *make_one_line_str(char *str)
390 {
391 char *new_str;
392 int i, j, n;
393
394 for (i = 0, n = 0; str[i]; ++i)
395 if (str[i] == ' ')
396 ++n;
397
398 new_str = g_new(char, strlen(str) + 6*n + 1);
399 new_str[0] = 0;
400
401 for (i = 0, j = 0; str[i]; ++i) {
402 if (str[i] == ' ') {
403 strcpy(new_str + j, "&nbsp;");
404 j += 6;
405 } else {
406 new_str[j] = str[i];
407 new_str[++j] = 0;
408 }
409 }
410
411 return new_str;
412 }
413
414 /*
415 * Given an urlencoded string, return it to the original version.
416 */
417 static void Unencode_str(char *e_str)
418 {
419 char *p, *e;
420
421 for (p = e = e_str; *e; e++, p++) {
422 if (*e == '+') {
423 *p = ' ';
424 } else if (*e == '%') {
425 if (g_strncasecmp(e, "%0D%0A", 6) == 0) {
426 *p = '\n';
427 e += 5;
428 } else {
429 *p = (isdigit(e[1]) ? (e[1] - '0') : (e[1] - 'A' + 10)) * 16 +
430 (isdigit(e[2]) ? (e[2] - '0') : (e[2] - 'A' + 10));
431 e += 2;
432 }
433 } else {
434 *p = *e;
435 }
436 }
437 *p = 0;
438 }
439
440 /*
441 * Read a line of text up to the newline character, store it into a newly
442 * allocated string and return it.
443 */
444 static char *Get_line(FILE *stream)
445 {
446 guint i, size = 64;
447 int ch;
448 char *buf;
449
450 buf = g_new(char, size);
451
452 for (i = 0; (ch = fgetc(stream)) != EOF; ++i) {
453 if (i + 1 == size) {
454 size *= 2;
455 buf = g_realloc(buf, size);
456 }
457 if ((buf[i] = ch) == '\n' && ++i)
458 break;
459 }
460 buf[i] = 0;
461
462 if (i > 0) {
463 buf = g_realloc(buf, i + 1);
464 } else {
465 g_free(buf);
466 buf = NULL;
467 }
468 return buf;
469 }
470
471 /*
472 * Send a short message to dillo's status bar.
473 */
474 static int Bmsrv_dpi_send_status_msg(SockHandler *sh, char *str)
475 {
476 int st;
477 char *d_cmd;
478
479 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", str);
480 st = sock_handler_write_str(sh, d_cmd, 1);
481 g_free(d_cmd);
482 return st;
483 }
484
485 /* -- ADT for bookmarks ---------------------------------------------------- */
486 /*
487 * Compare function for searching a bookmark by its key
488 */
489 static gint Bms_key_cmp(gconstpointer node, gconstpointer key)
490 {
491 return ( GPOINTER_TO_INT(key) != ((BmRec *)node)->key );
492 }
493
494 /*
495 * Compare function for searching a bookmark by section
496 */
497 static gint Bms_bysec_cmp(gconstpointer node, gconstpointer key)
498 {
499 return ( GPOINTER_TO_INT(key) != ((BmRec *)node)->section );
500 }
501
502 /*
503 * Compare function for searching a section by its number
504 */
505 static gint Bms_sec_cmp(gconstpointer node, gconstpointer key)
506 {
507 return ( GPOINTER_TO_INT(key) != ((BmSec *)node)->section );
508 }
509
510 /*
511 * Return the Bm record by key
512 */
513 static BmRec *Bms_get(gint key)
514 {
515 GSList *list;
516
517 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key),
518 Bms_key_cmp);
519 return (list) ? list->data : NULL;
520 }
521
522 /*
523 * Return the Section record by key
524 */
525 static BmSec *Bms_get_sec(gint key)
526 {
527 GSList *list;
528
529 list = g_slist_find_custom(B_secs, GINT_TO_POINTER(key),
530 Bms_sec_cmp);
531 return (list) ? list->data : NULL;
532 }
533
534 /*
535 * Add a bookmark
536 */
537 static void Bms_add(gint section, char *url, char *title)
538 {
539 BmRec *node;
540
541 node = g_new(BmRec, 1);
542 node->key = ++bm_key;
543 node->section = section;
544 node->url = Escape_uri_str(url, "'");
545 node->title = Escape_html_str(title);
546 B_bms = g_slist_append(B_bms, node);
547 }
548
549 /*
550 * Add a section
551 */
552 static void Bms_sec_add(char *title)
553 {
554 BmSec *node;
555
556 node = g_new(BmSec, 1);
557 node->section = sec_key++;
558 node->title = Escape_html_str(title);
559 B_secs = g_slist_append(B_secs, node);
560 }
561
562 /*
563 * Delete a bookmark by its key
564 */
565 static void Bms_del(gint key)
566 {
567 GSList *list;
568 BmRec *node;
569
570 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key),
571 Bms_key_cmp);
572 if (list) {
573 node = list->data;
574 g_free(node->title);
575 g_free(node->url);
576 g_free(list->data);
577 B_bms = g_slist_remove(B_bms, list->data);
578 }
579 if (B_bms == NULL)
580 bm_key = 0;
581 }
582
583 /*
584 * Delete a section and its bookmarks by section number
585 */
586 static void Bms_sec_del(gint section)
587 {
588 GSList *list;
589 BmSec *sec_node;
590 BmRec *bm_node;
591
592 list = g_slist_find_custom(B_secs, GINT_TO_POINTER(section),
593 Bms_sec_cmp);
594 if (list) {
595 sec_node = list->data;
596 g_free(sec_node->title);
597 g_free(list->data);
598 B_secs = g_slist_remove(B_secs, list->data);
599
600 /* iterate B_bms and remove those that match the section */
601 while ((list = g_slist_find_custom(B_bms, GINT_TO_POINTER(section),
602 Bms_bysec_cmp))) {
603 bm_node = list->data;
604 Bms_del(bm_node->key);
605 }
606 }
607 if (B_secs == NULL)
608 sec_key = 0;
609 }
610
611 /*
612 * Move a bookmark to another section
613 */
614 static void Bms_move(gint key, gint target_section)
615 {
616 GSList *list;
617
618 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key), Bms_key_cmp);
619 if (list) {
620 BmRec *node = list->data;
621 node->section = target_section;
622 }
623 }
624
625 /*
626 * Update a bookmark title by key
627 */
628 static void Bms_update_title(gint key, gchar *n_title)
629 {
630 GSList *list;
631
632 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key), Bms_key_cmp);
633 if (list) {
634 BmRec *node = list->data;
635 g_free(node->title);
636 node->title = Escape_html_str(n_title);
637 }
638 }
639
640 /*
641 * Update a section title by key
642 */
643 static void Bms_update_sec_title(gint key, gchar *n_title)
644 {
645 GSList *list;
646
647 list = g_slist_find_custom(B_secs, GINT_TO_POINTER(key),
648 Bms_sec_cmp);
649 if (list) {
650 BmSec *node = list->data;
651 g_free(node->title);
652 node->title = Escape_html_str(n_title);
653 }
654 }
655
656 /*
657 * Free all the bookmarks data (bookmarks and sections)
658 */
659 static void Bms_free(void)
660 {
661 BmRec *bm_node;
662 BmSec *sec_node;
663
664 /* free B_bms */
665 while (B_bms) {
666 bm_node = B_bms->data;
667 Bms_del(bm_node->key);
668 }
669 /* free B_secs */
670 while (B_secs) {
671 sec_node = B_secs->data;
672 Bms_sec_del(sec_node->section);
673 }
674 }
675
676 /*
677 * Enforce increasing correlative section numbers with no jumps.
678 */
679 static void Bms_normalize(void)
680 {
681 BmRec *bm_node;
682 BmSec *sec_node;
683 GSList *list1, *list2;
684 gint n;
685
686 /* we need at least one section */
687 if (!B_secs)
688 Bms_sec_add("Unclassified");
689
690 /* make correlative section numbers */
691 n = 0;
692 for (list1 = B_secs; list1; list1 = list1->next) {
693 sec_node = list1->data;
694 sec_node->o_sec = sec_node->section;
695 sec_node->section = n++;
696 }
697
698 /* iterate B_secs and make the changes in B_bms */
699 for (list1 = B_secs; list1; list1 = list1->next) {
700 sec_node = list1->data;
701 if (sec_node->section != sec_node->o_sec) {
702 /* update section numbers */
703 for (list2 = B_bms; list2; list2 = list2->next) {
704 bm_node = list2->data;
705 if (bm_node->section == sec_node->o_sec)
706 bm_node->section = sec_node->section;
707 }
708 }
709 }
710 }
711
712 /* -- Load bookmarks file -------------------------------------------------- */
713
714 /*
715 * If there's no "bm.txt", create one from "bookmarks.html".
716 */
717 static void Bms_check_import(void)
718 {
719 gchar *OldBmFile;
720 char *cmd1 =
721 "echo \":s0: Unclassified\" > %s";
722 char *cmd2 =
723 "grep -i \"href\" %s | "
724 "sed -e 's/<li><A HREF=\"/s0 /' -e 's/\">/ /' -e 's/<.*$//' >> %s";
725 GString *gstr = g_string_new("");
726
727
728 if (access(BmFile, F_OK) != 0) {
729 OldBmFile = g_strconcat(g_get_home_dir(),
730 "/", ".dillo/bookmarks.html", NULL);
731 if (access(OldBmFile, F_OK) == 0) {
732 g_string_sprintf(gstr, cmd1, BmFile);
733 system(gstr->str);
734 g_string_sprintf(gstr, cmd2, OldBmFile, BmFile);
735 system(gstr->str);
736 g_string_free(gstr, TRUE);
737 g_free(OldBmFile);
738 }
739 }
740 }
741
742 /*
743 * Load bookmarks data from a file
744 */
745 static int Bms_load(void)
746 {
747 FILE *BmTxt;
748 char *buf, *p, *url, *title, *u_title;
749 int section;
750 struct stat TimeStamp;
751
752 /* clear current bookmarks */
753 Bms_free();
754
755 /* open bm file */
756 if (!(BmTxt = fopen(BmFile, "r"))) {
757 perror("[fopen]");
758 return 1;
759 }
760
761 /* load bm file into memory */
762 while ((buf = Get_line(BmTxt)) != NULL) {
763 if (buf[0] == 's') {
764 /* get section, url and title */
765 section = strtol(buf + 1, NULL, 10);
766 p = strchr(buf, ' '); *p = 0;
767 url = ++p;
768 p = strchr(p, ' '); *p = 0;
769 title = ++p;
770 p = strchr(p, '\n'); *p = 0;
771 u_title = Unescape_html_str(title);
772 Bms_add(section, url, u_title);
773 g_free(u_title);
774
775 } else if (buf[0] == ':' && buf[1] == 's') {
776 /* section = strtol(buf + 2, NULL, 10); */
777 p = strchr(buf + 2, ' ');
778 title = ++p;
779 p = strchr(p, '\n'); *p = 0;
780 Bms_sec_add(title);
781
782 } else {
783 g_print("Syntax error in bookmarks file:\n %s", buf);
784 }
785 g_free(buf);
786 }
787 fclose(BmTxt);
788
789 /* keep track of the timestamp */
790 stat(BmFile, &TimeStamp);
791 BmFileTimeStamp = TimeStamp.st_mtime;
792
793 return 0;
794 }
795
796 /*
797 * Load bookmarks data if:
798 * - file timestamp is newer than ours or
799 * - we haven't loaded anything yet :)
800 */
801 static int Bms_cond_load(void)
802 {
803 int st = 0;
804 struct stat TimeStamp;
805
806 if (stat(BmFile, &TimeStamp) != 0) {
807 /* try to import... */
808 Bms_check_import();
809 if (stat(BmFile, &TimeStamp) != 0)
810 TimeStamp.st_mtime = 0;
811 }
812
813 if (!BmFileTimeStamp || !B_bms || !B_secs ||
814 BmFileTimeStamp < TimeStamp.st_mtime) {
815 Bms_load();
816 st = 1;
817 }
818 return st;
819 }
820
821 /* -- Save bookmarks file -------------------------------------------------- */
822
823 /*
824 * Update the bookmarks file from memory contents
825 * Return code: { 0:OK, 1:Abort }
826 */
827 static int Bms_save(void)
828 {
829 FILE *BmTxt;
830 BmRec *bm_node;
831 BmSec *sec_node;
832 GSList *list, *list2;
833 struct stat BmStat;
834 gchar *u_title;
835 GString *gstr = g_string_new("");
836
837 /* make a safety backup */
838 if (stat(BmFile, &BmStat) == 0 && BmStat.st_size > 256) {
839 gchar *BmFileBak = g_strconcat(BmFile, ".bak", NULL);
840 rename(BmFile, BmFileBak);
841 g_free(BmFileBak);
842 }
843
844 /* open bm file */
845 if (!(BmTxt = fopen(BmFile, "w"))) {
846 perror("[fopen]");
847 return 1;
848 }
849
850 /* normalize */
851 Bms_normalize();
852
853 /* save sections */
854 for (list = B_secs; list; list = list->next) {
855 sec_node = list->data;
856 u_title = Unescape_html_str(sec_node->title);
857 g_string_sprintf(gstr, ":s%d: %s\n", sec_node->section, u_title);
858 fwrite(gstr->str, (size_t)gstr->len, 1, BmTxt);
859 g_free(u_title);
860 }
861
862 /* save bookmarks (section url title) */
863 for (list = B_secs; list; list = list->next) {
864 sec_node = list->data;
865 for (list2 = B_bms; list2; list2 = list2->next) {
866 bm_node = list2->data;
867 if (bm_node->section == sec_node->section) {
868 u_title = Unescape_html_str(bm_node->title);
869 g_string_sprintf(gstr, "s%d %s %s\n",
870 bm_node->section, bm_node->url, u_title);
871 fwrite(gstr->str, (size_t)gstr->len, 1, BmTxt);
872 g_free(u_title);
873 }
874 }
875 }
876
877 g_string_free(gstr, TRUE);
878 fclose(BmTxt);
879
880 /* keep track of the timestamp */
881 stat(BmFile, &BmStat);
882 BmFileTimeStamp = BmStat.st_mtime;
883
884 return 0;
885 }
886
887 /* -- Add bookmark --------------------------------------------------------- */
888
889 /*
890 * Add a new bookmark to DB :)
891 */
892 static int Bmsrv_add_bm(SockHandler *sh, char *url, char *title)
893 {
894 char *u_title;
895 char *msg="Added bookmark!";
896 int section = 0;
897
898 /* Add in memory */
899 u_title = Unescape_html_str(title);
900 Bms_add(section, url, u_title);
901 g_free(u_title);
902
903 /* Write to file */
904 Bms_save();
905
906 if (Bmsrv_dpi_send_status_msg(sh, msg))
907 return 1;
908
909 return 0;
910 }
911
912 /* -- Modify --------------------------------------------------------------- */
913
914 /*
915 * Count how many sections and urls were marked in a request
916 */
917 static void Bmsrv_count_urls_and_sections(char *url, gint *n_sec, gint *n_url)
918 {
919 char *p, *q;
920 int i;
921
922 /* Check marked urls and sections */
923 *n_sec = *n_url = 0;
924 if ((p = strchr(url, '?'))) {
925 for (q = p; (q = strstr(q, "&url")); ++q) {
926 for (i = 0; isdigit(q[4+i]); ++i);
927 *n_url += (q[4+i] == '=') ? 1 : 0;
928 }
929 for (q = p; (q = strstr(q, "&s")); ++q) {
930 for (i = 0; isdigit(q[2+i]); ++i);
931 *n_sec += (q[2+i] == '=') ? 1 : 0;
932 }
933 }
934 }
935
936 /*
937 * Send a dpi reload request
938 * Return code: { 0:OK, 1:Abort, 2:Close }
939 */
940 static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
941 {
942 gint st;
943 char *d_cmd;
944
945 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "reload_request", url);
946 st = sock_handler_write_str(sh, d_cmd, 1) ? 1 : 0;
947 g_free(d_cmd);
948 return st;
949 }
950
951 /*
952 * Send the HTML for the modify page
953 * Return code: { 0:OK, 1:Abort, 2:Close }
954 */
955 static int Bmsrv_send_modify_page(SockHandler *sh)
956 {
957 static GString *gstr = NULL;
958 gchar *l_title;
959 GSList *list1, *list2;
960 BmSec *sec_node;
961 BmRec *bm_node;
962
963 if (!gstr)
964 gstr = g_string_new("");
965
966 /* send modify page header */
967 if (sock_handler_write_str(sh, modifypage_header, 0))
968 return 1;
969
970 /* write sections header */
971 if (sock_handler_write_str(sh, modifypage_sections_header, 0))
972 return 1;
973 /* write sections */
974 for (list1 = B_secs; list1; list1 = list1->next) {
975 sec_node = list1->data;
976 g_string_sprintf(gstr, modifypage_sections_item,
977 sec_node->section, sec_node->section, sec_node->title);
978 if (sock_handler_write_str(sh, gstr->str, 0))
979 return 1;
980 }
981 /* write sections footer */
982 if (sock_handler_write_str(sh, modifypage_sections_footer, 0))
983 return 1;
984
985 /* send page middle */
986 if (sock_handler_write_str(sh, modifypage_middle1, 0))
987 return 1;
988
989 /* send bookmark cards */
990 for (list1 = B_secs; list1; list1 = list1->next) {
991 sec_node = list1->data;
992
993 /* send card header */
994 l_title = make_one_line_str(sec_node->title);
995 g_string_sprintf(gstr, modifypage_section_card_header,
996 sec_node->section, l_title);
997 g_free(l_title);
998 if (sock_handler_write_str(sh, gstr->str, 0))
999 return 1;
1000
1001 /* send section's bookmarks */
1002 for (list2 = B_bms; list2; list2 = list2->next) {
1003 bm_node = list2->data;
1004 if (bm_node->section == sec_node->section) {
1005 g_string_sprintf(gstr, modifypage_section_card_item,
1006 bm_node->key, bm_node->url, bm_node->title);
1007 if (sock_handler_write_str(sh, gstr->str, 0))
1008 return 1;
1009 }
1010 }
1011
1012 /* send card footer */
1013 if (sock_handler_write_str(sh, modifypage_section_card_footer, 0))
1014 return 1;
1015 }
1016
1017 /* finish page */
1018 if (sock_handler_write_str(sh, modifypage_footer, 1))
1019 return 1;
1020
1021 return 2;
1022 }
1023
1024 /*
1025 * Send the HTML for the modify page for "add section"
1026 * Return code: { 0:OK, 1:Abort, 2:Close }
1027 */
1028 static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
1029 {
1030 /* send modify page2 */
1031 if (sock_handler_write_str(sh, modifypage_add_section_page, 1))
1032 return 1;
1033
1034 return 2;
1035 }
1036
1037 /*
1038 * Send the HTML for the modify page for "add url"
1039 * Return code: { 0:OK, 1:Abort, 2:Close }
1040 */
1041 static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
1042 {
1043 if (sock_handler_write_str(sh, modifypage_add_url, 1))
1044 return 1;
1045 return 2;
1046 }
1047
1048 /*
1049 * Parse a modify urls request and either:
1050 * - make a local copy of the url
1051 * or
1052 * - send the modify page for the marked urls and sections
1053 * Return code: { 0:OK, 1:Abort, 2:Close }
1054 */
1055 static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
1056 {
1057 static char *url1 = NULL;
1058 static GString *gstr = NULL;
1059 char *p, *q;
1060 int i, key, n_sec, n_url;
1061 BmRec *bm_node;
1062 BmSec *sec_node;
1063
1064 /* bookmarks were loaded before */
1065
1066 if (!gstr)
1067 gstr = g_string_new("");
1068
1069 if (sh == NULL) {
1070 /* just copy url */
1071 g_free(url1);
1072 url1 = g_strdup(url);
1073 return 0;
1074 }
1075
1076 /* send HTML here */
1077 if (sock_handler_write_str(sh, modifypage_update_header, 0))
1078 return 1;
1079
1080 /* Count number of marked urls and sections */
1081 Bmsrv_count_urls_and_sections(url1, &n_sec, &n_url);
1082
1083 if (n_sec) {
1084 g_string_sprintf(gstr, modifypage_update_title, "Update&nbsp;sections:");
1085 sock_handler_write_str(sh, gstr->str, 0);
1086 sock_handler_write_str(sh, modifypage_update_item_header, 0);
1087 /* send items here */
1088 p = strchr(url1, '?');
1089 for (q = p; (q = strstr(q, "&s")); ++q) {
1090 for (i = 0; isdigit(q[2+i]); ++i);
1091 if (q[2+i] == '=') {
1092 key = strtol(q + 2, NULL, 10);
1093 if ((sec_node = Bms_get_sec(key))) {
1094 g_string_sprintf(gstr, modifypage_update_item2,
1095 sec_node->section, sec_node->title);
1096 sock_handler_write_str(sh, gstr->str, 0);
1097 }
1098 }
1099 }
1100 sock_handler_write_str(sh, modifypage_update_item_footer, 0);
1101 }
1102
1103 if (n_url) {
1104 g_string_sprintf(gstr, modifypage_update_title, "Update&nbsp;titles:");
1105 sock_handler_write_str(sh, gstr->str, 0);
1106 sock_handler_write_str(sh, modifypage_update_item_header, 0);
1107 /* send items here */
1108 p = strchr(url1, '?');
1109 for (q = p; (q = strstr(q, "&url")); ++q) {
1110 for (i = 0; isdigit(q[4+i]); ++i);
1111 if (q[4+i] == '=') {
1112 key = strtol(q + 4, NULL, 10);
1113 bm_node = Bms_get(key);
1114 g_string_sprintf(gstr, modifypage_update_item,
1115 bm_node->key, bm_node->title, bm_node->url);
1116 sock_handler_write_str(sh, gstr->str, 0);
1117 }
1118 }
1119 sock_handler_write_str(sh, modifypage_update_item_footer, 0);
1120 }
1121
1122 sock_handler_write_str(sh, modifypage_update_footer, 1);
1123
1124 return 2;
1125 }
1126
1127 /*
1128 * Make the modify-page and send it back
1129 * Return code: { 0:OK, 1:Abort, 2:Close }
1130 */
1131 static int Bmsrv_send_modify_answer(SockHandler *sh, char *url)
1132 {
1133 char *d_cmd;
1134 int st;
1135
1136 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
1137 st = sock_handler_write_str(sh, d_cmd, 1);
1138 g_free(d_cmd);
1139 if (st != 0)
1140 return 1;
1141
1142 /* Send HTTP header */
1143 if (sock_handler_write(sh, Header, strlen(Header), 0) != 0) {
1144 return 1;
1145 }
1146
1147 if (MODIFY_PAGE_NUM == 2) {
1148 MODIFY_PAGE_NUM = 1;
1149 return Bmsrv_send_modify_page_add_section(sh);
1150 } else if (MODIFY_PAGE_NUM == 3) {
1151 MODIFY_PAGE_NUM = 1;
1152 return Bmsrv_send_modify_update(sh, NULL);
1153 } else if (MODIFY_PAGE_NUM == 4) {
1154 MODIFY_PAGE_NUM = 1;
1155 return Bmsrv_send_modify_page_add_url(sh);
1156 } else {
1157 return Bmsrv_send_modify_page(sh);
1158 }
1159 }
1160
1161
1162 /* Operations */
1163
1164 /*
1165 * Parse a delete bms request, delete them, and update bm file.
1166 * Return code: { 0:OK, 1:Abort }
1167 */
1168 static int Bmsrv_modify_delete(SockHandler *sh, char *url)
1169 {
1170 gchar *p;
1171 gint nb, ns, key;
1172
1173 /* bookmarks were loaded before */
1174
1175 /* Remove marked sections */
1176 p = strchr(url, '?');
1177 for (ns = 0; (p = strstr(p, "&s")); ++p) {
1178 if (isdigit(p[2])) {
1179 key = strtol(p + 2, NULL, 10);
1180 Bms_sec_del(key);
1181 ++ns;
1182 }
1183 }
1184
1185 /* Remove marked urls */
1186 p = strchr(url, '?');
1187 for (nb = 0; (p = strstr(p, "&url")); ++p) {
1188 if (isdigit(p[4])) {
1189 key = strtol(p + 4, NULL, 10);
1190 Bms_del(key);
1191 ++nb;
1192 }
1193 }
1194
1195 /* -- This doesn't work because dillo erases the message upon the
1196 * receipt of the first data stream.
1197 *
1198 sprintf(msg, "Deleted %d bookmark%s!>", n, (n > 1) ? "s" : "");
1199 if (Bmsrv_dpi_send_status_msg(sh, msg))
1200 return 1;
1201 */
1202
1203 /* Write new bookmarks file */
1204 if (nb || ns)
1205 Bms_save();
1206
1207 return 0;
1208 }
1209
1210 /*
1211 * Parse a move urls request, move and update bm file.
1212 * Return code: { 0:OK, 1:Abort }
1213 */
1214 static int Bmsrv_modify_move(SockHandler *sh, char *url)
1215 {
1216 char *p;
1217 int n, section = 0, key;
1218
1219 /* bookmarks were loaded before */
1220
1221 /* get target section */
1222 for (p = url; (p = strstr(p, "&s")); ++p) {
1223 if (isdigit(p[2])) {
1224 section = strtol(p + 2, NULL, 10);
1225 break;
1226 }
1227 }
1228 if (!p)
1229 return 1;
1230
1231 /* move marked urls */
1232 p = strchr(url, '?');
1233 for (n = 0; (p = strstr(p, "&url")); ++p) {
1234 if (isdigit(p[4])) {
1235 key = strtol(p + 4, NULL, 10);
1236 Bms_move(key, section);
1237 ++n;
1238 }
1239 }
1240
1241 /* Write new bookmarks file */
1242 if (n) {
1243 Bms_save();
1244 }
1245
1246 return 0;
1247 }
1248
1249 /*
1250 * Parse a modify request: update urls and sections, then save.
1251 * Return code: { 0:OK, 1:Abort }
1252 */
1253 static int Bmsrv_modify_update(SockHandler *sh, char *url)
1254 {
1255 char *p, *q, *title;
1256 int i, key;
1257
1258 /* bookmarks were loaded before */
1259
1260 p = strchr(url, '?');
1261 for ( ; (p = strstr(p, "s")); ++p) {
1262 if (p[-1] == '&' || p[-1] == '?' ) {
1263 for (i = 0; isdigit(p[1 + i]); ++i);
1264 if (i && p[1 + i] == '=') {
1265 /* we have a title/key to change */
1266 key = strtol(p + 1, NULL, 10);
1267 if ((q = strchr(p + 1, '&')))
1268 title = g_strndup(p + 2 + i, (guint)(q - (p + 2 + i)));
1269 else
1270 title = g_strdup(p + 2 + i);
1271
1272 Unencode_str(title);
1273 Bms_update_sec_title(key, title);
1274 g_free(title);
1275 }
1276 }
1277 }
1278
1279 p = strchr(url, '?');
1280 for ( ; (p = strstr(p, "title")); ++p) {
1281 if (p[-1] == '&' || p[-1] == '?' ) {
1282 for (i = 0; isdigit(p[5 + i]); ++i);
1283 if (i && p[5 + i] == '=') {
1284 /* we have a title/key to change */
1285 key = strtol(p + 5, NULL, 10);
1286 if ((q = strchr(p + 5, '&')))
1287 title = g_strndup(p + 6 + i, (guint)(q - (p + 6 + i)));
1288 else
1289 title = g_strdup(p + 6 + i);
1290
1291 Unencode_str(title);
1292 Bms_update_title(key, title);
1293 g_free(title);
1294 }
1295 }
1296 }
1297
1298 /* Write new bookmarks file */
1299 Bms_save();
1300
1301 return 0;
1302 }
1303
1304 /*
1305 * Parse an "add section" request, and update the bm file.
1306 * Return code: { 0:OK, 1:Abort }
1307 */
1308 static int Bmsrv_modify_add_section(SockHandler *sh, char *url)
1309 {
1310 char *p, *title = NULL;
1311
1312 /* bookmarks were loaded before */
1313
1314 /* get new section's title */
1315 if ((p = strstr(url, "&title="))) {
1316 title = g_strdup (p + 7);
1317 if ((p = strchr(title, '&')))
1318 *p = 0;
1319 Unencode_str(title);
1320 } else
1321 return 1;
1322
1323 Bms_sec_add(title);
1324 g_free(title);
1325
1326 /* Write new bookmarks file */
1327 Bms_save();
1328
1329 return 0;
1330 }
1331
1332 /*
1333 * Parse an "add url" request, and update the bm file.
1334 * Return code: { 0:OK, 1:Abort }
1335 */
1336 static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
1337 {
1338 char *p, *q, *title, *u_title, *url;
1339 int i;
1340 static gint section = 0;
1341
1342 /* bookmarks were loaded before */
1343
1344 if (sh == NULL) {
1345 /* look for section */
1346 for (q = s_url; (q = strstr(q, "&s")); ++q) {
1347 for (i = 0; isdigit(q[2+i]); ++i);
1348 if (q[2+i] == '=')
1349 section = strtol(q + 2, NULL, 10);
1350 }
1351 return 1;
1352 }
1353
1354 if (!(p = strstr(s_url, "&title=")) ||
1355 !(q = strstr(s_url, "&url=")))
1356 return 1;
1357
1358 title = g_strdup (p + 7);
1359 if ((p = strchr(title, '&')))
1360 *p = 0;
1361 url = g_strdup (q + 5);
1362 if ((p = strchr(url, '&')))
1363 *p = 0;
1364 if (strlen(title) && strlen(url)) {
1365 Unencode_str(title);
1366 Unencode_str(url);
1367 u_title = Unescape_html_str(title);
1368 Bms_add(section, url, u_title);
1369 g_free(u_title);
1370 }
1371 g_free(title);
1372 g_free(url);
1373 section = 0;
1374
1375 /* todo: we should send an "Bookmark added" message, but the
1376 msg-after-HTML functionallity is still pending, not hard though. */
1377
1378 /* Write new bookmarks file */
1379 Bms_save();
1380
1381 return 0;
1382 }
1383
1384 /*
1385 * Check the parameters of a modify request, and return an error message
1386 * when it's wrong.
1387 * Return code: { 0:OK, 2:Close }
1388 */
1389 static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
1390 {
1391 char *p, *msg;
1392 int n_sec, n_url;
1393
1394 /* Count number of marked urls and sections */
1395 Bmsrv_count_urls_and_sections(url, &n_sec, &n_url);
1396
1397 p = strchr(url, '?');
1398 if (strstr(p, "operation=delete&")) {
1399 if (n_url || n_sec)
1400 return 0;
1401 msg = "Delete: you must mark what to delete!";
1402
1403 } else if (strstr(url, "operation=move&")) {
1404 if (n_url && n_sec)
1405 return 0;
1406 else if (n_url)
1407 msg = "Move: you must mark a target section!";
1408 else if (n_sec)
1409 msg = "Move: can not move a section (yet).";
1410 else
1411 msg = "Move: you must mark some urls, and a target section!";
1412
1413 } else if (strstr(url, "operation=modify&")) {
1414 if (n_url || n_sec)
1415 return 0;
1416 msg = "Modify: you must mark what to update!";
1417
1418 } else if (strstr(url, "operation=modify2&")) {
1419 /* nothing to check here */
1420 return 0;
1421
1422 } else if (strstr(url, "operation=add_sec&")) {
1423 /* nothing to check here */
1424 return 0;
1425
1426 } else if (strstr(url, "operation=add_section&")) {
1427 /* nothing to check here */
1428 return 0;
1429
1430 } else if (strstr(url, "operation=add_url&")) {
1431 if (n_sec <= 1)
1432 return 0;
1433 msg = "Add url: only one target section is allowed!";
1434
1435 } else if (strstr(url, "operation=add_url2&")) {
1436 /* nothing to check here */
1437 return 0;
1438
1439 } else if (strstr(url, "operation=none&")) {
1440 msg = "No operation, just do nothing!";
1441
1442 } else {
1443 msg = "Sorry, not implemented yet.";
1444 }
1445
1446 Bmsrv_dpi_send_status_msg(sh, msg);
1447 return 2;
1448 }
1449
1450 /*
1451 * Parse a and process a modify request.
1452 * Return code: { 0:OK, 1:Abort, 2:Close }
1453 */
1454 static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
1455 {
1456 /* check the provided parameters */
1457 if (Bmsrv_check_modify_request(sh, url) != 0)
1458 return 2;
1459
1460 if (strstr(url, "operation=delete&")) {
1461 if (Bmsrv_modify_delete(sh, url) == 1)
1462 return 1;
1463 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1464 return 1;
1465
1466 } else if (strstr(url, "operation=move&")) {
1467 if (Bmsrv_modify_move(sh, url) == 1)
1468 return 1;
1469 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1470 return 1;
1471
1472 } else if (strstr(url, "operation=modify&")) {
1473 /* make a local copy of 'url' */
1474 Bmsrv_send_modify_update(NULL, url);
1475 MODIFY_PAGE_NUM = 3;
1476 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1477 return 1;
1478
1479 } else if (strstr(url, "operation=modify2&")) {
1480 if (Bmsrv_modify_update(sh, url) == 1)
1481 return 1;
1482 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1483 return 1;
1484
1485 } else if (strstr(url, "operation=add_sec&")) {
1486 /* this global variable tells which page to send (--hackish...) */
1487 MODIFY_PAGE_NUM = 2;
1488 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1489 return 1;
1490
1491 } else if (strstr(url, "operation=add_section&")) {
1492 if (Bmsrv_modify_add_section(sh, url) == 1)
1493 return 1;
1494 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1495 return 1;
1496
1497 } else if (strstr(url, "operation=add_url&")) {
1498 /* this global variable tells which page to send (--hackish...) */
1499 MODIFY_PAGE_NUM = 4;
1500 /* parse section if present */
1501 Bmsrv_modify_add_url(NULL, url);
1502 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1503 return 1;
1504
1505 } else if (strstr(url, "operation=add_url2&")) {
1506 if (Bmsrv_modify_add_url(sh, url) == 1)
1507 return 1;
1508 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1509 return 1;
1510 }
1511
1512 return 2;
1513 }
1514
1515 /* -- Bookmarks ------------------------------------------------------------ */
1516
1517 /*
1518 * Send the current bookmarks page (in HTML)
1519 */
1520 static int send_bm_page(SockHandler *sh)
1521 {
1522 static GString *gstr = NULL;
1523 gchar *l_title;
1524 GSList *list1, *list2;
1525 BmSec *sec_node;
1526 BmRec *bm_node;
1527
1528 if (!gstr)
1529 gstr = g_string_new("");
1530
1531 if (sock_handler_write_str(sh, mainpage_header, 0))
1532 return 1;
1533
1534 /* write sections header */
1535 if (sock_handler_write_str(sh, mainpage_sections_header, 0))
1536 return 1;
1537 /* write sections */
1538 for (list1 = B_secs; list1; list1 = list1->next) {
1539 sec_node = list1->data;
1540 g_string_sprintf(gstr, mainpage_sections_item,
1541 sec_node->section, sec_node->title);
1542 if (sock_handler_write_str(sh, gstr->str, 0))
1543 return 1;
1544 }
1545 /* write sections footer */
1546 if (sock_handler_write_str(sh, mainpage_sections_footer, 0))
1547 return 1;
1548
1549 /* send page middle */
1550 if (sock_handler_write_str(sh, mainpage_middle1, 0))
1551 return 1;
1552
1553 /* send bookmark cards */
1554 for (list1 = B_secs; list1; list1 = list1->next) {
1555 sec_node = list1->data;
1556
1557 /* send card header */
1558 l_title = make_one_line_str(sec_node->title);
1559 g_string_sprintf(gstr, mainpage_section_card_header,
1560 sec_node->section, l_title);
1561 g_free(l_title);
1562 if (sock_handler_write_str(sh, gstr->str, 0))
1563 return 1;
1564
1565 /* send section's bookmarks */
1566 for (list2 = B_bms; list2; list2 = list2->next) {
1567 bm_node = list2->data;
1568 if (bm_node->section == sec_node->section) {
1569 g_string_sprintf(gstr, mainpage_section_card_item,
1570 bm_node->url, bm_node->title);
1571 if (sock_handler_write_str(sh, gstr->str, 0))
1572 return 1;
1573 }
1574 }
1575
1576 /* send card footer */
1577 if (sock_handler_write_str(sh, mainpage_section_card_footer, 0))
1578 return 1;
1579 }
1580
1581 /* finish page */
1582 if (sock_handler_write_str(sh, mainpage_footer, 1))
1583 return 1;
1584
1585 return 0;
1586 }
1587
1588
1589 /* -- Dpi parser ----------------------------------------------------------- */
1590
1591 /*
1592 * Parse a data stream (dpi protocol)
1593 * Note: Buf is a zero terminated string
1594 * Return code: { 0:OK, 1:Abort, 2:Close }
1595 */
1596 static int Bmsrv_parse_buf(SockHandler *sh, char *Buf, size_t BufSize)
1597 {
1598 static char *msg1=NULL, *msg2=NULL, *msg3=NULL;
1599 char *p, *cmd, *d_cmd, *url, *title, *msg;
1600 int st;
1601
1602 if (!msg1) {
1603 /* Initialize data for the "chat" command. */
1604 msg1 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Hi browser");
1605 msg2 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Is it worth?");
1606 msg3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Ok, send it");
1607 }
1608
1609 if (!(p = strchr(Buf, '>'))) {
1610 /* Haven't got a full tag */
1611 g_print("Haven't got a full tag!\n");
1612 return 1;
1613 }
1614
1615 cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
1616
1617 if (cmd && strcmp(cmd, "chat") == 0) {
1618 g_free(cmd);
1619 msg = a_Dpip_get_attr(Buf, BufSize, "msg");
1620 if (*msg == 'H') {
1621 /* "Hi server" */
1622 if (sock_handler_write_str(sh, msg1, 1))
1623 return 1;
1624 } else if (*msg == 'I') {
1625 /* "I want to set abookmark" */
1626 if (sock_handler_write_str(sh, msg2, 1))
1627 return 1;
1628 } else if (*msg == 'S') {
1629 /* "Sure" */
1630 if (sock_handler_write_str(sh, msg3, 1))
1631 return 1;
1632 }
1633 g_free(msg);
1634 return 0;
1635 }
1636
1637 /* sync with the bookmarks file */
1638 Bms_cond_load();
1639
1640 if (cmd && strcmp(cmd, "DpiBye") == 0) {
1641 g_print("bookmarks dpi (pid %d): Got DpiBye.\n", (gint)getpid());
1642 exit(0);
1643
1644 } else if (cmd && strcmp(cmd, "add_bookmark") == 0) {
1645 g_free(cmd);
1646 url = a_Dpip_get_attr(Buf, BufSize, "url");
1647 title = a_Dpip_get_attr(Buf, BufSize, "title");
1648 if (strlen(title) == 0) {
1649 g_free(title);
1650 title = g_strdup("(Untitled)");
1651 }
1652 if (url && title)
1653 Bmsrv_add_bm(sh, url, title);
1654 g_free(url);
1655 g_free(title);
1656 return 2;
1657
1658 } else if (cmd && strcmp(cmd, "open_url") == 0) {
1659 g_free(cmd);
1660 url = a_Dpip_get_attr(Buf, BufSize, "url");
1661
1662 if (strcmp(url, "dpi:/bm/modify") == 0) {
1663 st = Bmsrv_send_modify_answer(sh, url);
1664 return st;
1665
1666 } else if (strncmp(url, "dpi:/bm/modify?", 15) == 0) {
1667 /* process request */
1668 st = Bmsrv_process_modify_request(sh, url);
1669 return st;
1670 }
1671
1672
1673 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
1674 st = sock_handler_write_str(sh, d_cmd, 1);
1675 g_free(d_cmd);
1676 if (st != 0)
1677 return 1;
1678
1679 /* Send HTTP header */
1680 if (sock_handler_write(sh, Header, strlen(Header), 1) != 0) {
1681 return 1;
1682 }
1683
1684 st = send_bm_page(sh);
1685 if (st != 0) {
1686 char *err =
1687 DOCTYPE
1688 "<HTML><body> Error on the bookmarks server...</body></html>";
1689 if (sock_handler_write(sh, err, strlen(err), 1) != 0) {
1690 return 1;
1691 }
1692 }
1693 return 2;
1694 }
1695
1696 return 0;
1697 }
1698
1699 /* -- Termination handlers ----------------------------------------------- */
1700 /*
1701 * (was to delete the local namespace socket),
1702 * but this is handled by 'dpid' now.
1703 */
1704 static void cleanup(void)
1705 {
1706 /* no cleanup required */
1707 }
1708
1709 /*
1710 * Perform any necessary cleanups upon abnormal termination
1711 */
1712 static void termination_handler(int signum)
1713 {
1714 exit(signum);
1715 }
1716
1717
1718 /*
1719 * -- MAIN -------------------------------------------------------------------
1720 */
1721 int main (void) {
1722 struct sockaddr_un spun;
1723 int temp_sock_descriptor;
1724 int address_size;
1725 char *buf;
1726 int code;
1727 SockHandler *sh;
1728
1729 /* Arrange the cleanup function for terminations via exit() */
1730 atexit(cleanup);
1731
1732 /* Arrange the cleanup function for abnormal terminations */
1733 if (signal (SIGINT, termination_handler) == SIG_IGN)
1734 signal (SIGINT, SIG_IGN);
1735 if (signal (SIGHUP, termination_handler) == SIG_IGN)
1736 signal (SIGHUP, SIG_IGN);
1737 if (signal (SIGTERM, termination_handler) == SIG_IGN)
1738 signal (SIGTERM, SIG_IGN);
1739
1740 BmFile = g_strconcat(g_get_home_dir(), "/", ".dillo/bm.txt", NULL);
1741
1742 g_print("bookmarks.dpi (v.13): accepting connections...\n");
1743
1744 /* some OSes may need this... */
1745 address_size = sizeof(struct sockaddr_un);
1746
1747 while (1) {
1748 temp_sock_descriptor =
1749 accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
1750 if (temp_sock_descriptor == -1) {
1751 perror("[accept]");
1752 exit(1);
1753 }
1754
1755 /* create the SockHandler structure */
1756 sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
1757
1758 while (1) {
1759 code = 1;
1760 if ((buf = sock_handler_read(sh)) != NULL) {
1761 /* Let's see what we fished... */
1762 code = Bmsrv_parse_buf(sh, buf, strlen(buf));
1763 }
1764 if (code == 1)
1765 exit(1);
1766 else if (code == 2)
1767 break;
1768 }
1769
1770 sock_handler_close(sh);
1771 sock_handler_free(sh);
1772
1773 }/*while*/
1774 }
0 /*
1 * File: cookies.c
2 * Cookies server.
3 *
4 * Copyright 2001 Lars Clausen <lrclause@cs.uiuc.edu>
5 * Jörgen Viksell <jorgen.viksell@telia.com>
6 * Copyright 2002-2005 Jorge Arellano Cid <jcid@dillo.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 */
14
15 /* Handling of cookies takes place here.
16 * This implementation aims to follow RFC 2965:
17 * http://www.cis.ohio-state.edu/cs/Services/rfc/rfc-text/rfc2965.txt
18 */
19
20 /* Todo: this server is not assembling the received packets.
21 * This means it currently expects dillo to send full dpi tags
22 * within the socket; if that fails, everything stops.
23 * This is not hard to fix, mainly is a matter of expecting the
24 * final '>' of a tag.
25 */
26
27 #ifdef DISABLE_COOKIES
28
29 int main(void)
30 {
31 return 0; /* never called */
32 }
33
34 #else
35
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40 #include <sys/un.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <stddef.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <time.h> /* for time() and time_t */
49 #include <ctype.h>
50 #include <netdb.h>
51 #include <signal.h>
52 #include "dpiutil.h"
53 #include "../dpip/dpip.h"
54
55
56 #include <glib.h>
57
58 /* This one is tricky, some sources state it should include the byte
59 * for the terminating NULL, and others say it shouldn't. */
60 # define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
61 + strlen ((ptr)->sun_path))
62
63 /* Debugging macros
64 */
65 #define _MSG(fmt...)
66 #define MSG(fmt...) g_print("[cookies dpi]: " fmt)
67
68
69 /*
70 * a_List_add()
71 *
72 * Make sure there's space for 'num_items' items within the list
73 * (First, allocate an 'alloc_step' sized chunk, after that, double the
74 * list size --to make it faster)
75 */
76 #define a_List_add(list,num_items,alloc_step) \
77 if ( !list ) { \
78 list = g_malloc(alloc_step * sizeof((*list))); \
79 } \
80 if ( num_items >= alloc_step ){ \
81 while ( num_items >= alloc_step ) \
82 alloc_step <<= 1; \
83 list = g_realloc(list, alloc_step * sizeof((*list))); \
84 }
85
86 /* The maximum length of a line in the cookie file */
87 #define LINE_MAXLEN 4096
88
89 typedef enum {
90 COOKIE_ACCEPT,
91 COOKIE_ACCEPT_SESSION,
92 COOKIE_DENY
93 } CookieControlAction;
94
95 typedef struct {
96 CookieControlAction action;
97 char *domain;
98 } CookieControl;
99
100 typedef struct {
101 char *name;
102 char *value;
103 char *domain;
104 char *path;
105 time_t expires_at;
106 guint version;
107 char *comment;
108 char *comment_url;
109 gboolean secure;
110 gboolean session_only;
111 GList *ports;
112 } CookieData_t;
113
114 /*
115 * Local data
116 */
117
118 /* Hashtable indexed by domain, each value is a set of cookies for
119 * that domain. */
120 static GHashTable *cookies;
121
122 /* Variables for access control */
123 static CookieControl *ccontrol = NULL;
124 static int num_ccontrol = 0;
125 static int num_ccontrol_max = 1;
126 static CookieControlAction default_action = COOKIE_DENY;
127
128 static gboolean disabled;
129 static FILE *file_stream;
130 static char *cookies_txt_header_str =
131 "# HTTP Cookie File\n"
132 "# http://www.netscape.com/newsref/std/cookie_spec.html\n"
133 "# This is a generated file! Do not edit.\n\n";
134
135
136 /*
137 * Forward declarations
138 */
139
140 static gchar *d_strsep(char **orig, const char *delim);
141 static FILE *Cookies_fopen(const char *file, gchar *init_str);
142 static CookieControlAction Cookies_control_check_domain(const char *domain);
143 static int Cookie_control_init(void);
144 static void Cookies_parse_ports(gint url_port, CookieData_t *cookie,
145 const char *port_str);
146 static char *Cookies_build_ports_str(CookieData_t *cookie);
147 static char *Cookies_strip_path(const char *path);
148 static void Cookies_add_cookie(CookieData_t *cookie);
149 static void Cookies_remove_cookie(CookieData_t *cookie);
150 static gint Cookies_equals(gconstpointer a, gconstpointer b);
151
152 /*
153 * strsep implementation
154 */
155 gchar *d_strsep(char **orig, const char *delim)
156 {
157 gchar *str, *p;
158
159 if (!(str = *orig))
160 return NULL;
161
162 p = strpbrk(str, delim);
163 if (p) {
164 *p++ = 0;
165 *orig = p;
166 } else {
167 *orig = NULL;
168 }
169 return str;
170 }
171
172 /*
173 * Return a file pointer. If the file doesn't exist, try to create it,
174 * with the optional 'init_str' as its content.
175 */
176 static FILE *Cookies_fopen(const char *filename, gchar *init_str)
177 {
178 FILE *F_in;
179 int fd;
180
181 if ((F_in = fopen(filename, "r+")) == NULL) {
182 /* Create the file */
183 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
184 if (fd != -1) {
185 if (init_str)
186 write(fd, init_str, strlen(init_str));
187 close(fd);
188
189 MSG("Created file: %s\n", filename);
190 F_in = Cookies_fopen(filename, NULL);
191 } else {
192 MSG("Could not create file: %s!\n", filename);
193 }
194 }
195
196 /* set close on exec */
197 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
198
199 return F_in;
200 }
201
202 static void Cookies_free_cookie(CookieData_t *cookie)
203 {
204 g_free(cookie->name);
205 g_free(cookie->value);
206 g_free(cookie->domain);
207 g_free(cookie->path);
208 g_free(cookie->comment);
209 g_free(cookie->comment_url);
210 g_list_free(cookie->ports);
211 g_free(cookie);
212 }
213
214 /*
215 * Initialize the cookies module
216 * (The 'disabled' variable is writable only within Cookies_init)
217 */
218 void Cookies_init()
219 {
220 CookieData_t *cookie;
221 char *filename;
222 char line[LINE_MAXLEN];
223 #ifndef HAVE_LOCKF
224 struct flock lck;
225 #endif
226 FILE *old_cookies_file_stream;
227
228 /* Default setting */
229 disabled = TRUE;
230
231 /* Read and parse the cookie control file (cookiesrc) */
232 if (Cookie_control_init() != 0) {
233 MSG("Disabling cookies.\n");
234 return;
235 }
236
237 /* Get a stream for the cookies file */
238 filename = g_strconcat(g_get_home_dir(), "/.dillo/cookies.txt", NULL);
239 file_stream = Cookies_fopen(filename, cookies_txt_header_str);
240
241 g_free(filename);
242
243 if (!file_stream) {
244 MSG("ERROR: Can't open ~/.dillo/cookies.txt, disabling cookies\n");
245 return;
246 }
247
248 /* Try to get a lock from the file descriptor */
249 #ifdef HAVE_LOCKF
250 disabled = (lockf(fileno(file_stream), F_TLOCK, 0) == -1);
251 #else /* POSIX lock */
252 lck.l_start = 0; /* start at beginning of file */
253 lck.l_len = 0; /* lock entire file */
254 lck.l_type = F_WRLCK;
255 lck.l_whence = SEEK_SET; /* absolute offset */
256
257 disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
258 #endif
259 if (disabled) {
260 MSG("The cookies file has a file lock: disabling cookies!\n");
261 fclose(file_stream);
262 return;
263 }
264
265 MSG("Enabling cookies as from cookiesrc...\n");
266
267 cookies = g_hash_table_new(g_str_hash, g_str_equal);
268
269 /* Get all lines in the file */
270 while (!feof(file_stream)) {
271 line[0] = '\0';
272 fgets(line, LINE_MAXLEN, file_stream);
273
274 /* Remove leading and trailing whitespaces */
275 g_strstrip(line);
276
277 if ((line[0] != '\0') && (line[0] != '#')) {
278 /* Would use g_strsplit, but it doesn't give empty trailing pieces.
279 */
280 /* Split the row into pieces using a tab as the delimiter.
281 * pieces[0] The domain name
282 * pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain?
283 * pieces[2] The path
284 * pieces[3] Is the cookie unsecure or secure (TRUE/FALSE)
285 * pieces[4] Timestamp of expire date
286 * pieces[5] Name of the cookie
287 * pieces[6] Value of the cookie
288 */
289 CookieControlAction action;
290 char *piece;
291 char *line_marker = line;
292
293 cookie = g_new0(CookieData_t, 1);
294
295 cookie->session_only = FALSE;
296 cookie->version = 0;
297 cookie->domain = g_strdup(d_strsep(&line_marker, "\t"));
298 d_strsep(&line_marker, "\t"); /* we use domain always as sufix */
299 cookie->path = g_strdup(d_strsep(&line_marker, "\t"));
300 piece = d_strsep(&line_marker, "\t");
301 if (piece != NULL && piece[0] == 'T')
302 cookie->secure = TRUE;
303 piece = d_strsep(&line_marker, "\t");
304 if (piece != NULL)
305 cookie->expires_at = (time_t) strtol(piece, NULL, 10);
306 cookie->name = g_strdup(d_strsep(&line_marker, "\t"));
307 cookie->value = g_strdup(d_strsep(&line_marker, "\t"));
308
309 if (!cookie->domain || cookie->domain[0] == '\0' ||
310 !cookie->path || cookie->path[0] != '/' ||
311 !cookie->name || cookie->name[0] == '\0' ||
312 !cookie->value) {
313 MSG("Malformed line in cookies.txt file!\n");
314 Cookies_free_cookie(cookie);
315 continue;
316 }
317
318 action = Cookies_control_check_domain(cookie->domain);
319 if (action == COOKIE_DENY) {
320 Cookies_free_cookie(cookie);
321 continue;
322 } else if (action == COOKIE_ACCEPT_SESSION) {
323 cookie->session_only = TRUE;
324 }
325
326 /* Save cookie in memory */
327 Cookies_add_cookie(cookie);
328 }
329 }
330
331 filename = g_strconcat(g_get_home_dir(), "/.dillo/cookies", NULL);
332 if ((old_cookies_file_stream = fopen(filename, "r")) != NULL) {
333 g_free(filename);
334 MSG("WARNING: Reading old cookies file ~/.dillo/cookies too\n");
335
336 /* Get all lines in the file */
337 while (!feof(old_cookies_file_stream)) {
338 line[0] = '\0';
339 fgets(line, LINE_MAXLEN, old_cookies_file_stream);
340
341 /* Remove leading and trailing whitespaces */
342 g_strstrip(line);
343
344 if (line[0] != '\0') {
345 /* Would use g_strsplit, but it doesn't give empty trailing pieces.
346 */
347 /* Split the row into pieces using a tab as the delimiter.
348 * pieces[0] The version this cookie was set as (0 / 1)
349 * pieces[1] The domain name
350 * pieces[2] A comma separated list of accepted ports
351 * pieces[3] The path
352 * pieces[4] Is the cookie unsecure or secure (0 / 1)
353 * pieces[5] Timestamp of expire date
354 * pieces[6] Name of the cookie
355 * pieces[7] Value of the cookie
356 * pieces[8] Comment
357 * pieces[9] Comment url
358 */
359 CookieControlAction action;
360 char *piece;
361 char *line_marker = line;
362
363 cookie = g_new0(CookieData_t, 1);
364
365 cookie->session_only = FALSE;
366 piece = d_strsep(&line_marker, "\t");
367 if (piece != NULL)
368 cookie->version = strtol(piece, NULL, 10);
369 cookie->domain = g_strdup(d_strsep(&line_marker, "\t"));
370 Cookies_parse_ports(0, cookie, d_strsep(&line_marker, "\t"));
371 cookie->path = g_strdup(d_strsep(&line_marker, "\t"));
372 piece = d_strsep(&line_marker, "\t");
373 if (piece != NULL && piece[0] == '1')
374 cookie->secure = TRUE;
375 piece = d_strsep(&line_marker, "\t");
376 if (piece != NULL)
377 cookie->expires_at = (time_t) strtol(piece, NULL, 10);
378 cookie->name = g_strdup(d_strsep(&line_marker, "\t"));
379 cookie->value = g_strdup(d_strsep(&line_marker, "\t"));
380 cookie->comment = g_strdup(d_strsep(&line_marker, "\t"));
381 cookie->comment_url = g_strdup(d_strsep(&line_marker, "\t"));
382
383 if (!cookie->domain || cookie->domain[0] == '\0' ||
384 !cookie->path || cookie->path[0] != '/' ||
385 !cookie->name || cookie->name[0] == '\0' ||
386 !cookie->value) {
387 MSG("Malformed line in cookies file!\n");
388 Cookies_free_cookie(cookie);
389 continue;
390 }
391
392 action = Cookies_control_check_domain(cookie->domain);
393 if (action == COOKIE_DENY) {
394 Cookies_free_cookie(cookie);
395 continue;
396 } else if (action == COOKIE_ACCEPT_SESSION) {
397 cookie->session_only = TRUE;
398 }
399
400 /* Save cookie in memory */
401 Cookies_add_cookie(cookie);
402 }
403 }
404 fclose(old_cookies_file_stream);
405 } else {
406 g_free(filename);
407 }
408
409 }
410
411 /*
412 * Save the cookies and remove them from the hash table
413 */
414 static gboolean Cookies_freeall_cb(gpointer key,
415 gpointer value,
416 gpointer data)
417 {
418 CookieData_t *cookie;
419 GList *domain_cookies = value;
420 /* char *ports_str; */
421
422 for (; domain_cookies; domain_cookies = g_list_next(domain_cookies)) {
423 cookie = domain_cookies->data;
424
425 if (!cookie->session_only) {
426 /* ports_str = Cookies_build_ports_str(cookie); */
427 fprintf(file_stream, "%s\tTRUE\t%s\t%s\t%ld\t%s\t%s\n",
428 cookie->domain,
429 cookie->path,
430 cookie->secure ? "TRUE" : "FALSE",
431 (long)cookie->expires_at,
432 cookie->name,
433 cookie->value);
434 /* g_free(ports_str); */
435 }
436
437 Cookies_free_cookie(cookie);
438 }
439 g_list_free(value);
440 g_free(key);
441
442 /* Return TRUE to tell GLIB to free this key from the hash table */
443 return TRUE;
444 }
445
446 /*
447 * Flush cookies to disk and free all the memory allocated.
448 */
449 void Cookies_freeall()
450 {
451 int fd;
452 #ifndef HAVE_LOCKF
453 struct flock lck;
454 #endif
455
456 if (disabled)
457 return;
458
459 rewind(file_stream);
460 fd = fileno(file_stream);
461 ftruncate(fd, 0);
462 fprintf(file_stream, cookies_txt_header_str);
463
464 g_hash_table_foreach_remove(cookies, Cookies_freeall_cb, NULL);
465
466 #ifdef HAVE_LOCKF
467 lockf(fd, F_ULOCK, 0);
468 #else /* POSIX file lock */
469 lck.l_start = 0; /* start at beginning of file */
470 lck.l_len = 0; /* lock entire file */
471 lck.l_type = F_UNLCK;
472 lck.l_whence = SEEK_SET; /* absolute offset */
473
474 fcntl(fileno(file_stream), F_SETLKW, &lck);
475 #endif
476 fclose(file_stream);
477 }
478
479 static char *months[] =
480 { "",
481 "Jan", "Feb", "Mar",
482 "Apr", "May", "Jun",
483 "Jul", "Aug", "Sep",
484 "Oct", "Nov", "Dec"
485 };
486
487 /*
488 * Take a months name and return a number between 1-12.
489 * E.g. 'April' -> 4
490 */
491 static int Cookies_get_month(const char *month_name)
492 {
493 int i;
494
495 for (i = 1; i <= 12; i++) {
496 if (!g_strncasecmp(months[i], month_name, 3))
497 return i;
498 }
499 return 0;
500 }
501
502 /*
503 * Return a local timestamp from a GMT date string
504 * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format.
505 *
506 * Wdy, DD-Mon-YY HH:MM:SS GMT
507 * Wdy, DD-Mon-YYYY HH:MM:SS GMT
508 * Weekday, DD-Mon-YY HH:MM:SS GMT
509 * Weekday, DD-Mon-YYYY HH:MM:SS GMT
510 * Tue May 21 13:46:22 1991\n
511 * Tue May 21 13:46:22 1991
512 *
513 * (return 0 on malformed date string syntax)
514 */
515 static time_t Cookies_create_timestamp(const char *expires)
516 {
517 time_t ret;
518 int day, month, year, hour, minutes, seconds;
519 gchar *cp;
520 gchar *E_msg =
521 "Expire date is malformed!\n"
522 " (should be RFC-1123 | RFC-850 | ANSI asctime)\n"
523 " Ignoring cookie: ";
524
525 cp = strchr(expires, ',');
526 if (!cp && (strlen(expires) == 24 || strlen(expires) == 25)) {
527 /* Looks like ANSI asctime format... */
528 cp = (gchar *)expires;
529 day = strtol(cp + 8, NULL, 10); /* day */
530 month = Cookies_get_month(cp + 4); /* month */
531 year = strtol(cp + 20, NULL, 10); /* year */
532 hour = strtol(cp + 11, NULL, 10); /* hour */
533 minutes = strtol(cp + 14, NULL, 10); /* minutes */
534 seconds = strtol(cp + 17, NULL, 10); /* seconds */
535
536 } else if (cp && (cp - expires == 3 || cp - expires > 5) &&
537 (strlen(cp) == 24 || strlen(cp) == 26)) {
538 /* RFC-1123 | RFC-850 format | Old Netscape format */
539 day = strtol(cp + 2, NULL, 10);
540 month = Cookies_get_month(cp + 5);
541 year = strtol(cp + 9, &cp, 10);
542 /* todo: tricky, because two digits for year IS ambiguous! */
543 year += (year < 70) ? 2000 : ((year < 100) ? 1900 : 0);
544 hour = strtol(cp + 1, NULL, 10);
545 minutes = strtol(cp + 4, NULL, 10);
546 seconds = strtol(cp + 7, NULL, 10);
547
548 } else {
549 MSG("%s%s\n", E_msg, expires);
550 return (time_t) 0;
551 }
552
553 /* Error checks --this may be overkill */
554 if (!(day > 0 && day < 32 && month > 0 && month < 13 && year > 1970 &&
555 hour >= 0 && hour < 24 && minutes >= 0 && minutes < 60 &&
556 seconds >= 0 && seconds < 60)) {
557 MSG("%s%s\n", E_msg, expires);
558 return (time_t) 0;
559 }
560
561 /* Calculate local timestamp.
562 * [stolen from Lynx... (http://lynx.browser.org)] */
563 month -= 3;
564 if (month < 0) {
565 month += 12;
566 year--;
567 }
568
569 day += (year - 1968) * 1461 / 4;
570 day += ((((month * 153) + 2) / 5) - 672);
571 ret = (time_t)((day * 60 * 60 * 24) +
572 (hour * 60 * 60) +
573 (minutes * 60) +
574 seconds);
575
576 MSG("Expires in %ld seconds, at %s",
577 (long)ret - time(NULL), ctime(&ret));
578
579 return ret;
580 }
581
582 /*
583 * Parse a string containing a list of port numbers.
584 */
585 static void Cookies_parse_ports(gint url_port, CookieData_t *cookie,
586 const char *port_str)
587 {
588 if ((!port_str || !port_str[0]) && url_port != 0) {
589 /* There was no list, so only the calling urls port should be allowed. */
590 cookie->ports = g_list_append(cookie->ports,
591 GINT_TO_POINTER(url_port));
592 } else if (port_str[0] == '"' && port_str[1] != '"') {
593 char **tokens, **i;
594 int port;
595
596 tokens = g_strsplit(port_str + 1, ",", -1);
597 for (i = tokens; *i; ++i) {
598 port = strtol(*i, NULL, 10);
599 if (port > 0) {
600 cookie->ports = g_list_append(cookie->ports,
601 GINT_TO_POINTER(port));
602 }
603 }
604 g_strfreev(tokens);
605 }
606 }
607
608 /*
609 * Build a string of the ports in 'cookie'.
610 */
611 static char *Cookies_build_ports_str(CookieData_t *cookie)
612 {
613 GString *gstr;
614 GList *list;
615 char *ret;
616
617 gstr = g_string_new("\"");
618 for (list = cookie->ports; list; list = g_list_next(list))
619 g_string_sprintfa(gstr, "%d,", GPOINTER_TO_INT(list->data));
620
621 /* Remove any trailing comma */
622 if (gstr->len > 1)
623 g_string_erase(gstr, gstr->len - 1, 1);
624
625 g_string_append(gstr, "\"");
626
627 ret = gstr->str;
628 g_string_free(gstr, FALSE);
629
630 return ret;
631 }
632
633 /*
634 * Used by g_list_insert_sorted() to sort the cookies by most specific path
635 */
636 static gint Cookies_compare(gconstpointer a, gconstpointer b)
637 {
638 const CookieData_t *ca = a, *cb = b;
639
640 return strcmp(ca->path, cb->path);
641 }
642
643 static void Cookies_add_cookie(CookieData_t *cookie)
644 {
645 GList *domain_cookies, *tmp;
646 char *domain_str;
647
648 /* Don't add an expired cookie */
649 if (!cookie->session_only && cookie->expires_at < time(NULL)) {
650 Cookies_free_cookie(cookie);
651 return;
652 }
653
654 domain_cookies = g_hash_table_lookup(cookies, cookie->domain);
655
656 if (domain_cookies) {
657 /* Respect the limit of 20 cookies per domain */
658 if (g_list_length(domain_cookies) > 20) {
659 MSG("There are too many cookies for this domain (%s)\n",
660 cookie->domain);
661 Cookies_free_cookie(cookie);
662 return;
663 }
664
665 /* Remove any cookies with the same name and path */
666 while ((tmp = g_list_find_custom(domain_cookies, cookie,
667 Cookies_equals))) {
668 Cookies_remove_cookie(tmp->data);
669 domain_cookies = g_hash_table_lookup(cookies, cookie->domain);
670 }
671 }
672
673 /* Allocate string key when no domain_cookies are left
674 * (because remove_cookie has then killed the key, when it was there) */
675 domain_str = domain_cookies ? cookie->domain : g_strdup(cookie->domain);
676
677 domain_cookies = g_list_insert_sorted(domain_cookies, cookie,
678 Cookies_compare);
679 g_hash_table_insert(cookies, domain_str, domain_cookies);
680 }
681
682 /*
683 * Remove the cookie from the domain list.
684 * If the domain list is empty, free the hash table entry.
685 * Free the cookie.
686 */
687 static void Cookies_remove_cookie(CookieData_t *cookie)
688 {
689 GList *list;
690 gpointer orig_key;
691 gpointer orig_val;
692
693 if (g_hash_table_lookup_extended(cookies, cookie->domain,
694 &orig_key, &orig_val)) {
695 list = g_list_remove(orig_val, cookie);
696
697 if (list) {
698 /* Make sure that we have the correct start of the list stored */
699 g_hash_table_insert(cookies, cookie->domain, list);
700 } else {
701 g_hash_table_remove(cookies, cookie->domain);
702 g_free(orig_key);
703 }
704 } else {
705 MSG("Attempting to remove a cookie that doesn't exist!\n");
706 }
707
708 Cookies_free_cookie(cookie);
709 }
710
711 /*
712 * Return the attribute that is present at *cookie_str. This function
713 * will also attempt to advance cookie_str past any equal-sign.
714 */
715 static char *Cookies_parse_attr(char **cookie_str)
716 {
717 char *str = *cookie_str;
718 guint i, end = 0;
719 gboolean got_attr = FALSE;
720
721 for (i = 0; ; i++) {
722 switch (str[i]) {
723 case ' ':
724 case '\t':
725 case '=':
726 case ';':
727 got_attr = TRUE;
728 if (end == 0)
729 end = i;
730 break;
731 case ',':
732 *cookie_str = str + i;
733 return g_strndup(str, i);
734 break;
735 case '\0':
736 if (!got_attr) {
737 end = i;
738 got_attr = TRUE;
739 }
740 /* fall through! */
741 default:
742 if (got_attr) {
743 *cookie_str = str + i;
744 return g_strndup(str, end);
745 }
746 break;
747 }
748 }
749
750 return NULL;
751 }
752
753 /*
754 * Get the value starting at *cookie_str.
755 * broken_syntax: watch out for stupid syntax (comma in unquoted string...)
756 */
757 static char *Cookies_parse_value(char **cookie_str,
758 gboolean broken_syntax,
759 gboolean keep_quotes)
760 {
761 guint i, end;
762 char *str = *cookie_str;
763
764 for (i = end = 0; !end; ++i) {
765 switch (str[i]) {
766 case ' ':
767 case '\t':
768 if (!broken_syntax && str[0] != '\'' && str[0] != '"') {
769 *cookie_str = str + i + 1;
770 end = 1;
771 }
772 break;
773 case '\'':
774 case '"':
775 if (i != 0 && str[i] == str[0]) {
776 char *tmp = str + i;
777
778 while (*tmp != '\0' && *tmp != ';' && *tmp != ',')
779 tmp++;
780
781 *cookie_str = (*tmp == ';') ? tmp + 1 : tmp;
782
783 if (keep_quotes)
784 i++;
785 end = 1;
786 }
787 break;
788 case '\0':
789 *cookie_str = str + i;
790 end = 1;
791 break;
792 case ',':
793 if (str[0] != '\'' && str[0] != '"' && !broken_syntax) {
794 /* A new cookie starts here! */
795 *cookie_str = str + i;
796 end = 1;
797 }
798 break;
799 case ';':
800 if (str[0] != '\'' && str[0] != '"') {
801 *cookie_str = str + i + 1;
802 end = 1;
803 }
804 break;
805 default:
806 break;
807 }
808 }
809 /* keep i as an index to the last char */
810 --i;
811
812 if ((str[0] == '\'' || str[0] == '"') && !keep_quotes) {
813 return i > 1 ? g_strndup(str + 1, i - 1) : NULL;
814 } else {
815 return g_strndup(str, i);
816 }
817 }
818
819 /*
820 * Parse one cookie...
821 */
822 static CookieData_t *Cookies_parse_one(gint url_port, char **cookie_str)
823 {
824 CookieData_t *cookie;
825 char *str = *cookie_str;
826 char *attr;
827 char *value;
828 int num_attr = 0;
829 gboolean max_age = FALSE;
830 gboolean discard = FALSE;
831
832 cookie = g_new0(CookieData_t, 1);
833 cookie->session_only = TRUE;
834
835 /* Iterate until there is nothing left of the string OR we come
836 * across a comma representing the start of another cookie */
837 while (*str != '\0' && *str != ',') {
838 /* Skip whitespace */
839 while (isspace(*str))
840 str++;
841
842 /* Get attribute */
843 attr = Cookies_parse_attr(&str);
844 if (!attr) {
845 MSG("Failed to parse cookie attribute!\n");
846 Cookies_free_cookie(cookie);
847 return NULL;
848 }
849
850 /* Get the value for the attribute and store it */
851 if (num_attr == 0) {
852 /* The first attr, which always is the user supplied attr, may
853 * have the same name as an ordinary attr. Hence this workaround. */
854 cookie->name = g_strdup(attr);
855 cookie->value = Cookies_parse_value(&str, FALSE, TRUE);
856 } else if (g_strcasecmp(attr, "Path") == 0) {
857 value = Cookies_parse_value(&str, FALSE, FALSE);
858 cookie->path = value;
859 } else if (g_strcasecmp(attr, "Domain") == 0) {
860 value = Cookies_parse_value(&str, FALSE, FALSE);
861 cookie->domain = value;
862 } else if (g_strcasecmp(attr, "Discard") == 0) {
863 cookie->session_only = TRUE;
864 discard = TRUE;
865 } else if (g_strcasecmp(attr, "Max-Age") == 0) {
866 if (!discard) {
867 value = Cookies_parse_value(&str, FALSE, FALSE);
868
869 if (value) {
870 cookie->expires_at = time(NULL) + strtol(value, NULL, 10);
871 cookie->session_only = FALSE;
872 max_age = TRUE;
873 g_free(value);
874 } else {
875 MSG("Failed to parse cookie value!\n");
876 Cookies_free_cookie(cookie);
877 return NULL;
878 }
879 }
880 } else if (g_strcasecmp(attr, "Expires") == 0) {
881 if (!max_age && !discard) {
882 MSG("Old netscape-style cookie...\n");
883 value = Cookies_parse_value(&str, TRUE, FALSE);
884 if (value) {
885 cookie->expires_at = Cookies_create_timestamp(value);
886 cookie->session_only = FALSE;
887 g_free(value);
888 } else {
889 MSG("Failed to parse cookie value!\n");
890 Cookies_free_cookie(cookie);
891 return NULL;
892 }
893 }
894 } else if (g_strcasecmp(attr, "Port") == 0) {
895 value = Cookies_parse_value(&str, FALSE, TRUE);
896 Cookies_parse_ports(url_port, cookie, value);
897 g_free(value);
898 } else if (g_strcasecmp(attr, "Comment") == 0) {
899 value = Cookies_parse_value(&str, FALSE, FALSE);
900 cookie->comment = value;
901 } else if (g_strcasecmp(attr, "CommentURL") == 0) {
902 value = Cookies_parse_value(&str, FALSE, FALSE);
903 cookie->comment_url = value;
904 } else if (g_strcasecmp(attr, "Version") == 0) {
905 value = Cookies_parse_value(&str, FALSE, FALSE);
906
907 if (value) {
908 cookie->version = strtol(value, NULL, 10);
909 g_free(value);
910 } else {
911 MSG("Failed to parse cookie value!\n");
912 Cookies_free_cookie(cookie);
913 return NULL;
914 }
915 } else if (g_strcasecmp(attr, "Secure") == 0) {
916 cookie->secure = TRUE;
917 } else {
918 /* Oops! this can't be good... */
919 g_free(attr);
920 Cookies_free_cookie(cookie);
921 MSG("Cookie contains illegal attribute!\n");
922 return NULL;
923 }
924
925 g_free(attr);
926 num_attr++;
927 }
928
929 *cookie_str = (*str == ',') ? str + 1 : str;
930
931 if (cookie->name && cookie->value) {
932 return cookie;
933 } else {
934 MSG("Cookie missing name and/or value!\n");
935 Cookies_free_cookie(cookie);
936 return NULL;
937 }
938 }
939
940 /*
941 * Iterate the cookie string until we catch all cookies.
942 * Return Value: a list with all the cookies! (or NULL upon error)
943 */
944 static GSList *Cookies_parse_string(gint url_port, char *cookie_string)
945 {
946 CookieData_t *cookie;
947 GSList *ret = NULL;
948 char *str = cookie_string;
949
950 /* The string may contain several cookies separated by comma.
951 * We'll iterate until we've catched them all */
952 while (*str) {
953 cookie = Cookies_parse_one(url_port, &str);
954
955 if (cookie) {
956 ret = g_slist_append(ret, cookie);
957 } else {
958 MSG("Malformed cookie field, ignoring cookie: %s\n", cookie_string);
959 return NULL;
960 }
961 }
962
963 return ret;
964 }
965
966 /*
967 * Compare cookies by name and path (return 0 if equal)
968 */
969 static gint Cookies_equals(gconstpointer a, gconstpointer b)
970 {
971 const CookieData_t *ca = a, *cb = b;
972
973 return (strcmp(ca->name, cb->name) || strcmp(ca->path, cb->path));
974 }
975
976 /*
977 * Validate cookies domain against some security checks.
978 */
979 static gboolean Cookies_validate_domain(CookieData_t *cookie, gchar *host,
980 gchar *url_path)
981 {
982 int dots, diff, i;
983 gboolean is_ip;
984
985 /* Make sure that the path is set to something */
986 if (!cookie->path || cookie->path[0] != '/') {
987 g_free(cookie->path);
988 cookie->path = Cookies_strip_path(url_path);
989 }
990
991 /* If the server never set a domain, or set one without a leading
992 * dot (which isn't allowed), we use the calling URL's hostname. */
993 if (cookie->domain == NULL || cookie->domain[0] != '.') {
994 g_free(cookie->domain);
995 cookie->domain = g_strdup(host);
996 return TRUE;
997 }
998
999 /* Count the number of dots and also find out if it is an IP-address */
1000 is_ip = TRUE;
1001 for (i = 0, dots = 0; cookie->domain[i] != '\0'; i++) {
1002 if (cookie->domain[i] == '.')
1003 dots++;
1004 else if (!isdigit(cookie->domain[i]))
1005 is_ip = FALSE;
1006 }
1007
1008 /* A valid domain must have at least two dots in it */
1009 /* NOTE: this breaks cookies on localhost... */
1010 if (dots < 2) {
1011 return FALSE;
1012 }
1013
1014 /* Now see if the url matches the domain */
1015 diff = strlen(host) - i;
1016 if (diff > 0) {
1017 if (g_strcasecmp(host + diff, cookie->domain))
1018 return FALSE;
1019
1020 if (!is_ip) {
1021 /* "x.y.test.com" is not allowed to set cookies for ".test.com";
1022 * only an url of the form "y.test.com" would be. */
1023 while ( diff-- )
1024 if (host[diff] == '.')
1025 return FALSE;
1026 }
1027 }
1028
1029 return TRUE;
1030 }
1031
1032 /*
1033 * Strip of the filename from a full path
1034 */
1035 static char *Cookies_strip_path(const char *path)
1036 {
1037 char *ret;
1038 guint len;
1039
1040 if (path) {
1041 len = strlen(path);
1042
1043 while (len && path[len] != '/')
1044 len--;
1045 ret = g_strndup(path, len + 1);
1046 } else {
1047 ret = g_strdup("/");
1048 }
1049
1050 return ret;
1051 }
1052
1053 /*
1054 * Set the value corresponding to the cookie string
1055 */
1056 void Cookies_set(gchar *cookie_string, gchar *url_host,
1057 gchar *url_path, gint url_port)
1058 {
1059 CookieControlAction action;
1060 GSList *list;
1061
1062 if (disabled)
1063 return;
1064
1065 action = Cookies_control_check_domain(url_host);
1066 if (action == COOKIE_DENY) {
1067 MSG("denied SET for %s\n", url_host);
1068 return;
1069 }
1070
1071 list = Cookies_parse_string(url_port, cookie_string);
1072
1073 while (list) {
1074 CookieData_t *cookie = list->data;
1075
1076 if (Cookies_validate_domain(cookie, url_host, url_path)) {
1077 if (action == COOKIE_ACCEPT_SESSION)
1078 cookie->session_only = TRUE;
1079
1080 Cookies_add_cookie(cookie);
1081 } else {
1082 MSG("Rejecting cookie for %s from host %s path %s\n",
1083 cookie->domain, url_host, url_path);
1084 Cookies_free_cookie(cookie);
1085 }
1086
1087 list = g_slist_remove(list, list->data);
1088 }
1089 }
1090
1091 /*
1092 * Compare the cookie with the supplied data to see if it matches
1093 */
1094 static gboolean Cookies_match(CookieData_t *cookie, gint port,
1095 const char *path, gboolean is_ssl)
1096 {
1097 /* Insecure cookies matches both secure and insecure urls, secure
1098 cookies matches only secure urls */
1099 if (cookie->secure && !is_ssl)
1100 return FALSE;
1101
1102 /* Check that the cookie path is a subpath of the current path */
1103 if (strncmp(cookie->path, path, strlen(cookie->path)) != 0)
1104 return FALSE;
1105
1106 /* Check if the port of the request URL matches any
1107 * of those set in the cookie */
1108 if (cookie->ports) {
1109 GList *list;
1110
1111 for (list = cookie->ports; list; list = g_list_next(list)) {
1112 if (GPOINTER_TO_INT(list->data) == port)
1113 return TRUE;
1114 }
1115
1116 return FALSE;
1117 }
1118
1119 /* It's a match */
1120 return TRUE;
1121 }
1122
1123 /*
1124 * Return a string that contains all relevant cookies as headers.
1125 */
1126 char *Cookies_get(gchar *url_host, gchar *url_path,
1127 gchar *url_scheme, gint url_port)
1128 {
1129 char *domain_string, *q, *str, *path;
1130 CookieData_t *cookie;
1131 GList *matching_cookies = NULL;
1132 GList *domain_cookie;
1133 gboolean is_ssl;
1134 GString *cookie_gstring;
1135
1136 if (disabled)
1137 return g_strdup("");
1138
1139 path = Cookies_strip_path(url_path);
1140
1141 /* Check if the protocol is secure or not */
1142 is_ssl = (!g_strcasecmp(url_scheme, "https"));
1143
1144 for (domain_string = (char *) url_host;
1145 domain_string != NULL && *domain_string;
1146 domain_string = strchr(domain_string+1, '.')) {
1147 domain_cookie = g_hash_table_lookup(cookies, domain_string);
1148
1149 while (domain_cookie) {
1150 cookie = domain_cookie->data;
1151 domain_cookie = g_list_next(domain_cookie);
1152
1153 /* Remove expired cookie. */
1154 if (!cookie->session_only && cookie->expires_at < time(NULL)) {
1155 Cookies_remove_cookie(cookie);
1156 continue;
1157 }
1158
1159 /* Check if the cookie matches the requesting URL */
1160 if (Cookies_match(cookie, url_port, path, is_ssl))
1161 matching_cookies = g_list_append(matching_cookies, cookie);
1162 }
1163 }
1164
1165 /* Found the cookies, now make the string */
1166 cookie_gstring = g_string_new("");
1167 if (matching_cookies != NULL) {
1168 CookieData_t *first_cookie = matching_cookies->data;
1169
1170 g_string_sprintfa(cookie_gstring, "Cookie: ");
1171
1172 if (first_cookie->version != 0)
1173 g_string_sprintfa(cookie_gstring, "$Version=\"%d\"; ",
1174 first_cookie->version);
1175
1176 while (matching_cookies) {
1177 cookie = matching_cookies->data;
1178 q = (cookie->version == 0 ? "" : "\"");
1179
1180 g_string_sprintfa(cookie_gstring,
1181 "%s=%s; $Path=%s%s%s; $Domain=%s%s%s",
1182 cookie->name, cookie->value,
1183 q, cookie->path, q, q, cookie->domain, q);
1184
1185 if (cookie->ports) {
1186 char *ports_str = Cookies_build_ports_str(cookie);
1187 g_string_sprintfa(cookie_gstring, "; $Port=%s", ports_str);
1188 g_free(ports_str);
1189 }
1190
1191 matching_cookies = g_list_next(matching_cookies);
1192 g_string_append(cookie_gstring, matching_cookies ? "; " : "\r\n");
1193 }
1194 }
1195
1196 g_free(path);
1197 str = cookie_gstring->str;
1198 g_string_free(cookie_gstring, FALSE);
1199 return str;
1200 }
1201
1202 /* -------------------------------------------------------------
1203 * Access control routines
1204 * ------------------------------------------------------------- */
1205
1206
1207 /*
1208 * Get the cookie control rules (from cookiesrc).
1209 * Return value:
1210 * 0 = Parsed OK, with cookies enabled
1211 * 1 = Parsed OK, with cookies disabled
1212 * 2 = Can't open the control file
1213 */
1214 static int Cookie_control_init(void)
1215 {
1216 CookieControl cc;
1217 FILE *stream;
1218 char *filename;
1219 char line[LINE_MAXLEN];
1220 char domain[LINE_MAXLEN];
1221 char rule[LINE_MAXLEN];
1222 int i, j;
1223 gboolean enabled = FALSE;
1224
1225 /* Get a file pointer */
1226 filename = g_strconcat(g_get_home_dir(), "/", ".dillo/cookiesrc", NULL);
1227 stream = Cookies_fopen(filename, "DEFAULT DENY\n");
1228 g_free(filename);
1229
1230 if (!stream)
1231 return 2;
1232
1233 /* Get all lines in the file */
1234 while (!feof(stream)) {
1235 line[0] = '\0';
1236 fgets(line, LINE_MAXLEN, stream);
1237
1238 /* Remove leading and trailing whitespaces */
1239 g_strstrip(line);
1240
1241 if (line[0] != '\0' && line[0] != '#') {
1242 i = 0;
1243 j = 0;
1244
1245 /* Get the domain */
1246 while (!isspace(line[i]))
1247 domain[j++] = line[i++];
1248 domain[j] = '\0';
1249
1250 /* Skip past whitespaces */
1251 i++;
1252 while (isspace(line[i]))
1253 i++;
1254
1255 /* Get the rule */
1256 j = 0;
1257 while (line[i] != '\0' && !isspace(line[i]))
1258 rule[j++] = line[i++];
1259 rule[j] = '\0';
1260
1261 if (g_strcasecmp(rule, "ACCEPT") == 0)
1262 cc.action = COOKIE_ACCEPT;
1263 else if (g_strcasecmp(rule, "ACCEPT_SESSION") == 0)
1264 cc.action = COOKIE_ACCEPT_SESSION;
1265 else if (g_strcasecmp(rule, "DENY") == 0)
1266 cc.action = COOKIE_DENY;
1267 else {
1268 MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n",
1269 rule, domain);
1270 continue;
1271 }
1272
1273 cc.domain = g_strdup(domain);
1274 if (g_strcasecmp(cc.domain, "DEFAULT") == 0) {
1275 /* Set the default action */
1276 default_action = cc.action;
1277 g_free(cc.domain);
1278 } else {
1279 a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
1280 ccontrol[num_ccontrol++] = cc;
1281 }
1282
1283 if (cc.action != COOKIE_DENY)
1284 enabled = TRUE;
1285 }
1286 }
1287
1288 fclose(stream);
1289
1290 return (enabled ? 0 : 1);
1291 }
1292
1293 /*
1294 * Check the rules for an appropriate action for this domain
1295 */
1296 static CookieControlAction Cookies_control_check_domain(const char *domain)
1297 {
1298 int i, diff;
1299
1300 for (i = 0; i < num_ccontrol; i++) {
1301 if (ccontrol[i].domain[0] == '.') {
1302 diff = strlen(domain) - strlen(ccontrol[i].domain);
1303 if (diff >= 0) {
1304 if (g_strcasecmp(domain + diff, ccontrol[i].domain) != 0)
1305 continue;
1306 } else {
1307 continue;
1308 }
1309 } else {
1310 if (g_strcasecmp(domain, ccontrol[i].domain) != 0)
1311 continue;
1312 }
1313
1314 /* If we got here we have a match */
1315 return( ccontrol[i].action );
1316 }
1317
1318 return default_action;
1319 }
1320
1321 /* -- Dpi parser ----------------------------------------------------------- */
1322
1323 /*
1324 * Parse a data stream (dpi protocol)
1325 * Note: Buf is a zero terminated string
1326 * Return code: { 0:OK, 1:Abort, 2:Close }
1327 */
1328 static int srv_parse_buf(SockHandler *sh, char *Buf, size_t BufSize)
1329 {
1330 char *p, *cmd, *cookie, *host, *path, *scheme;
1331 gint port;
1332
1333 if (!(p = strchr(Buf, '>'))) {
1334 /* Haven't got a full tag */
1335 MSG("Haven't got a full tag!\n");
1336 return 1;
1337 }
1338
1339 cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
1340
1341 if (cmd && strcmp(cmd, "DpiBye") == 0) {
1342 g_free(cmd);
1343 MSG("Cookies dpi (pid %d): Got DpiBye.\n", (gint)getpid());
1344 exit(0);
1345
1346 } else if (cmd && strcmp(cmd, "set_cookie") == 0) {
1347 g_free(cmd);
1348 cookie = a_Dpip_get_attr(Buf, BufSize, "cookie");
1349 host = a_Dpip_get_attr(Buf, BufSize, "host");
1350 path = a_Dpip_get_attr(Buf, BufSize, "path");
1351 p = a_Dpip_get_attr(Buf, BufSize, "port");
1352 port = strtol(p, NULL, 10);
1353 g_free(p);
1354
1355 Cookies_set(cookie, host, path, port);
1356
1357 g_free(path);
1358 g_free(host);
1359 g_free(cookie);
1360 return 2;
1361
1362 } else if (cmd && strcmp(cmd, "get_cookie") == 0) {
1363 g_free(cmd);
1364 scheme = a_Dpip_get_attr(Buf, BufSize, "scheme");
1365 host = a_Dpip_get_attr(Buf, BufSize, "host");
1366 path = a_Dpip_get_attr(Buf, BufSize, "path");
1367 p = a_Dpip_get_attr(Buf, BufSize, "port");
1368 port = strtol(p, NULL, 10);
1369 g_free(p);
1370
1371 cookie = Cookies_get(host, path, scheme, port);
1372 g_free(scheme);
1373 g_free(path);
1374 g_free(host);
1375
1376 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s", "get_cookie_answer", cookie);
1377
1378 if (sock_handler_write_str(sh, cmd, 1)) {
1379 g_free(cookie);
1380 g_free(cmd);
1381 return 1;
1382 }
1383 g_free(cookie);
1384 g_free(cmd);
1385
1386 return 2;
1387 }
1388
1389 return 0;
1390 }
1391
1392 /* -- Termination handlers ----------------------------------------------- */
1393 /*
1394 * (was to delete the local namespace socket),
1395 * but this is handled by 'dpid' now.
1396 */
1397 static void cleanup(void)
1398 {
1399 Cookies_freeall();
1400 MSG("cleanup\n");
1401 /* no more cleanup required */
1402 }
1403
1404 /*
1405 * Perform any necessary cleanups upon abnormal termination
1406 */
1407 static void termination_handler(int signum)
1408 {
1409 exit(signum);
1410 }
1411
1412
1413 /*
1414 * -- MAIN -------------------------------------------------------------------
1415 */
1416 int main (void) {
1417 struct sockaddr_un spun;
1418 int temp_sock_descriptor;
1419 int address_size;
1420 char *buf;
1421 int code;
1422 SockHandler *sh;
1423
1424 /* Arrange the cleanup function for terminations via exit() */
1425 atexit(cleanup);
1426
1427 /* Arrange the cleanup function for abnormal terminations */
1428 if (signal (SIGINT, termination_handler) == SIG_IGN)
1429 signal (SIGINT, SIG_IGN);
1430 if (signal (SIGHUP, termination_handler) == SIG_IGN)
1431 signal (SIGHUP, SIG_IGN);
1432 if (signal (SIGTERM, termination_handler) == SIG_IGN)
1433 signal (SIGTERM, SIG_IGN);
1434
1435 Cookies_init();
1436 MSG("(v.1) accepting connections...\n");
1437
1438 if (disabled)
1439 exit(1);
1440
1441 /* some OSes may need this... */
1442 address_size = sizeof(struct sockaddr_un);
1443
1444 while (1) {
1445 temp_sock_descriptor =
1446 accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
1447 if (temp_sock_descriptor == -1) {
1448 perror("[accept]");
1449 exit(1);
1450 }
1451
1452 /* create the SockHandler structure */
1453 sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
1454
1455 while (1) {
1456 code = 1;
1457 if ((buf = sock_handler_read(sh)) != NULL) {
1458 /* Let's see what we fished... */
1459 code = srv_parse_buf(sh, buf, strlen(buf));
1460 }
1461 if (code == 1)
1462 exit(1);
1463 else if (code == 2)
1464 break;
1465 }
1466
1467 _MSG("Closing SockHandler\n");
1468 sock_handler_close(sh);
1469 sock_handler_free(sh);
1470
1471 }/*while*/
1472 }
1473
1474 #endif /* !DISABLE_COOKIES */
0 /*
1 * File: datauri.c
2 *
3 * Copyright (C) 2006 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * Filter dpi for the "data:" URI scheme (RFC 2397).
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <glib.h>
18
19 #include "../dpip/dpip.h"
20 #include "dpiutil.h"
21
22 /*
23 * Debugging macros
24 */
25 #define _MSG(fmt...)
26 #define MSG(fmt...) g_printerr("[datauri dpi]: " fmt)
27
28 /*
29 * Global variables
30 */
31 static SockHandler *sh = NULL;
32
33
34
35 int b64decode(unsigned char* str)
36 {
37 unsigned char *cur, *start;
38 int d, dlast, phase;
39 unsigned char c;
40 static int table[256] = {
41 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
42 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
43 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
44 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
45 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
46 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
47 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
48 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
49 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
50 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
51 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
52 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
53 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
54 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
55 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
56 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
57 };
58
59 d = dlast = phase = 0;
60 start = str;
61 for (cur = str; *cur != '\0'; ++cur )
62 {
63 // jer: treat line endings as physical breaks.
64 //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
65 d = table[(int)*cur];
66 if(d != -1)
67 {
68 switch(phase)
69 {
70 case 0:
71 ++phase;
72 break;
73 case 1:
74 c = ((dlast << 2) | ((d & 0x30) >> 4));
75 *str++ = c;
76 ++phase;
77 break;
78 case 2:
79 c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
80 *str++ = c;
81 ++phase;
82 break;
83 case 3:
84 c = (((dlast & 0x03 ) << 6) | d);
85 *str++ = c;
86 phase = 0;
87 break;
88 }
89 dlast = d;
90 }
91 }
92 *str = '\0';
93 return str - start;
94 }
95
96 /* Modified from src/url.c --------------------------------------------------*/
97
98 /*
99 * Given an hex octet (e.g., e3, 2F, 20), return the corresponding
100 * character if the octet is valid, and -1 otherwise
101 */
102 static int Url_decode_hex_octet(const gchar *s)
103 {
104 gint hex_value;
105 gchar *tail, hex[3];
106
107 if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
108 hex[2] = 0;
109 hex_value = strtol(hex, &tail, 16);
110 if (tail - hex == 2)
111 return hex_value;
112 }
113 return -1;
114 }
115
116 /*
117 * Parse possible hexadecimal octets in the URI path.
118 * Returns a new allocated string.
119 */
120 gchar *a_Url_decode_hex_str(const gchar *str, size_t *p_sz)
121 {
122 gchar *new_str, *dest;
123 int i, val;
124
125 if (!str) {
126 *p_sz = 0;
127 return NULL;
128 }
129
130 dest = new_str = g_new(gchar, strlen(str) + 1);
131 for (i = 0; str[i]; i++) {
132 *dest++ = (str[i] == '%' && (val = Url_decode_hex_octet(str+i+1)) >= 0) ?
133 i+=2, val : str[i];
134 }
135 *dest = 0;
136
137 new_str = g_realloc(new_str, sizeof(gchar) * (dest - new_str + 1));
138 *p_sz = (size_t)(dest - new_str);
139 return new_str;
140 }
141
142 /* end ----------------------------------------------------------------------*/
143
144 /*
145 * Send decoded data to dillo in an HTTP envelope.
146 */
147 void send_decoded_data(const char *url, const char *mime_type,
148 unsigned char *data, size_t data_sz)
149 {
150 char *d_cmd;
151
152 /* Send dpip tag */
153 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
154 sock_handler_write_str(sh, d_cmd, 1);
155 g_free(d_cmd);
156
157 /* Send HTTP header. */
158 sock_handler_write_str(sh, "Content-type: ", 0);
159 sock_handler_write_str(sh, mime_type, 0);
160 sock_handler_write_str(sh, "\n\n", 1);
161
162 /* Send message */
163 sock_handler_write(sh, (char *)data, data_sz, 0);
164 }
165
166 void send_failure_message(const char *url, const char *mime_type,
167 unsigned char *data, size_t data_sz)
168 {
169 char *d_cmd;
170 char buf[1024];
171
172 const char *msg =
173 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
174 "<html><body>\n"
175 "<hr><h1>Datauri dpi</h1><hr>\n"
176 "<p><b>Can't parse datauri:</b><br>\n";
177 const char *msg_mime_type="text/html";
178
179 /* Send dpip tag */
180 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
181 sock_handler_write_str(sh, d_cmd, 1);
182 g_free(d_cmd);
183
184 /* Send HTTP header. */
185 sock_handler_write_str(sh, "Content-type: ", 0);
186 sock_handler_write_str(sh, msg_mime_type, 0);
187 sock_handler_write_str(sh, "\n\n", 1);
188
189 /* Send message */
190 sock_handler_write_str(sh, msg, 0);
191
192 /* send some debug info */
193 g_snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
194 mime_type, (int)data_sz, data);
195 sock_handler_write_str(sh, buf, 0);
196
197 /* close page */
198 sock_handler_write_str(sh, "</body></html>", 0);
199 }
200
201 /*
202 * Get mime type from the data URI.
203 * todo: there's no point in handling "charset" because current dillo
204 * only handles ISO-LATIN-1. The FLTK2 version (utf-8) could use it in the
205 * future.
206 */
207 char *datauri_get_mime(char *url)
208 {
209 char buf[256];
210 char *mime_type = NULL, *p;
211 size_t len = 0;
212
213 if (g_strncasecmp(url, "data:", 5) == 0) {
214 if ((p = strchr(url, ',')) && p - url < 256) {
215 url += 5;
216 len = p - url;
217 strncpy(buf, url, len);
218 buf[len] = 0;
219 /* strip ";base64" */
220 if (len >= 7 && g_strcasecmp(buf + len - 7, ";base64") == 0) {
221 len -= 7;
222 buf[len] = 0;
223 }
224 }
225
226 /* that's it, now handle omitted types */
227 if (len == 0) {
228 mime_type = g_strdup("text/plain;charset=US-ASCII");
229 } else if (!g_strncasecmp(buf, "charset", 7)) {
230 mime_type = g_strconcat("text/plain", buf, NULL);
231 } else {
232 mime_type = g_strdup(buf);
233 }
234 }
235
236 return mime_type;
237 }
238
239 /*
240 * Return a decoded data string.
241 */
242 unsigned char *datauri_get_data(char *url, size_t *p_sz)
243 {
244 char *p;
245 int is_base64 = 0;
246 unsigned char *data = NULL;
247
248 if ((p = strchr(url, ',')) && p - url >= 12 && /* "data:;base64" */
249 g_strncasecmp(p - 7, ";base64", 7) == 0) {
250 is_base64 = 1;
251 }
252
253 if (p) {
254 ++p;
255 if (is_base64) {
256 data = (unsigned char *)g_strdup(p);
257 *p_sz = (size_t) b64decode(data);
258 } else {
259 data = (unsigned char *)a_Url_decode_hex_str(p, p_sz);
260 }
261 } else {
262 data = g_strdup("");
263 *p_sz = 0;
264 }
265
266 return data;
267 }
268
269 /*
270 *
271 */
272 int main(void)
273 {
274 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
275 unsigned char *data;
276 size_t data_size = 0;
277
278 /* Initialize the SockHandler */
279 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
280
281 /* wget may need to write a temporary file... */
282 chdir("/tmp");
283
284 /* Read the dpi command from STDIN */
285 dpip_tag = sock_handler_read(sh);
286 MSG("[%s]\n", dpip_tag);
287
288 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
289 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
290 if (!cmd || !url) {
291 MSG("Error, cmd=%s, url=%s\n", cmd, url);
292 exit (EXIT_FAILURE);
293 }
294
295 /* Parse the data URI */
296 mime_type = datauri_get_mime(url);
297 data = datauri_get_data(url, &data_size);
298
299 MSG("mime_type: %s\n", mime_type);
300 MSG("data_size: %d\n", data_size);
301 MSG("data: {%s}\n", data);
302
303 if (mime_type && data) {
304 /* good URI */
305 send_decoded_data(url, mime_type, data, data_size);
306 } else {
307 /* malformed URI */
308 send_failure_message(url, mime_type, data, data_size);
309 }
310
311 g_free(data);
312 g_free(mime_type);
313 g_free(url);
314 g_free(cmd);
315 g_free(dpip_tag);
316
317 /* Finish the SockHandler */
318 sock_handler_close(sh);
319 sock_handler_free(sh);
320
321 return 0;
322 }
323
0 /*
1 * Downloads server (chat version).
2 *
3 * NOTE: A simple downloads dpi that illustrates how to make a dpi-server.
4 *
5 * It uses wget to download a link. This has been tested with wget 1.8.1
6 * The server accepts multiple connections once it has been started.
7 * If there are no requests within 5 minutes it waits for all child processes
8 * to finish and then it exits.
9 *
10 * Copyright 2002-2004 Jorge Arellano Cid <jcid@dillo.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 */
18
19
20 #include <config.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/un.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <signal.h>
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <sys/time.h>
34 #include <glib.h>
35 #include "../dpip/dpip.h"
36 #include "dpiutil.h"
37
38 /*
39 * Debugging macros
40 */
41 #define _MSG(fmt...)
42 #define _CMSG(fmt...)
43 #define MSG(fmt...) g_print("[downloads dpi]: " fmt)
44 #define CMSG(fmt...) g_print("[downloads (child)]: " fmt)
45
46 pid_t origpid, fpid;
47
48 /*---------------------------------------------------------------------------*/
49
50 /*
51 * Make a new name and place it in 'dl_dest'.
52 */
53 static void make_new_name(gchar **dl_dest, const gchar *url)
54 {
55 GString *gstr = g_string_new(*dl_dest);
56 gint idx = gstr->len;
57
58 if (gstr->str[idx - 1] != '/'){
59 g_string_append_c(gstr, '/');
60 ++idx;
61 }
62
63 /* Use a mangled url as name */
64 g_string_append(gstr, url);
65 for ( ; idx < gstr->len; ++idx)
66 if (!isalnum(gstr->str[idx]))
67 gstr->str[idx] = '_';
68
69 /* free memory */
70 g_free(*dl_dest);
71 *dl_dest = gstr->str;
72 g_string_free(gstr, FALSE);
73 }
74
75
76 /*---------------------------------------------------------------------------*/
77
78 /*
79 * SIGCHLD handler
80 */
81 static void sigchld(int sig)
82 {
83 MSG("received sigchld, pid=%d\n", origpid);
84 fflush(stderr);
85 while (waitpid(0, NULL, WNOHANG) > 0) {
86 }
87 }
88
89 /*
90 * Establish SIGCHLD handler
91 */
92 static void est_sigchld(void)
93 {
94 struct sigaction act;
95 sigset_t block;
96
97 sigemptyset(&block);
98 sigaddset(&block, SIGCHLD);
99 act.sa_handler = sigchld;
100 act.sa_mask = block;
101 act.sa_flags = SA_NOCLDSTOP;
102 sigaction(SIGCHLD, &act, NULL);
103 }
104
105 /*
106 * Read a single line from a socket and store it in a GString.
107 */
108 static ssize_t readline(int socket, GString ** msg)
109 {
110 ssize_t st;
111 gchar buf[16384], *aux;
112
113 /* can't use fread() */
114 do
115 st = read(socket, buf, 16384);
116 while (st < 0 && errno == EINTR);
117
118 if (st == -1)
119 MSG("readline, %s\n", strerror(errno));
120
121 if (st > 0) {
122 aux = g_strndup(buf, (guint)st);
123 g_string_assign(*msg, aux);
124 g_free(aux);
125 } else {
126 g_string_assign(*msg, "");
127 }
128
129 return st;
130 }
131
132
133 /*!
134 * Main download process.
135 */
136 int main(void)
137 {
138 int new_socket, ns;
139 socklen_t csz;
140 ssize_t rdlen;
141 struct sockaddr_un clnt_addr;
142
143 //char *wget_cmd = "wget --no-parent -t 1 -nc -k -nH --cut-dirs=30 -P";
144 //char *wget_cmd = "wget -t 1 -nH -P"; --doesn't rename, +other problems
145 char *wget_cmd = "wget -O - --load-cookies $HOME/.dillo/cookies.txt ";
146
147 char *url = NULL, *esc_url = NULL, *dl_dest = NULL, *cmd = NULL;
148 GString *gs_dl_cmd, *tag;
149 fd_set active_set, selected_set;
150 struct timeval tout;
151 sigset_t blockSC;
152
153 origpid = getpid();
154 fpid = origpid;
155 MSG("v1.1 started (pid=%u)\n", origpid);
156 fflush(stdout);
157 sigemptyset(&blockSC);
158 sigaddset(&blockSC, SIGCHLD);
159 est_sigchld();
160
161 csz = (socklen_t) sizeof(clnt_addr);
162 FD_ZERO(&active_set);
163 FD_SET(STDIN_FILENO, &active_set);
164 while (1) {
165 MSG("before select\n");
166 do {
167 /* exit if there are no download requests after this time */
168 tout.tv_sec = TOUT;
169 tout.tv_usec = 0;
170 selected_set = active_set;
171 ns = select(STDIN_FILENO + 1, &selected_set, NULL, NULL, &tout);
172 } while (ns == -1 && errno == EINTR);
173 MSG("after select\n");
174
175 if (ns == -1) {
176 MSG("select, %s\n", strerror(errno));
177 exit(1);
178
179 } else if (ns == 0) { /* exit if no download requests */
180 close(STDIN_FILENO);
181 printf("downloads server %d:Terminating.\n"
182 "Waiting for children to finish\n", origpid);
183 fflush(stdout);
184 /* BUG? Any further calls to downloads server will be queued by dpid
185 * until all the children have finished. This could be a long time */
186 while (waitpid(-1, NULL, 0) >= 0) {
187 }
188 printf("\n\nDL_SRV %d: EXITING\n", origpid);
189 fflush(stdout);
190 exit(0);
191
192 } else {
193 /* accept the request */
194 do {
195 new_socket = accept(STDIN_FILENO, (struct sockaddr *) &clnt_addr,
196 &csz);
197 } while (new_socket == -1 && errno == EINTR);
198
199 if (new_socket == -1) {
200 MSG("accept, %s\n", strerror(errno));
201 exit(1);
202 }
203 }
204
205 sigprocmask(SIG_BLOCK, &blockSC, NULL);
206 tag = g_string_new(NULL);
207 MSG("before readline\n");
208 rdlen = readline(new_socket, &tag);
209 MSG("after readline\n");
210 MSG("[%s]\n", tag->str);
211
212 if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) {
213 MSG("Failed to parse 'cmd' in %s\n", tag->str);
214 exit(1);
215 }
216 if (strcmp(cmd, "DpiBye") == 0) {
217 MSG("got DpiBye, terminating.\n");
218 exit(0);
219 }
220 if (strcmp(cmd, "download") != 0) {
221 MSG("unknown command: '%s'. Aborting.\n", cmd);
222 exit(1);
223 }
224 g_free(cmd);
225
226 fpid = fork();
227 if (fpid == 0) {
228 pid_t ppid, cpid;
229 FILE *in_stream, *out_stream;
230 gchar buf[4096];
231 struct stat sb;
232 size_t n;
233
234 origpid = cpid = getpid();
235 ppid = getppid();
236 CMSG("pid=%u, from parent=%u\n", (unsigned)cpid, (unsigned)ppid);
237 if (!(url = a_Dpip_get_attr(tag->str,(size_t)tag->len, "url"))){
238 CMSG("Failed to parse 'url' in %s\n", tag->str);
239 exit(1);
240 }
241
242 dl_dest = a_Dpip_get_attr(tag->str, (size_t)tag->len, "destination");
243 if (dl_dest == NULL) {
244 CMSG("Failed to parse 'destination' in %s\n", tag->str);
245 exit(1);
246 }
247
248 CMSG("url=%s, dl_dest=%s\n", url, dl_dest);
249
250 /* 'dl_dest' may be a directory */
251 if (stat(dl_dest, &sb) == 0 && S_ISDIR(sb.st_mode))
252 make_new_name(&dl_dest, url);
253
254 /* open the target stream */
255 if ((out_stream = fopen(dl_dest, "w")) == NULL) {
256 CMSG("%s\n", strerror(errno));
257 exit(1);
258 }
259
260 /* make the download command string */
261 gs_dl_cmd = g_string_new(NULL);
262 /* escape "'" character for the shell */
263 esc_url = Escape_uri_str(url, "'");
264 /* avoid malicious SMTP relaying with FTP urls */
265 if (g_strncasecmp(esc_url, "ftp:/", 5) == 0)
266 Filter_smtp_hack(esc_url);
267 g_string_sprintf(gs_dl_cmd, "%s '%s'", wget_cmd, esc_url);
268 CMSG(" cmd: %s\n", gs_dl_cmd->str);
269 CMSG(" to: %s\n", dl_dest);
270
271 g_free(dl_dest);
272 g_free(esc_url);
273 g_free(url);
274
275 CMSG("pid=%u, Running: %s\n", cpid, gs_dl_cmd->str);
276
277 /* fork through popen */
278 if ((in_stream = popen(gs_dl_cmd->str, "r")) == NULL) {
279 CMSG("popen, %s\n", strerror(errno));
280 exit(1);
281 }
282
283 /* do the file transfer */
284 while ((n = fread (buf, 1, 4096, in_stream)) > 0)
285 fwrite(buf, 1, n, out_stream);
286
287 /* close transfer */
288 if (pclose(in_stream) != 0)
289 CMSG("pclose, %s\n", strerror(errno));
290 if (fclose(out_stream) != 0)
291 CMSG("fclose, %s\n", strerror(errno));
292
293 g_string_free(gs_dl_cmd, TRUE);
294
295
296 if (close(new_socket) == -1) {
297 CMSG("close, %s\n", strerror(errno));
298 exit(EXIT_FAILURE);
299 }
300
301 CMSG("pid=%u, done!\n", cpid);
302 exit(0);
303 }
304 g_string_free(tag, TRUE);
305 sigprocmask(SIG_UNBLOCK, &blockSC, NULL);
306 close(new_socket);
307 }
308 }
309
0 /*
1 * File: downloads.cc
2 *
3 * Copyright (C) 2005 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * A FLTK2-based GUI for the downloads dpi (dillo plugin).
13 */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <ctype.h>
22 #include <math.h>
23 #include <time.h>
24 #include <signal.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/un.h>
29 #include <sys/wait.h>
30
31 #include <glib.h>
32
33 #include <fltk/run.h>
34 #include <fltk/Window.h>
35 #include <fltk/Widget.h>
36 #include <fltk/damage.h>
37 #include <fltk/Box.h>
38 #include <fltk/draw.h>
39 #include <fltk/HighlightButton.h>
40 #include <fltk/PackedGroup.h>
41 #include <fltk/ScrollGroup.h>
42 #include <fltk/ask.h>
43 #include <fltk/file_chooser.h>
44
45 #include "dpiutil.h"
46 #include "../dpip/dpip.h"
47
48 using namespace fltk;
49
50 /*
51 * Debugging macros
52 */
53 #define _MSG(fmt...)
54 #define MSG(fmt...) g_print("[downloads dpi]: " fmt)
55
56 /*
57 * Internal types
58 */
59 typedef enum {
60 DL_NEWFILE,
61 DL_CONTINUE,
62 DL_RENAME,
63 DL_OVERWRITE,
64 DL_ABORT
65 } DLAction;
66
67 /*
68 * Class declarations
69 */
70
71 // ProgressBar widget --------------------------------------------------------
72
73 // class FL_API ProgressBar : public Widget {
74 class ProgressBar : public Widget {
75 protected:
76 double mMin;
77 double mMax;
78 double mPresent;
79 double mStep;
80 bool mShowPct, mShowMsg;
81 char mMsg[64];
82 Color mTextColor;
83 void draw();
84 public:
85 ProgressBar(int x, int y, int w, int h, const char *lbl = 0);
86 void range(double min, double max, double step = 1) {
87 mMin = min; mMax = max; mStep = step;
88 };
89 void step(double step) { mPresent += step; redraw(); };
90 void move(double step);
91 double minimum() { return mMin; }
92 double maximum() { return mMax; }
93 void minimum(double nm) { mMin = nm; };
94 void maximum(double nm) { mMax = nm; };
95 double position () { return mPresent; }
96 double step() { return mStep; }
97 void position(double pos) { mPresent = pos; redraw(); }
98 void showtext(bool st) { mShowPct = st; }
99 void message(char *msg) { mShowMsg = true; strncpy(mMsg,msg,63); redraw(); }
100 bool showtext() { return mShowPct; }
101 void text_color(Color col) { mTextColor = col; }
102 Color text_color() { return mTextColor; }
103 };
104
105 // Download-item class -------------------------------------------------------
106
107 class DLItem {
108 enum {
109 ST_newline, ST_number, ST_discard, ST_copy
110 };
111
112 pid_t mPid;
113 int LogPipe[2];
114 char *shortname, *fullname;
115 char *target_dir;
116 int log_len, log_max, log_state;
117 char *log_text;
118 time_t init_time;
119 char **dl_argv;
120 time_t twosec_time, onesec_time;
121 int twosec_bytesize, onesec_bytesize;
122 int init_bytesize, curr_bytesize, total_bytesize;
123 int DataDone, LogDone, ForkDone, UpdatesDone, WidgetDone;
124 int WgetStatus;
125
126 int gw, gh;
127 Group *group;
128 ProgressBar *prBar;
129 HighlightButton *prButton;
130 Widget *prTitle, *prGot, *prSize, *prRate, *pr_Rate, *prETA, *prETAt;
131
132 public:
133 DLItem(const char *full_filename, const char *url, DLAction action);
134 ~DLItem();
135 void child_init();
136 void father_init();
137 void update_size(int new_sz);
138 void log_text_add(char *buf, ssize_t st);
139 void log_text_show();
140 void abort_dl();
141 void prButton_cb();
142 pid_t pid() { return mPid; }
143 void pid(pid_t p) { mPid = p; }
144 void child_finished(int status);
145 void status_msg(char *msg) { prBar->message(msg); }
146 Widget *get_widget() { return group; }
147 int widget_done() { return WidgetDone; }
148 void widget_done(int val) { WidgetDone = val; }
149 int updates_done() { return UpdatesDone; }
150 void updates_done(int val) { UpdatesDone = val; }
151 int fork_done() { return ForkDone; }
152 void fork_done(int val) { ForkDone = val; }
153 int log_done() { return LogDone; }
154 void log_done(int val) { LogDone = val; }
155 int wget_status() { return WgetStatus; }
156 void wget_status(int val) { WgetStatus = val; }
157 void update_prSize(int newsize);
158 void update();
159 };
160
161 // DLItem List ---------------------------------------------------------------
162
163 /// BUG: make dynamic
164 class DLItemList {
165 DLItem *mList[32];
166 int mNum, mMax;
167
168 public:
169 DLItemList() { mNum = 0; mMax = 32; }
170 ~DLItemList() { }
171 int num() { return mNum; }
172 void add(DLItem *i) { if (mNum < mMax) mList[mNum++] = i; }
173 DLItem *get(int n) { return (n >= 0 && n < mNum) ? mList[n] : NULL; }
174 void del(int n) { if (n >= 0 && n < mNum) mList[n] = mList[--mNum]; }
175 };
176
177 // DLWin ---------------------------------------------------------------------
178
179 class DLWin {
180 DLItemList *mDList;
181 Window *mWin;
182 ScrollGroup *mScroll;
183 PackedGroup *mPG;
184
185 public:
186 DLWin(int ww, int wh);
187 void add(const char *full_filename, const char *url, DLAction action);
188 void del(int n_item);
189 int num();
190 int num_running();
191 void listen(int req_fd);
192 void show() { mWin->show(); }
193 void hide() { mWin->hide(); }
194 void abort_all();
195 DLAction check_filename(char **p_dl_dest);
196 };
197
198
199 /*
200 * Global variables
201 */
202
203 // SIGCHLD mask
204 sigset_t mask_sigchld;
205
206 // SIGCHLD flag
207 volatile sig_atomic_t caught_sigchld = 0;
208
209 // The download window object
210 static class DLWin *dl_win = NULL;
211
212
213
214 // ProgressBar widget --------------------------------------------------------
215
216 void ProgressBar::move(double step)
217 {
218 mPresent += step;
219 if (mPresent > mMax)
220 mPresent = mMin;
221 redraw();
222 }
223
224 ProgressBar::ProgressBar(int x, int y, int w, int h, const char *lbl)
225 : Widget(x, y, w, h, lbl)
226 {
227 mMin = mPresent = 0;
228 mMax = 100;
229 mShowPct = true;
230 mShowMsg = false;
231 box(DOWN_BOX);
232 selection_color(BLUE);
233 color(WHITE);
234 textcolor(RED);
235 }
236
237 void ProgressBar::draw()
238 {
239 drawstyle(style(), flags());
240 if (damage() & DAMAGE_ALL)
241 draw_box();
242 Rectangle r(w(), h());
243 box()->inset(r);
244 if (mPresent > mMax)
245 mPresent = mMax;
246 if (mPresent < mMin)
247 mPresent = mMin;
248 double pct = (mPresent - mMin) / mMax;
249
250 if (vertical()) {
251 int barHeight = int (r.h() * pct + .5);
252 r.y(r.y() + r.h() - barHeight);
253 r.h(barHeight);
254 } else {
255 r.w(int (r.w() * pct + .5));
256 }
257
258 setcolor(selection_color());
259
260 if (mShowPct) {
261 fillrect(r);
262 } else {
263 Rectangle r2(int (r.w() * pct), 0, int (w() * .1), h());
264 push_clip(r2);
265 fillrect(r);
266 pop_clip();
267 }
268
269 if (mShowMsg) {
270 setcolor(textcolor());
271 setfont(this->labelfont(), this->labelsize());
272 drawtext(mMsg, Rectangle(w(), h()), ALIGN_CENTER);
273 } else if (mShowPct) {
274 char buffer[30];
275 sprintf(buffer, "%d%%", int (pct * 100 + .5));
276 setcolor(textcolor());
277 setfont(this->labelfont(), this->labelsize());
278 drawtext(buffer, Rectangle(w(), h()), ALIGN_CENTER);
279 }
280 }
281
282
283 // Download-item class -------------------------------------------------------
284
285 static void prButton_scb(Widget *, void *cb_data)
286 {
287 DLItem *i = (DLItem *)cb_data;
288
289 i->prButton_cb();
290 }
291
292 DLItem::DLItem(const char *full_filename, const char *url, DLAction action)
293 {
294 struct stat ss;
295 char *p, *esc_url;
296
297 if (pipe(LogPipe) < 0) {
298 MSG("pipe, %s\n", strerror(errno));
299 return;
300 }
301 /* Set FD to background */
302 fcntl(LogPipe[0], F_SETFL,
303 O_NONBLOCK | fcntl(LogPipe[0], F_GETFL));
304
305 fullname = strdup(full_filename);
306 p = strrchr(fullname, '/');
307 shortname = (p) ? strdup(p + 1) : strdup("??");
308 p = strrchr(full_filename, '/');
309 target_dir= p ? g_strndup(full_filename,p-full_filename+1) : g_strdup("??");
310
311 log_len = 0;
312 log_max = 0;
313 log_state = ST_newline;
314 log_text = NULL;
315 onesec_bytesize = twosec_bytesize = curr_bytesize = init_bytesize = 0;
316 total_bytesize = -1;
317
318 // Init value. Reset later, upon the first data bytes arrival
319 init_time = time(NULL);
320
321 // BUG:? test a URL with ' inside.
322 /* escape "'" character for the shell. Is it necessary? */
323 esc_url = Escape_uri_str(url, "'");
324 /* avoid malicious SMTP relaying with FTP urls */
325 if (g_strncasecmp(esc_url, "ftp:/", 5) == 0)
326 Filter_smtp_hack(esc_url);
327 dl_argv = new char*[8];
328 int i = 0;
329 dl_argv[i++] = "wget";
330 if (action == DL_CONTINUE) {
331 if (stat(fullname, &ss) == 0)
332 init_bytesize = (int)ss.st_size;
333 dl_argv[i++] = "-c";
334 }
335 dl_argv[i++] = "--load-cookies";
336 dl_argv[i++] = g_strconcat(g_get_home_dir(), "/.dillo/cookies.txt", NULL);
337 dl_argv[i++] = "-O";
338 dl_argv[i++] = fullname;
339 dl_argv[i++] = esc_url; //g_strdup_printf("'%s'", esc_url);
340 dl_argv[i++] = NULL;
341 //g_free(esc_url);
342
343 DataDone = 0;
344 LogDone = 0;
345 UpdatesDone = 0;
346 ForkDone = 0;
347 WidgetDone = 0;
348 WgetStatus = -1;
349
350 gw = 470, gh = 70;
351 group = new Group(0,0,gw,gh);
352 group->begin();
353 prTitle = new Widget(24, 7, 290, 23, shortname);
354 prTitle->box(fltk::RSHADOW_BOX);
355 prTitle->align(ALIGN_LEFT|ALIGN_INSIDE|ALIGN_CLIP);
356 // Attach this 'log_text' to the tooltip
357 log_text_add("Target File: ", 13);
358 log_text_add(fullname, strlen(fullname));
359 log_text_add("\n\n", 2);
360
361 prBar = new ProgressBar(24, 40, 92, 20);
362 prBar->box(BORDER_BOX); // ENGRAVED_BOX
363 prBar->tooltip("Progress Status");
364
365 int ix = 122, iy = 36, iw = 50, ih = 14;
366 Widget *o = new Widget(ix,iy,iw,ih, "Got");
367 o->box(RFLAT_BOX);
368 o->color((Color)0xc0c0c000);
369 o->tooltip("Downloaded Size");
370 prGot = new Widget(ix,iy+14,iw,ih, "0KB");
371 prGot->labelcolor((Color)0x6c6cbd00);
372 prGot->box(fltk::NO_BOX);
373
374 ix += iw;
375 o = new Widget(ix,iy,iw,ih, "Size");
376 o->box(RFLAT_BOX);
377 o->color((Color)0xc0c0c000);
378 o->tooltip("Total Size");
379 prSize = new Widget(ix,iy+14,iw,ih, "??");
380 prSize->box(fltk::NO_BOX);
381
382 ix += iw;
383 o = new Widget(ix,iy,iw,ih, "Rate");
384 o->box(RFLAT_BOX);
385 o->color((Color)0xc0c0c000);
386 o->tooltip("Current transfer Rate (KBytes/sec)");
387 prRate = new Widget(ix,iy+14,iw,ih, "??");
388 prRate->box(fltk::NO_BOX);
389
390 ix += iw;
391 o = new Widget(ix,iy,iw,ih, "~Rate");
392 o->box(RFLAT_BOX);
393 o->color((Color)0xc0c0c000);
394 o->tooltip("Average transfer Rate (KBytes/sec)");
395 pr_Rate = new Widget(ix,iy+14,iw,ih, "??");
396 pr_Rate->box(fltk::NO_BOX);
397
398 ix += iw;
399 prETAt = o = new Widget(ix,iy,iw,ih, "ETA");
400 o->box(RFLAT_BOX);
401 o->color((Color)0xc0c0c000);
402 o->tooltip("Estimated Time of Arrival");
403 prETA = new Widget(ix,iy+14,iw,ih, "??");
404 prETA->box(fltk::NO_BOX);
405
406 //ix += 50;
407 //prButton = new HighlightButton(ix, 41, 38, 19, "Stop");
408 prButton = new HighlightButton(328, 9, 38, 19, "Stop");
409 prButton->tooltip("Stop this transfer");
410 prButton->box(UP_BOX);
411 prButton->clear_tab_to_focus();
412 prButton->callback(prButton_scb, this);
413
414 //group->resizable(group);
415 group->box(ROUND_UP_BOX);
416 group->end();
417 }
418
419 DLItem::~DLItem()
420 {
421 free(shortname);
422 g_free(fullname);
423 g_free(target_dir);
424 free(log_text);
425 int idx = (strcmp(dl_argv[1], "-c")) ? 2 : 3;
426 g_free(dl_argv[idx]);
427 g_free(dl_argv[idx+3]);
428 delete(dl_argv);
429
430 delete(group);
431 }
432
433 /*
434 * Abort a running download
435 */
436 void DLItem::abort_dl()
437 {
438 if (!log_done()) {
439 close(LogPipe[0]);
440 remove_fd(LogPipe[0]);
441 log_done(1);
442 // Stop wget
443 if (!fork_done())
444 kill(pid(), SIGTERM);
445 }
446 widget_done(1);
447 }
448
449 void DLItem::prButton_cb()
450 {
451 prButton->deactivate();
452 abort_dl();
453 }
454
455 void DLItem::child_init()
456 {
457 close(0); // stdin
458 close(1); // stdout
459 close(LogPipe[0]);
460 dup2(LogPipe[1], 2); // stderr
461 // set the locale to C for log parsing
462 setenv("LC_ALL", "C", 1);
463 // start wget
464 execvp(dl_argv[0], dl_argv);
465 }
466
467 /*
468 * Update displayed size
469 */
470 void DLItem::update_prSize(int newsize)
471 {
472 char num[64];
473
474 if (newsize > 1024 * 1024)
475 snprintf(num, 64, "%.1fMB", (float)newsize / (1024*1024));
476 else
477 snprintf(num, 64, "%.0fKB", (float)newsize / 1024);
478 prSize->copy_label(num);
479 prSize->redraw();
480 }
481
482 void DLItem::log_text_add(char *buf, ssize_t st)
483 {
484 char *p, *q, *d, num[64];
485
486 // Make room...
487 if (log_len + st >= log_max) {
488 log_max = log_len + st + 1024;
489 log_text = (char *) realloc (log_text, log_max);
490 log_text[log_len] = 0;
491 prTitle->tooltip(log_text);
492 }
493
494 // FSM to remove wget's "dot-progress" (i.e. "^ " || "^[0-9]+K")
495 q = log_text + log_len;
496 for (p = buf; (p - buf) < st; ++p) {
497 switch (log_state) {
498 case ST_newline:
499 if (*p == ' ') {
500 log_state = ST_discard;
501 } else if (isdigit(*p)) {
502 *q++ = *p; log_state = ST_number;
503 } else if (*p == '\n') {
504 *q++ = *p;
505 } else {
506 *q++ = *p; log_state = ST_copy;
507 }
508 break;
509 case ST_number:
510 if (isdigit(*q++ = *p)) {
511 // keep here
512 } else if (*p == 'K') {
513 for(--q; isdigit(q[-1]); --q); log_state = ST_discard;
514 } else {
515 log_state = ST_copy;
516 }
517 break;
518 case ST_discard:
519 if (*p == '\n')
520 log_state = ST_newline;
521 break;
522 case ST_copy:
523 if ((*q++ = *p) == '\n')
524 log_state = ST_newline;
525 break;
526 }
527 }
528 *q = 0;
529 log_len = strlen(log_text);
530
531 // Now scan for the length of the file
532 if (total_bytesize == -1) {
533 p = strstr(log_text, "\nLength: ");
534 if (p && isdigit(p[9]) && strchr(p + 9, ' ')) {
535 for (p += 9, d = &num[0]; *p != ' '; ++p)
536 if (isdigit(*p))
537 *d++ = *p;
538 *d = 0;
539 total_bytesize = strtol (num, NULL, 10);
540 // Update displayed size
541 update_prSize(total_bytesize);
542 }
543 }
544
545 // Show we're connecting...
546 if (curr_bytesize == 0) {
547 prTitle->label("Connecting...");
548 prTitle->redraw();
549 }
550 }
551
552 ///
553 void DLItem::log_text_show()
554 {
555 fprintf(stderr, "\nStored Log:\n%s", log_text);
556 }
557
558 void DLItem::update_size(int new_sz)
559 {
560 char buf[64];
561
562 if (curr_bytesize == 0 && new_sz) {
563 // Start the timer with the first bytes got
564 init_time = time(NULL);
565 // Update the title
566 prTitle->label(shortname);
567 prTitle->redraw();
568 }
569
570 curr_bytesize = new_sz;
571 if (curr_bytesize > 1024 * 1024)
572 snprintf(buf, 64, "%.1fMB", (float)curr_bytesize / (1024*1024));
573 else
574 snprintf(buf, 64, "%.0fKB", (float)curr_bytesize / 1024);
575 prGot->copy_label(buf);
576 prGot->redraw();
577 if (total_bytesize == -1) {
578 prBar->showtext(false);
579 prBar->move(1);
580 } else {
581 prBar->showtext(true);
582 double pos = 100.0 * (double)curr_bytesize / total_bytesize;
583 prBar->position(pos);
584 }
585 }
586
587 static void read_log_cb(int fd_in, void *data)
588 {
589 DLItem *dl_item = (DLItem *)data;
590 int BufLen = 4096;
591 char Buf[BufLen];
592 ssize_t st;
593 int ret = -1;
594
595 do {
596 st = read(fd_in, Buf, BufLen);
597 if (st < 0) {
598 if (errno == EAGAIN) {
599 ret = 1;
600 break;
601 }
602 perror("read, ");
603 break;
604 } else if (st == 0) {
605 close(fd_in);
606 remove_fd(fd_in, 1);
607 dl_item->log_done(1);
608 ret = 0;
609 break;
610 } else {
611 dl_item->log_text_add(Buf, st);
612 }
613 } while (1);
614 }
615
616 void DLItem::father_init()
617 {
618 close(LogPipe[1]);
619 add_fd(LogPipe[0], 1, read_log_cb, this); // Read
620
621 // Start the timer after the child is running.
622 // (this makes a big difference with wget)
623 //init_time = time(NULL);
624 }
625
626 /*
627 * Our wget exited, let's check its status and update the panel.
628 */
629 void DLItem::child_finished(int status)
630 {
631 wget_status(status);
632
633 if (status == 0) {
634 prButton->label("Done");
635 prButton->tooltip("Close this information panel");
636 } else {
637 prButton->label("Close");
638 prButton->tooltip("Close this information panel");
639 status_msg("ABORTED");
640 if (curr_bytesize == 0) {
641 // Update the title
642 prTitle->label(shortname);
643 prTitle->redraw();
644 }
645 }
646 prButton->activate();
647 prButton->redraw();
648 MSG("wget status %d\n", status);
649 }
650
651 /*
652 * Convert seconds into human readable [hour]:[min]:[sec] string.
653 */
654 void secs2timestr(int et, char *str)
655 {
656 int eh, em, es;
657
658 eh = et / 3600; em = (et % 3600) / 60; es = et % 60;
659 if (eh == 0) {
660 if (em == 0)
661 snprintf(str, 8, "%ds", es);
662 else
663 snprintf(str, 8, "%dm%ds", em, es);
664 } else {
665 snprintf(str, 8, "%dh%dm", eh, em);
666 }
667 }
668
669 /*
670 * Update Got, Rate, ~Rate and ETA
671 */
672 void DLItem::update()
673 {
674 struct stat ss;
675 time_t curr_time;
676 float csec, tsec, rate, _rate = 0;
677 char str[64];
678 int et;
679
680 if (updates_done())
681 return;
682
683 /* Update curr_size */
684 if (stat(fullname, &ss) == -1) {
685 MSG("stat, %s\n", strerror(errno));
686 return;
687 }
688 update_size((int)ss.st_size);
689
690 /* Get current time */
691 time(&curr_time);
692 csec = (float) (curr_time - init_time);
693
694 /* Rate */
695 if (csec >= 2) {
696 tsec = (float) (curr_time - twosec_time);
697 rate = ((float)(curr_bytesize-twosec_bytesize) / 1024) / tsec;
698 snprintf(str, 64, (rate < 100) ? "%.1fK/s" : "%.0fK/s", rate);
699 prRate->copy_label(str);
700 prRate->redraw();
701 }
702 /* ~Rate */
703 if (csec >= 1) {
704 _rate = ((float)(curr_bytesize-init_bytesize) / 1024) / csec;
705 snprintf(str, 64, (_rate < 100) ? "%.1fK/s" : "%.0fK/s", _rate);
706 pr_Rate->copy_label(str);
707 pr_Rate->redraw();
708 }
709
710 /* ETA */
711 if (fork_done()) {
712 updates_done(1); // Last update
713 prETAt->label("Time");
714 prETAt->tooltip("Download Time");
715 prETAt->redraw();
716 secs2timestr((int)csec, str);
717 prETA->copy_label(str);
718 if (total_bytesize == -1) {
719 update_prSize(curr_bytesize);
720 if (wget_status() == 0)
721 status_msg("Done");
722 }
723 } else {
724 if (_rate > 0 && total_bytesize > 0 && curr_bytesize > 0) {
725 et = (int)((total_bytesize-curr_bytesize) / (_rate * 1024));
726 secs2timestr(et, str);
727 prETA->copy_label(str);
728 }
729 }
730 prETA->redraw();
731
732 /* Update one and two secs ago times and bytesizes */
733 twosec_time = onesec_time;
734 onesec_time = curr_time;
735 twosec_bytesize = onesec_bytesize;
736 onesec_bytesize = curr_bytesize;
737 }
738
739 // SIGCHLD -------------------------------------------------------------------
740
741 /*! SIGCHLD handler
742 */
743 void raw_sigchld(int)
744 {
745 caught_sigchld = 1;
746 }
747
748 /*! Establish SIGCHLD handler */
749 void est_sigchld(void)
750 {
751 struct sigaction sigact;
752 sigset_t set;
753
754 (void) sigemptyset(&set);
755 sigact.sa_handler = raw_sigchld;
756 sigact.sa_mask = set;
757 sigact.sa_flags = SA_NOCLDSTOP;
758 if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
759 perror("sigaction");
760 exit(1);
761 }
762 }
763
764 /*
765 * Timeout function to check wget's exit status.
766 */
767 void cleanup_cb(void *data)
768 {
769 DLItemList *list = (DLItemList *)data;
770
771 sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
772 if (caught_sigchld) {
773 /* Handle SIGCHLD */
774 int i, status;
775 for (i = 0; i < list->num(); ++i) {
776 if (!list->get(i)->fork_done() &&
777 waitpid(list->get(i)->pid(), &status, WNOHANG) > 0) {
778 list->get(i)->child_finished(status);
779 list->get(i)->fork_done(1);
780 }
781 }
782 caught_sigchld = 0;
783 }
784 sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
785
786 repeat_timeout(1.0,cleanup_cb,data);
787 }
788
789 /*
790 * Timeout function to update the widget indicators,
791 * also remove widgets marked "done".
792 */
793 void update_cb(void *data)
794 {
795 static int cb_used = 0;
796
797 DLItemList *list = (DLItemList *)data;
798
799 /* Update the widgets and remove the ones marked as done */
800 for (int i = 0; i < list->num(); ++i) {
801 if (!list->get(i)->widget_done()) {
802 list->get(i)->update();
803 } else if (list->get(i)->fork_done()) {
804 // widget_done and fork_done avoid a race condition.
805 dl_win->del(i); --i;
806 }
807 cb_used = 1;
808 }
809
810 if (cb_used && list->num() == 0)
811 exit(0);
812
813 repeat_timeout(1.0,update_cb,data);
814 }
815
816
817 // DLWin ---------------------------------------------------------------------
818
819 /*
820 * Read a single line from a socket and store it in a GString.
821 */
822 static ssize_t readline(int socket, GString ** msg)
823 {
824 ssize_t st;
825 gchar buf[16384], *aux;
826
827 /* can't use fread() */
828 do
829 st = read(socket, buf, 16384);
830 while (st < 0 && errno == EINTR);
831
832 if (st == -1)
833 MSG("readline, %s\n", strerror(errno));
834
835 if (st > 0) {
836 aux = g_strndup(buf, (guint)st);
837 g_string_assign(*msg, aux);
838 g_free(aux);
839 } else {
840 g_string_assign(*msg, "");
841 }
842
843 return st;
844 }
845
846 /*
847 * Make a new name and place it in 'dl_dest'.
848 */
849 static void make_new_name(gchar **dl_dest, const gchar *url)
850 {
851 GString *gstr = g_string_new(*dl_dest);
852 gint idx = gstr->len;
853
854 if (gstr->str[idx - 1] != '/'){
855 g_string_append_c(gstr, '/');
856 ++idx;
857 }
858
859 /* Use a mangled url as name */
860 g_string_append(gstr, url);
861 for ( ; idx < gstr->len; ++idx)
862 if (!isalnum(gstr->str[idx]))
863 gstr->str[idx] = '_';
864
865 /* free memory */
866 g_free(*dl_dest);
867 *dl_dest = gstr->str;
868 g_string_free(gstr, FALSE);
869 }
870
871 /*
872 * Callback function for the request socket.
873 * Read the request, parse and start a new download.
874 */
875 static void read_req_cb(int req_fd, void *)
876 {
877 GString *tag;
878 struct sockaddr_un clnt_addr;
879 int new_socket;
880 socklen_t csz;
881 struct stat sb;
882 char *cmd = NULL, *url = NULL, *dl_dest = NULL;
883 DLAction action = DL_ABORT; /* compiler happiness */
884
885 /* Initialize the value-result parameter */
886 csz = sizeof(struct sockaddr_un);
887 /* accept the request */
888 do {
889 new_socket = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
890 } while (new_socket == -1 && errno == EINTR);
891 if (new_socket == -1) {
892 MSG("accept, %s fd=%d\n", strerror(errno), req_fd);
893 return;
894 }
895
896 //sigprocmask(SIG_BLOCK, &blockSC, NULL);
897 tag = g_string_new(NULL);
898 readline(new_socket, &tag);
899 close(new_socket);
900 _MSG("Received tag={%s}\n", tag->str);
901
902 if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) {
903 MSG("Failed to parse 'cmd' in %s\n", tag->str);
904 goto end;
905 }
906 if (strcmp(cmd, "DpiBye") == 0) {
907 MSG("got DpiBye, ignoring...\n");
908 goto end;
909 }
910 if (strcmp(cmd, "download") != 0) {
911 MSG("unknown command: '%s'. Aborting.\n", cmd);
912 goto end;
913 }
914 if (!(url = a_Dpip_get_attr(tag->str,(size_t)tag->len, "url"))){
915 MSG("Failed to parse 'url' in %s\n", tag->str);
916 goto end;
917 }
918 if (!(dl_dest = a_Dpip_get_attr(tag->str,(size_t)tag->len,"destination"))){
919 MSG("Failed to parse 'destination' in %s\n", tag->str);
920 goto end;
921 }
922 /* 'dl_dest' may be a directory */
923 if (stat(dl_dest, &sb) == 0 && S_ISDIR(sb.st_mode))
924 make_new_name(&dl_dest, url);
925
926 action = dl_win->check_filename(&dl_dest);
927 if (action != DL_ABORT) {
928 // Start the whole thing whithin FLTK.
929 dl_win->add(dl_dest, url, action);
930 } else if (dl_win->num() == 0) {
931 exit(0);
932 }
933
934 end:
935 g_free(cmd);
936 g_free(url);
937 g_free(dl_dest);
938 g_string_free(tag, TRUE);
939 }
940
941 /*
942 * Callback for close window request (WM or EscapeKey press)
943 */
944 static void dlwin_esc_cb(Widget *, void *)
945 {
946 char *msg = "There are running downloads.\n"
947 "ABORT them and EXIT anyway?";
948
949 if (dl_win && dl_win->num_running() > 0) {
950 int ch = fltk::choice(msg, "Yes", "*No", "Cancel");
951 if (ch != 0)
952 return;
953 }
954
955 /* abort each download properly */
956 dl_win->abort_all();
957 }
958
959 /*
960 * Add a new download request to the main window and
961 * fork a child to do the job.
962 */
963 void DLWin::add(const char *full_filename, const char *url, DLAction action)
964 {
965 DLItem *dl_item = new DLItem(full_filename, url, action);
966 mDList->add(dl_item);
967 //mPG->add(*dl_item->get_widget());
968 mPG->insert(*dl_item->get_widget(), 0);
969
970 _MSG("Child index = %d\n", mPG->find(dl_item->get_widget()));
971
972 // Start the child process
973 pid_t f_pid = fork();
974 if (f_pid == 0) {
975 /* child */
976 dl_item->child_init();
977 _exit(EXIT_FAILURE);
978 } else if (f_pid < 0) {
979 perror("fork, ");
980 exit(1);
981 } else {
982 /* father */
983 dl_win->show();
984 dl_item->pid(f_pid);
985 dl_item->father_init();
986 }
987 }
988
989 /*
990 * Decide what to do whe the filename already exists.
991 * (renaming takes place here when necessary)
992 */
993 DLAction DLWin::check_filename(char **p_fullname)
994 {
995 struct stat ss;
996 char *msg;
997 int ch;
998 DLAction ret = DL_ABORT;
999
1000 if (stat(*p_fullname, &ss) == -1)
1001 return DL_NEWFILE;
1002
1003 msg = g_strdup_printf(
1004 "The file:\n %s (%d Bytes)\nalready exists. What do we do?",
1005 *p_fullname, (int)ss.st_size);
1006 ch = fltk::choice(msg, "Rename", "Continue", "Abort");
1007 g_free(msg);
1008 MSG("Choice %d\n", ch);
1009 if (ch == 0) {
1010 const char *p;
1011 p = fltk::file_chooser("Enter a new name:", NULL, *p_fullname);
1012 if (p) {
1013 g_free(*p_fullname);
1014 *p_fullname = g_strdup(p);
1015 ret = check_filename(p_fullname);
1016 }
1017 } else if (ch == 1) {
1018 ret = DL_CONTINUE;
1019 }
1020 return ret;
1021 }
1022
1023 /*
1024 * Add a new download request to the main window and
1025 * fork a child to do the job.
1026 */
1027 void DLWin::del(int n_item)
1028 {
1029 DLItem *dl_item = mDList->get(n_item);
1030
1031 // Remove the widget from the scroll group
1032 mPG->remove(dl_item->get_widget());
1033 // Resize the scroll group
1034 mPG->resize(mWin->w(), 1);
1035
1036 mDList->del(n_item);
1037 delete(dl_item);
1038 }
1039
1040 /*
1041 * Return number of entries
1042 */
1043 int DLWin::num()
1044 {
1045 return mDList->num();
1046 }
1047
1048 /*
1049 * Return number of running downloads
1050 */
1051 int DLWin::num_running()
1052 {
1053 int i, nr;
1054
1055 for (i = nr = 0; i < mDList->num(); ++i)
1056 if (!mDList->get(i)->fork_done())
1057 ++nr;
1058 return nr;
1059 }
1060
1061 /*
1062 * Set a callback function for the request socket
1063 */
1064 void DLWin::listen(int req_fd)
1065 {
1066 add_fd(req_fd, 1, read_req_cb, NULL); // Read
1067 }
1068
1069 /*
1070 * Abort each download properly, and let the main cycle exit
1071 */
1072 void DLWin::abort_all()
1073 {
1074 for (int i = 0; i < mDList->num(); ++i)
1075 mDList->get(i)->abort_dl();
1076 }
1077
1078 /*
1079 * Create the main window and an empty list of requests.
1080 */
1081 DLWin::DLWin(int ww, int wh) {
1082
1083 // Init an empty list for the download requests
1084 mDList = new DLItemList();
1085
1086 // Create the empty main window
1087 mWin = new Window(ww, wh, "Downloads:");
1088 mWin->begin();
1089 mScroll = new ScrollGroup(0,0,ww,wh);
1090 mScroll->begin();
1091 mPG = new PackedGroup(0,0,ww,wh);
1092 mPG->end();
1093 //mPG->spacing(10);
1094 mScroll->end();
1095 mWin->resizable(mWin);
1096 mWin->end();
1097 mWin->callback(dlwin_esc_cb, NULL);
1098 mWin->show();
1099
1100 // Set SIGCHLD handlers
1101 sigemptyset(&mask_sigchld);
1102 sigaddset(&mask_sigchld, SIGCHLD);
1103 est_sigchld();
1104
1105 // Set the cleanup timeout
1106 add_timeout(1.0, cleanup_cb, mDList);
1107 // Set the update timeout
1108 add_timeout(1.0, update_cb, mDList);
1109 }
1110
1111
1112 // ---------------------------------------------------------------------------
1113
1114
1115
1116 //int main(int argc, char **argv)
1117 int main()
1118 {
1119 int ww = 420, wh = 85;
1120
1121 lock();
1122
1123 // Create the download window
1124 dl_win = new DLWin(ww, wh);
1125
1126 // Start listening to the request socket
1127 dl_win->listen(STDIN_FILENO);
1128
1129 MSG("started...\n");
1130
1131 return run();
1132 }
1133
0 /*
1 * File: dpiutil.c
2 *
3 * Copyright 2004 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 */
11
12 #include "dpiutil.h"
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <errno.h>
19 #include <sys/socket.h>
20 #include <glib.h>
21
22 /* Escaping/De-escaping ---------------------------------------------------*/
23
24 /*
25 * Escape URI characters in 'esc_set' as %XX sequences.
26 * Return value: New escaped string.
27 */
28 gchar *Escape_uri_str(const gchar *str, gchar *p_esc_set)
29 {
30 static const char *hex = "0123456789ABCDEF";
31 gchar *p, *esc_set;
32 GString *gstr;
33 gint i;
34
35 esc_set = (p_esc_set) ? p_esc_set : "%#:' ";
36 gstr = g_string_sized_new(64);
37 for (i = 0; str[i]; ++i) {
38 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
39 g_string_append_c(gstr, '%');
40 g_string_append_c(gstr, hex[(str[i] >> 4) & 15]);
41 g_string_append_c(gstr, hex[str[i] & 15]);
42 } else {
43 g_string_append_c(gstr, str[i]);
44 }
45 }
46 p = gstr->str;
47 g_string_free(gstr, FALSE);
48
49 return p;
50 }
51
52 static const char *unsafe_chars = "&<>\"'";
53 static const char *unsafe_rep[] =
54 { "&amp;", "&lt;", "&gt;", "&quot;", "&#39;" };
55 static const int unsafe_rep_len[] = { 5, 4, 4, 6, 5 };
56
57 /*
58 * Escape unsafe characters as html entities.
59 * Return value: New escaped string.
60 */
61 gchar *Escape_html_str(const gchar *str)
62 {
63 gint i;
64 gchar *p;
65 GString *gstr = g_string_sized_new(64);
66
67 for (i = 0; str[i]; ++i) {
68 if ((p = strchr(unsafe_chars, str[i])))
69 g_string_append(gstr, unsafe_rep[p - unsafe_chars]);
70 else
71 g_string_append_c(gstr, str[i]);
72 }
73 p = gstr->str;
74 g_string_free(gstr, FALSE);
75
76 return p;
77 }
78
79 /*
80 * Unescape a few HTML entities (inverse of Escape_html_str)
81 * Return value: New unescaped string.
82 */
83 gchar *Unescape_html_str(const gchar *str)
84 {
85 gint i, j, k;
86 gchar *u_str = g_strdup(str);
87
88 if (!strchr(str, '&'))
89 return u_str;
90
91 for (i = 0, j = 0; str[i]; ++i) {
92 if (str[i] == '&') {
93 for (k = 0; k < 5; ++k) {
94 if (!g_strncasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) {
95 i += unsafe_rep_len[k] - 1;
96 break;
97 }
98 }
99 u_str[j++] = (k < 5) ? unsafe_chars[k] : str[i];
100 } else {
101 u_str[j++] = str[i];
102 }
103 }
104 u_str[j] = 0;
105
106 return u_str;
107 }
108
109 /*
110 * Filter '\n', '\r', "%0D" and "%0A" from the authority part of an FTP url.
111 * This helps to avoid a SMTP relaying hack. This filtering could be done
112 * only when port == 25, but if the mail server is listening on another
113 * port it wouldn't work.
114 * Note: AFAIS this should be done by wget.
115 */
116 char *Filter_smtp_hack(char *url)
117 {
118 int i;
119 char c;
120
121 if (strlen(url) > 6) { /* ftp:// */
122 for (i = 6; (c = url[i]) && c != '/'; ++i) {
123 if (c == '\n' || c == '\r') {
124 memmove(url + i, url + i + 1, strlen(url + i));
125 --i;
126 } else if (c == '%' && url[i+1] == '0' &&
127 (tolower(url[i+2]) == 'a' || tolower(url[i+2]) == 'd')) {
128 memmove(url + i, url + i + 3, strlen(url + i + 2));
129 --i;
130 }
131 }
132 }
133 return url;
134 }
135
136
137 /* Streamed Sockets API (not mandatory) ----------------------------------*/
138
139 /*
140 * Create and initialize the SockHandler structure
141 */
142 SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz)
143 {
144 SockHandler *sh = g_new(SockHandler, 1);
145
146 /* init descriptors and streams */
147 sh->fd_in = fd_in;
148 sh->fd_out = fd_out;
149 sh->out = fdopen(fd_out, "w");
150
151 /* init buffer */
152 sh->buf_max = 8 * 1024 + 128;
153 sh->buf = g_new(char, sh->buf_max);
154 sh->buf_sz = 0;
155 sh->flush_sz = flush_sz;
156
157 return sh;
158 }
159
160 /*
161 * Streamed write to socket
162 * Return: 0 on success, 1 on error.
163 */
164 int sock_handler_write(SockHandler *sh, const char *Data, size_t DataSize,
165 int flush)
166 {
167 gint retval = 1;
168
169 /* append to buf */
170 while (sh->buf_max < sh->buf_sz + DataSize) {
171 sh->buf_max <<= 1;
172 sh->buf = g_realloc(sh->buf, sh->buf_max);
173 }
174 memcpy(sh->buf + sh->buf_sz, Data, DataSize);
175 sh->buf_sz += DataSize;
176 /*
177 g_printerr(
178 "sh->buf=%p, sh->buf_sz=%d, sh->buf_max=%d, sh->flush_sz=%d\n",
179 sh->buf, sh->buf_sz, sh->buf_max, sh->flush_sz);
180 */
181 /**/
182 #if 0
183 {
184 guint i;
185 /* Test dpip's stream handling by chopping data into characters */
186 for (i = 0; i < sh->buf_sz; ++i) {
187 fputc(sh->buf[i], sh->out);
188 fflush(sh->out);
189 usleep(50);
190 }
191 if (i == sh->buf_sz) {
192 sh->buf_sz = 0;
193 retval = 0;
194 }
195 }
196 #else
197 /* flush data if necessary */
198 if (flush || sh->buf_sz >= sh->flush_sz) {
199 if (sh->buf_sz && fwrite (sh->buf, sh->buf_sz, 1, sh->out) != 1) {
200 perror("[sock_handler_write]");
201 } else {
202 fflush(sh->out);
203 sh->buf_sz = 0;
204 retval = 0;
205 }
206
207 } else {
208 retval = 0;
209 }
210 #endif
211 return retval;
212 }
213
214 /*
215 * Convenience function.
216 */
217 int sock_handler_write_str(SockHandler *sh, const char *str, int flush)
218 {
219 return sock_handler_write(sh, str, strlen(str), flush);
220 }
221
222 /*
223 * Return a newlly allocated string with the contents read from the socket.
224 */
225 gchar *sock_handler_read(SockHandler *sh)
226 {
227 ssize_t st;
228 gchar buf[16384];
229
230 /* can't use fread() */
231 do
232 st = read(sh->fd_in, buf, 16384);
233 while (st < 0 && errno == EINTR);
234
235 if (st == -1)
236 perror("[sock_handler_read]");
237
238 return (st > 0) ? g_strndup(buf, (guint)st) : NULL;
239 }
240
241 /*
242 * Close this socket for reading and writing.
243 */
244 void sock_handler_close(SockHandler *sh)
245 {
246 /* flush before closing */
247 sock_handler_write(sh, "", 0, 1);
248
249 fclose(sh->out);
250 close(sh->fd_out);
251 }
252
253 /*
254 * Free the SockHandler structure
255 */
256 void sock_handler_free(SockHandler *sh)
257 {
258 g_free(sh->buf);
259 g_free(sh);
260 }
261
262 /* ------------------------------------------------------------------------ */
263
0 /*
1 * File: dpiutil.h
2 *
3 * Copyright 2004-2005 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 */
11
12 /*
13 * This file contains common functions used by dpi programs.
14 * (i.e. a convenience library).
15 */
16
17 #ifndef __DPIUTIL_H__
18 #define __DPIUTIL_H__
19
20 #include <stdio.h>
21 #include <glib.h>
22
23 #ifdef __cplusplus
24 extern "C" {
25 #endif /* __cplusplus */
26
27
28 #define BUFLEN 256
29 #define TOUT 300
30
31
32 /* Streamed Sockets API (not mandatory) ----------------------------------*/
33
34 typedef struct _SockHandler SockHandler;
35 struct _SockHandler {
36 gint fd_in;
37 gint fd_out;
38 /* FILE *in; --Unused. The stream functions block when reading. */
39 FILE *out;
40
41 gchar *buf; /* internal buffer */
42 guint buf_sz; /* data size */
43 guint buf_max; /* allocated size */
44 guint flush_sz; /* max size before flush */
45 };
46
47 SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz);
48 int sock_handler_write(SockHandler *sh, const char *Data,size_t DataSize,
49 int flush);
50 int sock_handler_write_str(SockHandler *sh, const char *str, int flush);
51 gchar *sock_handler_read(SockHandler *sh);
52 void sock_handler_close(SockHandler *sh);
53 void sock_handler_free(SockHandler *sh);
54
55 #define sock_handler_printf(sh, flush, fmt...) \
56 G_STMT_START { \
57 gchar *str = g_strdup_printf(fmt); \
58 sock_handler_write(sh, str, strlen(str), flush); \
59 g_free(str); \
60 } G_STMT_END
61
62 /* ----------------------------------------------------------------------- */
63
64 /*
65 * Escape URI characters in 'esc_set' as %XX sequences.
66 * Return value: New escaped string.
67 */
68 gchar *Escape_uri_str(const gchar *str, gchar *p_esc_set);
69
70 /*
71 * Escape unsafe characters as html entities.
72 * Return value: New escaped string.
73 */
74 gchar *Escape_html_str(const gchar *str);
75
76 /*
77 * Unescape a few HTML entities (inverse of Escape_html_str)
78 * Return value: New unescaped string.
79 */
80 gchar *Unescape_html_str(const gchar *str);
81
82 /*
83 * Filter an SMTP hack with a FTP URI
84 */
85 char *Filter_smtp_hack(char *url);
86
87
88 #ifdef __cplusplus
89 }
90 #endif /* __cplusplus */
91
92 #endif /* __DPIUTIL_H__ */
93
0 /*
1 * File: file.c :)
2 *
3 * Copyright (C) 2000 - 2004 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Directory scanning is no longer streamed, but it gets sorted instead!
13 * Directory entries on top, files next.
14 * With new HTML layout.
15 */
16
17 #include <pthread.h>
18
19 #include <ctype.h> /* for tolower */
20 #include <errno.h> /* for errno */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/un.h>
30 #include <dirent.h>
31 #include <fcntl.h>
32 #include <time.h>
33 #include <signal.h>
34 #include <glib.h>
35
36 #include "../dpip/dpip.h"
37 #include "dpiutil.h"
38
39 #define MAXNAMESIZE 30
40 #define HIDE_DOTFILES TRUE
41
42 #define _MSG(fmt...)
43 #define MSG(fmt...) g_print("[file dpi]: " fmt)
44
45 enum {
46 FILE_OK,
47 FILE_NOT_FOUND,
48 FILE_NO_ACCESS
49 };
50
51 typedef struct {
52 char *full_path;
53 const char *filename;
54 off_t size;
55 mode_t mode;
56 time_t mtime;
57 } FileInfo;
58
59 typedef struct {
60 char *dirname;
61 GList *flist; /* List of files and subdirectories (for sorting) */
62 } DilloDir;
63
64 typedef struct {
65 SockHandler *sh;
66 gint status;
67 gint old_style;
68 pthread_t thrID;
69 gint done;
70 } ClientInfo;
71
72 /*
73 * Forward references
74 */
75 static const char *File_content_type(const char *filename);
76 static gint File_get_file(ClientInfo *Client,
77 const gchar *filename,
78 struct stat *sb,
79 const char *orig_url);
80 static gint File_get_dir(ClientInfo *Client,
81 const gchar *DirName,
82 const char *orig_url);
83
84 /*
85 * Global variables
86 */
87 static volatile gint DPIBYE = 0;
88 static volatile gint ThreadRunning = 0;
89 static gint OLD_STYLE = 0;
90 /* A list for the clients we are serving */
91 static GList *Clients = NULL;
92 /* a mutex for operations on clients */
93 static pthread_mutex_t ClMut;
94
95 /*
96 * Close a file descriptor, but handling EINTR
97 */
98 static void File_close(int fd)
99 {
100 while (close(fd) < 0 && errno == EINTR)
101 ;
102 }
103
104 /*
105 * Detects 'Content-Type' when the server does not supply one.
106 * It uses the magic(5) logic from file(1). Currently, it
107 * only checks the few mime types that Dillo supports.
108 *
109 * 'Data' is a pointer to the first bytes of the raw data.
110 * (this is based on a_Misc_get_content_type_from_data())
111 */
112 static const gchar *File_get_content_type_from_data(void *Data, size_t Size)
113 {
114 static const gchar *Types[] = {
115 "application/octet-stream",
116 "text/html", "text/plain",
117 "image/gif", "image/png", "image/jpeg",
118 };
119 gint Type = 0;
120 gchar *p = Data;
121 size_t i, non_ascci;
122
123 _MSG("File_get_content_type_from_data:: Size = %d\n", Size);
124
125 /* HTML try */
126 for (i = 0; i < Size && isspace(p[i]); ++i);
127 if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
128 (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
129 (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
130 (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) ||
131 /* this line is workaround for FTP through the Squid proxy */
132 (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) {
133
134 Type = 1;
135
136 /* Images */
137 } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
138 Type = 3;
139 } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
140 Type = 4;
141 } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
142 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
143 * at the character representation should be machine independent. */
144 Type = 5;
145
146 /* Text */
147 } else {
148 /* We'll assume "text/plain" if the set of chars above 127 is <= 10
149 * in a 256-bytes sample. Better heuristics are welcomed! :-) */
150 non_ascci = 0;
151 Size = MIN (Size, 256);
152 for (i = 0; i < Size; i++)
153 if ((unsigned char) p[i] > 127)
154 ++non_ascci;
155 if (Size == 256) {
156 Type = (non_ascci > 10) ? 0 : 2;
157 } else {
158 Type = (non_ascci > 0) ? 0 : 2;
159 }
160 }
161
162 return (Types[Type]);
163 }
164
165 /*
166 * Compare two FileInfo pointers
167 * This function is used for sorting directories
168 */
169 static gint File_comp(gconstpointer a, gconstpointer b)
170 {
171 FileInfo *f1 = (FileInfo *) a;
172 FileInfo *f2 = (FileInfo *) b;
173
174 if (S_ISDIR(f1->mode)) {
175 if (S_ISDIR(f2->mode)) {
176 return strcmp(f1->filename, f2->filename);
177 } else {
178 return -1;
179 }
180 } else {
181 if (S_ISDIR(f2->mode)) {
182 return 1;
183 } else {
184 return strcmp(f1->filename, f2->filename);
185 }
186 }
187 }
188
189 /*
190 * Allocate a DilloDir structure, set safe values in it and sort the entries.
191 */
192 static DilloDir *File_dillodir_new(char *dirname)
193 {
194 struct stat sb;
195 struct dirent *de;
196 DIR *dir;
197 DilloDir *Ddir;
198 FileInfo *finfo;
199 char *fname;
200 int dirname_len;
201
202 if ( !(dir = opendir(dirname)))
203 return NULL;
204
205 Ddir = g_new(DilloDir, 1);
206 Ddir->dirname = g_strdup(dirname);
207 Ddir->flist = NULL;
208
209 dirname_len = strlen(Ddir->dirname);
210
211 /* Scan every name and sort them */
212 while ((de = readdir(dir)) != 0) {
213 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
214 continue; /* skip "." and ".." */
215
216 if (HIDE_DOTFILES) {
217 /* Don't add hidden files or backup files to the list */
218 if (de->d_name[0] == '.' ||
219 de->d_name[0] == '#' ||
220 (de->d_name[0] != '\0' &&
221 de->d_name[strlen(de->d_name) - 1] == '~'))
222 continue;
223 }
224
225 fname = g_strdup_printf("%s/%s", Ddir->dirname, de->d_name);
226
227 if (stat(fname, &sb) == -1) {
228 g_free(fname);
229 continue; /* ignore files we can't stat */
230 }
231
232 finfo = g_new(FileInfo, 1);
233 finfo->full_path = fname;
234 finfo->filename = fname + dirname_len + 1;
235 finfo->size = sb.st_size;
236 finfo->mode = sb.st_mode;
237 finfo->mtime = sb.st_mtime;
238
239 Ddir->flist = g_list_prepend(Ddir->flist, finfo);
240 }
241
242 closedir(dir);
243
244 /* sort the entries */
245 Ddir->flist = g_list_sort(Ddir->flist, File_comp);
246
247 return Ddir;
248 }
249
250 /*
251 * Deallocate a DilloDir structure.
252 */
253 static void File_dillodir_free(DilloDir *Ddir)
254 {
255 GList *list;
256
257 for (list = Ddir->flist; list; list = g_list_next(list)) {
258 FileInfo *finfo = list->data;
259 g_free(finfo->full_path);
260 g_free(finfo);
261 }
262
263 g_list_free(Ddir->flist);
264 g_free(Ddir->dirname);
265 g_free(Ddir);
266 }
267
268 /*
269 * Output the string for parent directory
270 */
271 static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
272 {
273 if (strcmp(dirname, "/") != 0) { /* Not the root dir */
274 char *p, *parent, *HUparent, *Uparent;
275
276 parent = g_strdup(dirname);
277 /* cut trailing slash */
278 parent[strlen(parent) - 1] = '\0';
279 /* make 'parent' have the parent dir path */
280 if ( (p = strrchr(parent, '/')) )
281 *(p + 1) = '\0';
282
283 Uparent = Escape_uri_str(parent, NULL);
284 HUparent = Escape_html_str(Uparent);
285 sock_handler_printf(Client->sh, 0,
286 "<a href='file:%s'>Parent directory</a>", HUparent);
287 g_free(HUparent);
288 g_free(Uparent);
289 g_free(parent);
290 }
291 }
292
293 /*
294 * Given a timestamp, output an HTML-formatted date string.
295 */
296 static void File_print_mtime(ClientInfo *Client, time_t mtime)
297 {
298 char *ds = ctime(&mtime);
299
300 /* Month, day and {hour or year} */
301 if (Client->old_style) {
302 sock_handler_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
303 if (time(NULL) - mtime > 15811200) {
304 sock_handler_printf(Client->sh, 0, " %.4s", ds + 20);
305 } else {
306 sock_handler_printf(Client->sh, 0, " %.5s", ds + 11);
307 }
308 } else {
309 sock_handler_printf(Client->sh, 0,
310 "<td>%.3s&nbsp;%.2s&nbsp;%.5s", ds + 4, ds + 8,
311 /* (more than 6 months old) ? year : hour; */
312 (time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11);
313 }
314 }
315
316 /*
317 * Return a HTML-line from file info.
318 */
319 static void File_info2html(ClientInfo *Client,
320 FileInfo *finfo, const char *dirname, gint n)
321 {
322 gint size;
323 char *sizeunits;
324 char namebuf[MAXNAMESIZE + 1];
325 char *Uref, *HUref, *Hname;
326 const char *ref, *filecont, *name = finfo->filename;
327
328 if (finfo->size <= 9999) {
329 size = finfo->size;
330 sizeunits = "bytes";
331 } else if (finfo->size / 1024 <= 9999) {
332 size = finfo->size / 1024 + (finfo->size % 1024 >= 1024 / 2);
333 sizeunits = "Kb";
334 } else {
335 size = finfo->size / 1048576 + (finfo->size % 1048576 >= 1048576 / 2);
336 sizeunits = "Mb";
337 }
338
339 /* we could note if it's a symlink... */
340 if S_ISDIR (finfo->mode) {
341 filecont = "Directory";
342 } else if (finfo->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
343 filecont = "Executable";
344 } else {
345 filecont = File_content_type(finfo->full_path);
346 if (!filecont || !strcmp(filecont, "application/octet-stream"))
347 filecont = "unknown";
348 }
349
350 ref = name;
351
352 if (strlen(name) > MAXNAMESIZE) {
353 memcpy(namebuf, name, MAXNAMESIZE - 3);
354 strcpy(namebuf + (MAXNAMESIZE - 3), "...");
355 name = namebuf;
356 }
357
358 /* escape problematic filenames */
359 Uref = Escape_uri_str(ref, NULL);
360 HUref = Escape_html_str(Uref);
361 Hname = Escape_html_str(name);
362
363 if (Client->old_style) {
364 char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..";
365 gint ndots = MAXNAMESIZE - strlen(name);
366 sock_handler_printf(Client->sh, 0,
367 "%s<a href='%s'>%s</a>"
368 " %s"
369 " %-11s%4d %-5s",
370 S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
371 dots + 50 - (ndots > 0 ? ndots : 0),
372 filecont, size, sizeunits);
373
374 } else {
375 sock_handler_printf(Client->sh, 0,
376 "<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>"
377 "<td>%s<td>%d&nbsp;%s",
378 (n & 1) ? "bgcolor=#dcdcdc" : "",
379 S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
380 filecont, size, sizeunits);
381 }
382 File_print_mtime(Client, finfo->mtime);
383 sock_handler_printf(Client->sh, 0, "\n");
384
385 g_free(Hname);
386 g_free(HUref);
387 g_free(Uref);
388 }
389
390 /*
391 * Read a local directory and translate it to html.
392 */
393 static void File_transfer_dir(ClientInfo *Client,
394 DilloDir *Ddir, const char *orig_url)
395 {
396 gint n;
397 GList *list;
398 char *d_cmd, *Hdirname, *Udirname, *HUdirname;
399
400 /* Send DPI header */
401 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
402 sock_handler_write_str(Client->sh, d_cmd, 1);
403 g_free(d_cmd);
404
405 /* Send page title */
406 Udirname = Escape_uri_str(Ddir->dirname, NULL);
407 HUdirname = Escape_html_str(Udirname);
408 Hdirname = Escape_html_str(Ddir->dirname);
409
410 sock_handler_printf(Client->sh, 0,
411 "Content-Type: text/html\n\n"
412 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
413 "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
414 " <TITLE>file:%s</TITLE>\n</HEAD>\n"
415 "<BODY><H1>Directory listing of %s</H1>\n",
416 HUdirname, Hdirname, Hdirname);
417 g_free(Hdirname);
418 g_free(HUdirname);
419 g_free(Udirname);
420
421 if (Client->old_style) {
422 sock_handler_printf(Client->sh, 0, "<pre>\n");
423 }
424
425 /* Output the parent directory */
426 File_print_parent_dir(Client, Ddir->dirname);
427
428 /* HTML style toggle */
429 sock_handler_printf(Client->sh, 0,
430 "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%%</a>\n");
431
432 if (Ddir->flist) {
433 if (Client->old_style) {
434 sock_handler_printf(Client->sh, 0, "\n\n");
435 } else {
436 sock_handler_printf(Client->sh, 0,
437 "<br><br>\n"
438 "<table border=0 cellpadding=1 cellspacing=0"
439 " bgcolor=#E0E0E0 width=100%%>\n"
440 "<tr align=center>\n"
441 "<td>\n"
442 "<td width=60%%><b>Filename</b>"
443 "<td><b>Type</b>"
444 "<td><b>Size</b>"
445 "<td><b>Modified&nbsp;at</b>\n");
446 }
447 } else {
448 sock_handler_printf(Client->sh, 0, "<br><br>Directory is empty...");
449 }
450
451 /* Output entries */
452 for (n = 0, list = Ddir->flist; list; list = g_list_next(list)) {
453 File_info2html(Client, list->data, Ddir->dirname, ++n);
454 }
455
456 if (Ddir->flist) {
457 if (Client->old_style) {
458 sock_handler_printf(Client->sh, 0, "</pre>\n");
459 } else {
460 sock_handler_printf(Client->sh, 0, "</table>\n");
461 }
462 }
463
464 sock_handler_printf(Client->sh, 0, "</BODY></HTML>\n");
465 }
466
467 /*
468 * Return a content type based on the extension of the filename.
469 */
470 static const char *File_ext(const char *filename)
471 {
472 char *e;
473
474 if ( !(e = strrchr(filename, '.')) )
475 return NULL;
476
477 e++;
478
479 if (!strcasecmp(e, "gif")) {
480 return "image/gif";
481 } else if (!strcasecmp(e, "jpg") ||
482 !strcasecmp(e, "jpeg")) {
483 return "image/jpeg";
484 } else if (!strcasecmp(e, "png")) {
485 return "image/png";
486 } else if (!strcasecmp(e, "html") ||
487 !strcasecmp(e, "htm") ||
488 !strcasecmp(e, "shtml")) {
489 return "text/html";
490 } else {
491 return NULL;
492 }
493 }
494
495 /*
496 * Based on the extension, return the content_type for the file.
497 * (if there's no extension, analyze the data and try to figure it out)
498 */
499 static const char *File_content_type(const char *filename)
500 {
501 gint fd;
502 struct stat sb;
503 const gchar *ct;
504 gchar buf[256];
505 ssize_t buf_size;
506
507 if (!(ct = File_ext(filename))) {
508 /* everything failed, let's analyze the data... */
509 if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) != -1) {
510 if ((buf_size = read(fd, buf, 256)) == 256 ) {
511 ct = File_get_content_type_from_data(buf, (size_t)buf_size);
512
513 } else if (stat(filename, &sb) != -1 &&
514 buf_size > 0 && buf_size == sb.st_size) {
515 ct = File_get_content_type_from_data(buf, (size_t)buf_size);
516 }
517 File_close(fd);
518 }
519 }
520
521 return ct;
522 }
523
524 /*
525 * Try to stat the file and determine if it's readable.
526 */
527 static void File_get(ClientInfo *Client, const char *filename,
528 const char *orig_url)
529 {
530 int res;
531 struct stat sb;
532 char *msg = NULL, *d_cmd;
533
534 if (stat(filename, &sb) != 0) {
535 /* stat failed, prepare a file-not-found error. */
536 res = FILE_NOT_FOUND;
537 } else if (S_ISDIR(sb.st_mode)) {
538 /* set up for reading directory */
539 res = File_get_dir(Client, filename, orig_url);
540 } else {
541 /* set up for reading a file */
542 res = File_get_file(Client, filename, &sb, orig_url);
543 }
544
545 if (res == FILE_NOT_FOUND) {
546 msg = g_strdup_printf("%s Not Found: %s",
547 S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
548 } else if (res == FILE_NO_ACCESS) {
549 msg = g_strdup_printf("Access denied to %s: %s",
550 S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
551 }
552 if (msg) {
553 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", msg);
554 sock_handler_write_str(Client->sh, d_cmd, 1);
555 g_free(d_cmd);
556 g_free(msg);
557 }
558 }
559
560 /*
561 *
562 */
563 static gint File_get_dir(ClientInfo *Client,
564 const gchar *DirName, const char *orig_url)
565 {
566 GString *g_dirname;
567 DilloDir *Ddir;
568
569 /* Let's make sure this directory url has a trailing slash */
570 g_dirname = g_string_new(DirName);
571 if ( g_dirname->str[g_dirname->len - 1] != '/' )
572 g_string_append(g_dirname, "/");
573
574 /* Let's get a structure ready for transfer */
575 Ddir = File_dillodir_new(g_dirname->str);
576 g_string_free(g_dirname, TRUE);
577 if ( Ddir ) {
578 File_transfer_dir(Client, Ddir, orig_url);
579 File_dillodir_free(Ddir);
580 return FILE_OK;
581 } else
582 return FILE_NO_ACCESS;
583 }
584
585 /*
586 * Send the MIME content/type and then send the file itself.
587 */
588 static gint File_get_file(ClientInfo *Client,
589 const gchar *filename,
590 struct stat *sb,
591 const char *orig_url)
592 {
593 #define LBUF 16*1024
594
595 const gchar *ct;
596 char buf[LBUF], *d_cmd;
597 gint fd, st;
598
599 if ( (fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
600 return FILE_NO_ACCESS;
601
602 /* Content-Type info is based on filename extension. If there's no
603 * known extension, then we do data sniffing. If this doesn't lead
604 * to a conclusion, "application/octet-stream" is sent.
605 */
606 if (!(ct = File_content_type(filename)))
607 ct = "application/octet-stream";
608
609 /* Send DPI command */
610 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
611 sock_handler_write_str(Client->sh, d_cmd, 1);
612 g_free(d_cmd);
613
614 /* Send HTTP stream */
615 sock_handler_printf(Client->sh, 0,
616 "Content-Type: %s\n"
617 "Content-length: %ld\n\n",
618 ct, sb->st_size);
619
620 /* Send raw file contents */
621 do {
622 if ((st = read(fd, buf, LBUF)) > 0) {
623 if (sock_handler_write(Client->sh, buf, (size_t)st, 0) != 0)
624 break;
625 } else if (st < 0) {
626 perror("[read]");
627 if (errno == EINTR || errno == EAGAIN)
628 continue;
629 }
630 } while (st > 0);
631
632 /* todo: It may be better to send an error report to dillo instead of
633 * calling abort from g_error() */
634 if (st == -1)
635 g_error("ERROR while reading from file \"%s\", error was \"%s\"\n",
636 filename, strerror(errno));
637
638 File_close(fd);
639 return FILE_OK;
640 }
641
642 /*
643 * Given an hex octet (e3, 2F, 20), return the corresponding
644 * character if the octet is valid, and -1 otherwise
645 */
646 static int File_parse_hex_octet(const gchar *s)
647 {
648 gint hex_value;
649 gchar *tail, hex[3];
650
651 if ( (hex[0] = s[0]) && (hex[1] = s[1]) ) {
652 hex[2] = 0;
653 hex_value = strtol(hex, &tail, 16);
654 if (tail - hex == 2)
655 return hex_value;
656 }
657
658 return -1;
659 }
660
661 static void File_parse_hex_octets(char **str)
662 {
663 gchar *dest, *tmp, *orig = *str;
664 int i, val;
665
666 if (strchr(orig, '%')) {
667 dest = tmp = g_new(gchar, strlen(orig) + 1);
668
669 for (i = 0; orig[i]; i++) {
670 if (orig[i] == '%' &&
671 (val = File_parse_hex_octet(orig + i + 1)) >= 0) {
672 *dest++ = val;
673 i += 2;
674 } else {
675 *dest++ = orig[i];
676 }
677 }
678 *dest = '\0';
679
680 g_free(orig);
681 *str = tmp;
682 }
683 }
684
685 /*
686 * Make a file URL into a human (and machine) readable path.
687 * The idea is to always have a path that starts with only one slash.
688 * Embedded slashes are ignored.
689 */
690 static char *File_normalize_path(const char *orig)
691 {
692 char *str = (char *) orig, *basename = NULL, *ret, *p;
693
694 /* Make sure the string starts with file: (it should, but...) */
695 if (strncmp(str, "file:", 5) != 0)
696 return NULL;
697
698 str += 5;
699
700 /* Skip slashes */
701 while (str[0] == '/' && str[1] != '\0' && str[1] == '/')
702 str++;
703
704 if (*str == '\0') {
705 /* Give CWD if the string is only "file:" */
706 basename = g_get_current_dir();
707 } else if (*str == '~') {
708 if (str[1] == '\0' || str[1] == '/') {
709 /* Expand 'tilde' to user's home */
710 basename = g_strdup(g_get_home_dir());
711 str++;
712 }
713 } else if (*str == '.') {
714 if (str[1] == '\0' || str[1] == '/') {
715 /* User wants the CWD */
716 basename = g_get_current_dir();
717 str++;
718 } else if (str[1] == '.') {
719 /* One level down from the CWD */
720 char *tmp1 = g_get_current_dir();
721 char *tmp2 = strrchr(tmp1, '/');
722
723 if (tmp2) {
724 basename = g_strndup(tmp1, (guint)(tmp2 - tmp1));
725 }
726 str += 2;
727 g_free(tmp1);
728 }
729 } else if (*str != '/') {
730 return NULL;
731 }
732
733 ret = g_strdup_printf("%s%s%s",
734 basename ? basename : "",
735 basename ? "/" : "",
736 str);
737 g_free(basename);
738
739 /* remove the fragment if present */
740 if ((p = strrchr(ret, '#')) != NULL)
741 *p = 0;
742 /* Parse possible hexadecimal octets in the URI path */
743 File_parse_hex_octets(&ret);
744
745 return ret;
746 }
747
748 /*
749 * Set the style flag and ask for a reload, so it shows inmediatly.
750 */
751 static void File_toggle_html_style(ClientInfo *Client)
752 {
753 char *d_cmd;
754
755 OLD_STYLE = !OLD_STYLE;
756 d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request");
757 sock_handler_write_str(Client->sh, d_cmd, 1);
758 g_free(d_cmd);
759 }
760
761 /*
762 * Perform any necessary cleanups upon abnormal termination
763 */
764 static void termination_handler(int signum)
765 {
766 exit(signum);
767 }
768
769
770 /* Client handling ----------------------------------------------------------*/
771
772 /*
773 * Add a new client to the list.
774 */
775 static ClientInfo *File_add_client(gint sock_fd)
776 {
777 ClientInfo *NewClient;
778
779 NewClient = g_new(ClientInfo, 1);
780 NewClient->sh = sock_handler_new(sock_fd, sock_fd, 8*1024);
781 NewClient->status = 0;
782 NewClient->done = 0;
783 NewClient->old_style = OLD_STYLE;
784 pthread_mutex_lock(&ClMut);
785 Clients = g_list_append(Clients, NewClient);
786 pthread_mutex_unlock(&ClMut);
787 return NewClient;
788 }
789
790 /*
791 * Get client record by number
792 */
793 static void *File_get_client_n(guint n)
794 {
795 void *client;
796
797 pthread_mutex_lock(&ClMut);
798 client = g_list_nth_data(Clients, n);
799 pthread_mutex_unlock(&ClMut);
800
801 return client;
802 }
803
804 /*
805 * Remove a client from the list.
806 */
807 static void File_remove_client_n(guint n)
808 {
809 ClientInfo *Client;
810
811 pthread_mutex_lock(&ClMut);
812 Client = g_list_nth_data(Clients, n);
813 Clients = g_list_remove(Clients, (void *)Client);
814 pthread_mutex_unlock(&ClMut);
815
816 _MSG("Closing Socket Handler\n");
817 sock_handler_close(Client->sh);
818 sock_handler_free(Client->sh);
819 g_free(Client);
820 }
821
822 /*
823 * Return the number of clients.
824 */
825 static gint File_num_clients(void)
826 {
827 guint n;
828
829 pthread_mutex_lock(&ClMut);
830 n = g_list_length(Clients);
831 pthread_mutex_unlock(&ClMut);
832
833 return n;
834 }
835
836 /*
837 * Serve this client.
838 * (this function runs on its own thread)
839 */
840 static void *File_serve_client(void *data)
841 {
842 char *dpip_tag, *cmd = NULL, *url = NULL, *path;
843 ClientInfo *Client = data;
844
845 /* Read the dpi command */
846 dpip_tag = sock_handler_read(Client->sh);
847 MSG("dpip_tag={%s}\n", dpip_tag);
848
849 if (dpip_tag) {
850 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
851 if (cmd) {
852 if (strcmp(cmd, "DpiBye") == 0) {
853 DPIBYE = 1;
854 } else {
855 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
856 if (!url)
857 MSG("file.dpi:: Failed to parse 'url'\n");
858 }
859 }
860 }
861 g_free(cmd);
862 g_free(dpip_tag);
863
864 if (!DPIBYE && url) {
865 _MSG("url = '%s'\n", url);
866
867 path = File_normalize_path(url);
868 if (path) {
869 _MSG("path = '%s'\n", path);
870 File_get(Client, path, url);
871 } else if (strcmp(url, "dpi:/file/toggle") == 0) {
872 File_toggle_html_style(Client);
873 } else {
874 MSG("ERROR: URL path was %s\n", url);
875 }
876 g_free(path);
877 }
878 g_free(url);
879
880 /* flag the the transfer finished */
881 Client->done = 1;
882
883 return NULL;
884 }
885
886 /*
887 * Serve the client queue.
888 * (this function runs on its own thread)
889 */
890 static void *File_serve_clients(void *client)
891 {
892 /* switch to detached state */
893 pthread_detach(pthread_self());
894
895 while (File_num_clients()) {
896 client = File_get_client_n((guint)0);
897 File_serve_client(client);
898 File_remove_client_n((guint)0);
899 }
900 ThreadRunning = 0;
901
902 return NULL;
903 }
904
905 /* --------------------------------------------------------------------------*/
906
907 /*
908 * Check a fd for activity, with a max timeout.
909 * return value: 0 if timeout, 1 if input available, -1 if error.
910 */
911 int File_check_fd(int filedes, unsigned int seconds)
912 {
913 int st;
914 fd_set set;
915 struct timeval timeout;
916
917 /* Initialize the file descriptor set. */
918 FD_ZERO (&set);
919 FD_SET (filedes, &set);
920
921 /* Initialize the timeout data structure. */
922 timeout.tv_sec = seconds;
923 timeout.tv_usec = 0;
924
925 do {
926 st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
927 } while (st == -1 && errno == EINTR);
928
929 return st;
930 }
931
932
933 int main(void)
934 {
935 ClientInfo *NewClient;
936 struct sockaddr_un spun;
937 gint temp_sock_descriptor;
938 gint address_size, c_st, st = 1;
939 guint i;
940
941 /* Arrange the cleanup function for abnormal terminations */
942 if (signal (SIGINT, termination_handler) == SIG_IGN)
943 signal (SIGINT, SIG_IGN);
944 if (signal (SIGHUP, termination_handler) == SIG_IGN)
945 signal (SIGHUP, SIG_IGN);
946 if (signal (SIGTERM, termination_handler) == SIG_IGN)
947 signal (SIGTERM, SIG_IGN);
948
949 MSG("(v.1) accepting connections...\n");
950
951 /* initialize mutex */
952 pthread_mutex_init(&ClMut, NULL);
953
954 /* some OSes may need this... */
955 address_size = sizeof(struct sockaddr_un);
956
957 /* start the service loop */
958 while (!DPIBYE) {
959 /* wait for a connection */
960 do {
961 c_st = File_check_fd(STDIN_FILENO, 1);
962 } while (c_st == 0 && !DPIBYE);
963 if (c_st < 0) {
964 perror("[select]");
965 break;
966 }
967 if (DPIBYE)
968 break;
969
970 temp_sock_descriptor =
971 accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
972
973 if (temp_sock_descriptor == -1) {
974 perror("[accept]");
975 break;
976 }
977
978 /* Create and initialize a new client */
979 NewClient = File_add_client(temp_sock_descriptor);
980
981 if (!ThreadRunning) {
982 ThreadRunning = 1;
983 /* Serve the client from a thread (avoids deadlocks) */
984 if (pthread_create(&NewClient->thrID, NULL,
985 File_serve_clients, NewClient) != 0) {
986 perror("[pthread_create]");
987 ThreadRunning = 0;
988 break;
989 }
990 }
991 }
992
993 /* todo: handle a running thread better. */
994 for (i = 0; i < 5 && ThreadRunning; ++i) {
995 MSG("sleep i=%u", i);
996 sleep(i);
997 }
998
999 if (DPIBYE)
1000 st = 0;
1001 return st;
1002 }
1003
0 /*
1 * Dpi for FTP.
2 *
3 * This server checks the ftp-URL to be a directory (requires wget).
4 * If true, it sends back an html representation of it, and if not
5 * a dpip message (which is catched by dillo who redirects the ftp URL
6 * to the downloads server).
7 *
8 * Feel free to polish!
9 *
10 * Copyright 2003-2005 Jorge Arellano Cid <jcid@dillo.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 */
18
19 /*
20 * TODO:
21 * - Send feedback about the FTP login process from wget's stderr.
22 * i.e. capture our child's stderr, process it, and report back.
23 * - Handle simultaneous connections.
24 * If ftp.dpi is implemented with a low level ftp library, it becomes
25 * possible to keep the connection alive, and thus make browsing of ftp
26 * directories faster (this avoids one login per page, and forks). Perhaps
27 * it's not worth, but can be done.
28 */
29
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <signal.h>
38 #include <sys/wait.h>
39 #include <errno.h>
40 #include <sys/time.h>
41 #include <ctype.h>
42
43 #include <glib.h>
44
45 #include "../dpip/dpip.h"
46 #include "dpiutil.h"
47
48 /*
49 * Debugging macros
50 */
51 #define _MSG(fmt...)
52 #define MSG(fmt...) g_printerr("[ftp dpi]: " fmt)
53
54 /*
55 * Global variables
56 */
57 static SockHandler *sh = NULL;
58 char **dl_argv = NULL;
59
60 /*---------------------------------------------------------------------------*/
61
62 /* TODO: could use dStr ADT! */
63 typedef struct ContentType_ {
64 const char *str;
65 int len;
66 } ContentType_t;
67
68 static const ContentType_t MimeTypes[] = {
69 { "application/octet-stream", 24 },
70 { "text/html", 9 },
71 { "text/plain", 10 },
72 { "image/gif", 9 },
73 { "image/png", 9 },
74 { "image/jpeg", 10 },
75 { NULL, 0 }
76 };
77
78 /*
79 * Detects 'Content-Type' from a data stream sample.
80 *
81 * It uses the magic(5) logic from file(1). Currently, it
82 * only checks the few mime types that Dillo supports.
83 *
84 * 'Data' is a pointer to the first bytes of the raw data.
85 *
86 * Return value: (0 on success, 1 on doubt, 2 on lack of data).
87 */
88 int a_Misc_get_content_type_from_data(void *Data, size_t Size,
89 const char **PT)
90 {
91 int st = 1; /* default to "doubt' */
92 int Type = 0; /* default to "application/octet-stream" */
93 char *p = Data;
94 size_t i, non_ascci;
95
96 /* HTML try */
97 for (i = 0; i < Size && isspace(p[i]); ++i);
98 if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
99 (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
100 (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
101 (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) ||
102 /* this line is workaround for FTP through the Squid proxy */
103 (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) {
104
105 Type = 1;
106 st = 0;
107 /* Images */
108 } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
109 Type = 3;
110 st = 0;
111 } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
112 Type = 4;
113 st = 0;
114 } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
115 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
116 * at the character representation should be machine independent. */
117 Type = 5;
118 st = 0;
119
120 /* Text */
121 } else {
122 /* We'll assume "text/plain" if the set of chars above 127 is <= 10
123 * in a 256-bytes sample. Better heuristics are welcomed! :-) */
124 non_ascci = 0;
125 Size = MIN (Size, 256);
126 for (i = 0; i < Size; i++)
127 if ((unsigned char) p[i] > 127)
128 ++non_ascci;
129 if (Size == 256) {
130 Type = (non_ascci > 10) ? 0 : 2;
131 st = 0;
132 } else {
133 Type = (non_ascci > 0) ? 0 : 2;
134 }
135 }
136
137 *PT = MimeTypes[Type].str;
138 return st;
139 }
140
141 /*---------------------------------------------------------------------------*/
142
143 /*
144 * Build a shell command using wget for this URL.
145 */
146 static void make_wget_argv(gchar *url)
147 {
148 gchar *esc_url;
149
150 if (dl_argv) {
151 g_free(dl_argv[2]);
152 g_free(dl_argv);
153 }
154 dl_argv = g_new(gchar*, 10);
155
156 esc_url = Escape_uri_str(url, "'");
157 /* avoid malicious SMTP relaying with FTP urls */
158 Filter_smtp_hack(esc_url);
159
160 dl_argv[0] = "wget";
161 dl_argv[1] = "-O-";
162 dl_argv[2] = g_strdup(esc_url);
163 dl_argv[3] = NULL;
164 /*
165 dl_argv[0] = "wget";
166 dl_argv[1] = "-t2";
167 dl_argv[2] = "-O-";
168 dl_argv[3] = g_strdup_printf("'%s'", esc_url);
169 dl_argv[4] = "2>/dev/null";
170 dl_argv[5] = NULL;
171 */
172 g_free(esc_url);
173 }
174
175 /*
176 * Fork, exec command, get its output and send via stdout.
177 * Return: Number of bytes transfered.
178 */
179 static gint try_ftp_transfer(gchar *url)
180 {
181 #define MinSZ 256
182
183 ssize_t n;
184 gint nb, minibuf_sz;
185 const gchar *mime_type;
186 char buf[4096], minibuf[MinSZ], *d_cmd;
187 pid_t ch_pid;
188 gint aborted = 0;
189 int DataPipe[2];
190
191 if (pipe(DataPipe) < 0) {
192 MSG("pipe, %s\n", strerror(errno));
193 return 0;
194 }
195
196 /* Prepare args for execvp() */
197 make_wget_argv(url);
198
199 /* Start the child process */
200 if ((ch_pid = fork()) == 0) {
201 /* child */
202 /* start wget */
203 close(DataPipe[0]);
204 dup2(DataPipe[1], 1); /* stdout */
205 execvp(dl_argv[0], dl_argv);
206 _exit(1);
207 } else if (ch_pid < 0) {
208 perror("fork, ");
209 exit(1);
210 } else {
211 /* father continues below */
212 close(DataPipe[1]);
213 }
214
215 /* Read/Write the real data */
216 minibuf_sz = 0;
217 for (nb = 0; 1; nb += n) {
218 while ((n = read(DataPipe[0], buf, 4096)) < 0 && errno == EINTR);
219 if (n <= 0)
220 break;
221
222 if (minibuf_sz < MinSZ) {
223 memcpy(minibuf + minibuf_sz, buf, MIN(n, MinSZ - minibuf_sz));
224 minibuf_sz += MIN(n, MinSZ - minibuf_sz);
225 if (minibuf_sz < MinSZ)
226 continue;
227 a_Misc_get_content_type_from_data(minibuf, minibuf_sz, &mime_type);
228 if (strcmp(mime_type, "application/octet-stream") == 0) {
229 /* abort transfer */
230 kill(ch_pid, SIGTERM);
231 /* The "application/octet-stream" MIME type will be sent and
232 * Dillo will offer a download dialog */
233 aborted = 1;
234 }
235 }
236
237 if (nb == 0) {
238 /* Send dpip tag */
239 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
240 sock_handler_write_str(sh, d_cmd, 1);
241 g_free(d_cmd);
242
243 /* Send HTTP header. */
244 sock_handler_write_str(sh, "Content-type: ", 0);
245 sock_handler_write_str(sh, mime_type, 0);
246 sock_handler_write_str(sh, "\n\n", 1);
247 }
248
249 if (!aborted)
250 sock_handler_write(sh, buf, n, 0);
251 }
252
253 return nb;
254 }
255
256 /*
257 *
258 */
259 int main(void)
260 {
261 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
262 gint nb;
263 gchar *p, *d_cmd;
264
265 /* Initialize the SockHandler */
266 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
267
268 /* wget may need to write a temporary file... */
269 chdir("/tmp");
270
271 /* Read the dpi command from STDIN */
272 dpip_tag = sock_handler_read(sh);
273 g_printerr("ftp.dpi::[%s]\n", dpip_tag);
274
275 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
276 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
277 if (!cmd || !url) {
278 g_printerr("ftp.dpi:: Error, cmd=%s, url=%s\n", cmd, url);
279 exit (EXIT_FAILURE);
280 }
281
282 if ((nb = try_ftp_transfer(url)) == 0) {
283 /* Transfer failed, the requested file may not exist or be a symlink
284 * to a directory. Try again... */
285 if ((p = strrchr(url, '/')) && p[1]) {
286 url2 = g_strconcat(url, "/", NULL);
287 nb = try_ftp_transfer(url2);
288 }
289 }
290
291 if (nb == 0) {
292 /* The transfer failed, let dillo know... */
293 d_cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
294 "answer", "open_url", "not a directory");
295 sock_handler_write_str(sh, d_cmd, 1);
296 g_free(d_cmd);
297 }
298
299 g_free(cmd);
300 g_free(url);
301 g_free(url2);
302 g_free(dpip_tag);
303
304 /* Finish the SockHandler */
305 sock_handler_close(sh);
306 sock_handler_free(sh);
307
308 return 0;
309 }
310
0 /*
1 * Dpi for "Hello World".
2 *
3 * This server is an example. Play with it and modify to your taste.
4 *
5 * Copyright 2003 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 */
13
14 #include <glib.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <errno.h>
21 #include "../dpip/dpip.h"
22 #include "dpiutil.h"
23
24 /*---------------------------------------------------------------------------*/
25
26
27 /*
28 *
29 */
30 int main(void)
31 {
32 FILE *in_stream;
33 SockHandler *sh;
34 gchar *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
35 gchar *esc_tag, *d_cmd;
36 size_t n;
37 gint ret;
38 gchar buf[4096];
39 gchar *choice[] = {"Window was closed", "Yes", "No",
40 "Could be", "It's OK", "Cancel"};
41 /* "Could>be", ">It's OK", "Can'>cel"}; --for testing */
42 gint choice_num;
43
44 g_printerr("hello.dpi:: starting...\n");
45
46 /* Initialize the SockHandler */
47 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
48
49 /* Read the dpi command from STDIN */
50 dpip_tag = sock_handler_read(sh);
51 g_printerr("[%s]\n", dpip_tag);
52
53 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
54 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
55
56 /*-- Dialog part */
57 {
58 gchar *dpip_tag2, *dialog_msg;
59
60 /* Let's confirm the request */
61 /* NOTE: you can send less alternatives (e.g. only alt1 and alt2) */
62 d_cmd = a_Dpip_build_cmd(
63 "cmd=%s msg=%s alt1=%s alt2=%s alt3=%s alt4=%s alt5=%s",
64 "dialog", "Do you want to see the hello page?",
65 choice[1], choice[2], choice[3], choice[4], choice[5]);
66 sock_handler_write_str(sh, d_cmd, 1);
67 g_free(d_cmd);
68
69 /* Get the answer */
70 dpip_tag2 = sock_handler_read(sh);
71 g_printerr("[%s]\n", dpip_tag2);
72
73 /* Get "msg" value */
74 dialog_msg = a_Dpip_get_attr(dpip_tag2, strlen(dpip_tag2), "msg");
75 choice_num = 0;
76 if (dialog_msg)
77 choice_num = *dialog_msg - '0';
78
79 g_free(dialog_msg);
80 g_free(dpip_tag2);
81 }
82 /*-- EOD part */
83
84 /* Start sending our answer */
85 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
86 sock_handler_write_str(sh, d_cmd, 0);
87 g_free(d_cmd);
88
89 sock_handler_printf(sh, 0,
90 "Content-type: text/html\n\n"
91 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
92 "<html>\n"
93 "<head><title>Simple dpi test page (hello.dpi)</title></head>\n"
94 "<body><hr><h1>Hello world!</h1><hr>\n<br><br>\n");
95
96 /* Show the choice received with the dialog */
97 sock_handler_printf(sh, 0,
98 "<hr>\n"
99 "<table width='100%%' border='1' bgcolor='burlywood'><tr><td>\n"
100 "<big><em>Dialog question:</em> Do you want to see the hello page?<br>\n"
101 "<em>Answer received:</em> <b>%s</b></big> </table>\n"
102 "<hr>\n",
103 choice[choice_num]);
104
105 /* Show the dpip tag we received */
106 esc_tag = Escape_html_str(dpip_tag);
107 sock_handler_printf(sh, 0,
108 "<h3>dpip tag received:</h3>\n"
109 "<pre>\n%s</pre>\n"
110 "<br><small>(<b>dpip:</b> dpi protocol)</small><br><br><br>\n",
111 esc_tag);
112 g_free(esc_tag);
113
114
115 /* Now something more interesting,
116 * fork a command and show its feedback */
117 if (cmd && url) {
118 child_cmd = g_strdup("date -R");
119 g_printerr("[%s]\n", child_cmd);
120
121 /* Fork, exec command, get its output and answer */
122 if ((in_stream = popen(child_cmd, "r")) == NULL) {
123 perror("popen");
124 return EXIT_FAILURE;
125 }
126
127 sock_handler_printf(sh, 0, "<h3>date:</h3>\n");
128 sock_handler_printf(sh, 0, "<pre>\n");
129
130 /* Read/Write */
131 while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
132 sock_handler_write(sh, buf, n, 0);
133 }
134
135 sock_handler_printf(sh, 0, "</pre>\n");
136
137 if ((ret = pclose(in_stream)) != 0)
138 g_printerr("popen: [%d]\n", ret);
139
140 g_free(child_cmd);
141 }
142
143 sock_handler_printf(sh, 1, "</body></html>\n");
144
145 g_free(cmd);
146 g_free(url);
147 g_free(dpip_tag);
148
149 /* Finish the SockHandler */
150 sock_handler_close(sh);
151 sock_handler_free(sh);
152
153 return 0;
154 }
155
0 /*
1 * Dpi for HTTPS.
2 *
3 *
4 *
5 * W A R N I N G
6 *
7 * One of the important things to have in mind is about whether
8 * unix domain sockets (UDS) are secure for https. I mean, root can always
9 * snoop on sockets (regardless of permissions) so he'd be able to "see" all
10 * the traffic. OTOH, if someone has root access on a machine he can do
11 * anything, and that includes modifying the binaries, peeking-up in
12 * memory space, installing a key-grabber, ...
13 *
14 *
15 * Copyright 2003, 2004 Jorge Arellano Cid <jcid@dillo.org>
16 * Copyright 2004 Garrett Kajmowicz <gkajmowi@tbaytel.net>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * As a special exception permission is granted to link the code of
24 * the https dillo plugin with the OpenSSL project's "OpenSSL"
25 * library, and distribute the linked executables, without including
26 * the source code for OpenSSL in the source distribution. You must
27 * obey the GNU General Public License, version 2, in all respects
28 * for all of the code used other than "OpenSSL".
29 *
30 */
31
32 /*
33 * TODO: a lot of things, this is just a bare bones example.
34 *
35 * For instance:
36 * - Handle cookies (now that they arrive with the dpip tag, it needs
37 * testing).
38 * - Certificate authentication (asking the user in case it can't be verified)
39 * - Certificate management.
40 * - Session caching ...
41 *
42 */
43
44 #include <config.h>
45 #include <unistd.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <netdb.h>
50 #include <sys/un.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <signal.h>
55 #include <sys/wait.h>
56 #include <errno.h>
57 #include <sys/time.h>
58 #include <sys/stat.h>
59 #include <glib.h>
60 #include "../dpip/dpip.h"
61 #include "dpiutil.h"
62
63 #define ENABLE_SSL
64 #undef ENABLE_SSL
65 #ifdef ENABLE_SSL
66
67 #include <openssl/ssl.h>
68 #include <openssl/rand.h>
69
70 static int get_network_connection(gchar * url);
71 static int handle_certificate_problem(SSL * ssl_connection);
72 static int save_certificate_home(X509 * cert);
73
74 #endif
75
76 int main(void);
77
78 /*---------------------------------------------------------------------------*/
79 /*
80 * Global variables
81 */
82 static gchar *root_url = NULL; /*Holds the URL we are connecting to*/
83 static SockHandler *sh;
84
85
86 #ifdef ENABLE_SSL
87
88 /*
89 * Read the answer dpip tag for a dialog and return the number for
90 * the user-selected alternative.
91 * Return: (-1: parse error, 0: window closed, 1-5 alt. number)
92 */
93 static gint dialog_get_answer_number(void)
94 {
95 gint response_number = -1;
96 gchar *dpip_tag, *response;
97
98 /* Read the dpi command from STDIN */
99 dpip_tag = sock_handler_read(sh);
100 response = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "msg");
101 response_number = (response) ? strtol (response, NULL, 10) : -1;
102 g_free(dpip_tag);
103 g_free(response);
104
105 return response_number;
106 }
107
108
109 /*
110 * This function does all of the work with SSL
111 */
112 static void yes_ssl_support(void)
113 {
114 /* The following variable will be set to 1 in the event of
115 * an error and skip any further processing
116 */
117 gint exit_error = 0;
118 SSL_CTX * ssl_context = NULL;
119 SSL * ssl_connection = NULL;
120
121 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
122 gchar buf[4096];
123 int retval = 0;
124 int network_socket = -1;
125
126
127 g_printerr("{In https.filter.dpi}\n");
128
129 /*Initialize library*/
130 SSL_load_error_strings();
131 SSL_library_init();
132 if (RAND_status() != 1){
133 /*Insufficient entropy. Deal with it?*/
134 g_printerr("Insufficient random entropy\n");
135 }
136
137 /*Create context and SSL object*/
138 if (exit_error == 0){
139 ssl_context = SSL_CTX_new(SSLv23_client_method());
140 if (ssl_context == NULL){
141 g_printerr("Error creating SSL context\n");
142 exit_error = 1;
143 }
144 }
145
146 /*Set directory to load certificates from*/
147 /*FIXME - provide for sysconfdir variables and such*/
148 if (exit_error == 0){
149 if (SSL_CTX_load_verify_locations(
150 ssl_context, NULL, "/etc/ssl/certs/" ) == 0){
151 g_printerr("Error opening system x509 certificate location\n");
152 exit_error = 1;
153 }
154 }
155
156 if (exit_error == 0){
157 g_snprintf(buf, 4095, "%s/.dillo/certs/", g_get_home_dir() );
158 if (SSL_CTX_load_verify_locations(ssl_context, NULL, buf )==0){
159 g_printerr("Error opening user x509 certificate location\n");
160 exit_error = 1;
161 }
162 }
163
164 if (exit_error == 0){
165 ssl_connection = SSL_new(ssl_context);
166 if (ssl_connection == NULL){
167 g_printerr("Error creating SSL connection\n");
168 exit_error = 1;
169 }
170 }
171
172 if (exit_error == 0){
173 /* Need to do the following if we want to deal with all
174 * possible ciphers
175 */
176 SSL_set_cipher_list(ssl_connection, "ALL");
177
178 /* Need to do this if we want to have the option of dealing
179 * with self-signed certs
180 */
181 SSL_set_verify(ssl_connection, SSL_VERIFY_NONE, 0);
182
183 /*Get the network address and command to be used*/
184 dpip_tag = sock_handler_read(sh);
185 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
186 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
187 http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
188
189 if (cmd == NULL || url == NULL || http_query == NULL){
190 g_printerr("***Value of cmd, url or http_query is NULL"
191 " - cannot continue\n");
192 exit_error = 1;
193 }
194 }
195
196 if (exit_error == 0){
197 network_socket = get_network_connection(url);
198 if (network_socket<0){
199 g_printerr("Network socket create error\n");
200 exit_error = 1;
201 }
202 }
203
204
205 if (exit_error == 0){
206 /* Configure SSL to use network file descriptor */
207 if (SSL_set_fd(ssl_connection, network_socket) == 0){
208 g_printerr("Error connecting network socket to SSL\n");
209 exit_error = 1;
210 }
211 }
212
213 if (exit_error == 0){
214 /*Actually do SSL connection handshake*/
215 if (SSL_connect(ssl_connection) != 1){
216 g_printerr("SSL_connect failed\n");
217 exit_error = 1;
218 }
219 }
220
221 /*Use handle error function to decide what to do*/
222 if (exit_error == 0){
223 if (handle_certificate_problem(ssl_connection) < 0){
224 g_printerr("Certificate verification error\n");
225 exit_error = 1;
226 }
227 }
228
229 if (exit_error == 0) {
230 char *d_cmd;
231
232 /*Send query we want*/
233 SSL_write(ssl_connection, http_query, (int)strlen(http_query));
234
235 /*Analyse response from server*/
236
237 /*Send dpi command*/
238 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
239 sock_handler_write_str(sh, d_cmd, 1);
240 g_free(d_cmd);
241
242 /*Send remaining data*/
243
244 while ((retval = SSL_read(ssl_connection, buf, 4096)) > 0 ){
245 sock_handler_write(sh, buf, (size_t)retval, 0);
246 }
247 }
248
249 /*Begin cleanup of all resources used*/
250 g_free(dpip_tag);
251 g_free(cmd);
252 g_free(url);
253 g_free(http_query);
254
255 if (network_socket != -1){
256 close(network_socket);
257 network_socket = -1;
258 }
259 if (ssl_connection != NULL){
260 SSL_free(ssl_connection);
261 ssl_connection = NULL;
262 }
263 if (ssl_context != NULL){
264 SSL_CTX_free(ssl_context);
265 ssl_context = NULL;
266 }
267 }
268
269 /*
270 * The following function attempts to open up a connection to the
271 * remote server and return the file descriptor number of the
272 * socket. Returns -1 in the event of an error
273 */
274 static int get_network_connection(gchar * url)
275 {
276 struct sockaddr_in address;
277 struct hostent *hp;
278
279 int s;
280 int url_offset = 0;
281 int portnum = 443;
282 unsigned int url_look_up_length = 0;
283 gchar * url_look_up = NULL;
284
285 /*Determine how much of url we chop off as unneeded*/
286 if (g_strncasecmp(url, "https://", 8) == 0){
287 url_offset = 8;
288 }
289
290 /*Find end of URL*/
291
292 if (strpbrk(url+url_offset, ":/") != NULL){
293 url_look_up_length = strpbrk(url+url_offset, ":/") - (url+url_offset);
294 url_look_up = g_strndup(url+url_offset, url_look_up_length);
295
296 /*Check for port number*/
297 if (strchr(url+url_offset, ':') ==
298 (url + url_offset + url_look_up_length)){
299 portnum = atoi(url + url_offset + url_look_up_length + 1);
300 }
301 } else {
302 url_look_up = url + url_offset;
303 }
304
305 root_url = g_strdup(url_look_up);
306 hp=gethostbyname(url_look_up);
307
308 /*url_look_uip no longer needed, so free if neccessary*/
309 if (url_look_up_length != 0){
310 g_free(url_look_up);
311 }
312
313 if (hp == NULL){
314 g_printerr("gethostbyname() failed\n");
315 return -1;
316 }
317
318 memset(&address,0,sizeof(address));
319 memcpy((char *)&address.sin_addr, hp->h_addr, (size_t)hp->h_length);
320 address.sin_family=hp->h_addrtype;
321 address.sin_port= htons((u_short)portnum);
322
323 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
324 if (connect(s, (struct sockaddr *)&address, sizeof(address)) != 0){
325 close(s);
326 s = -1;
327 g_printerr("errno: %i\n", errno);
328 }
329 return s;
330 }
331
332
333 /* This function is run only when the certificate cannot
334 * be completely trusted. This will notify the user and
335 * allow the user to decide what to do. It may save the
336 * certificate to the user's .dillo directory if it is
337 * trusted.
338 * Return value: -1 on abort, 0 or higher on continue
339 */
340 static int handle_certificate_problem(SSL * ssl_connection)
341 {
342 gint response_number;
343 int retval = -1;
344 long st;
345 char *cn, *cn_end;
346 char buf[4096], *d_cmd, *msg;
347
348 X509 * remote_cert;
349
350 remote_cert = SSL_get_peer_certificate(ssl_connection);
351 if (remote_cert == NULL){
352 /*Inform user that remote system cannot be trusted*/
353 d_cmd = a_Dpip_build_cmd(
354 "cmd=%s msg=%s alt1=%s alt2=%s",
355 "dialog",
356 "The remote system is NOT presenting a certificate.\n"
357 "This site CAN NOT be trusted. Sending data is NOT SAFE.\n"
358 "What do I do?",
359 "Continue", "Cancel");
360 sock_handler_write_str(sh, d_cmd, 1);
361 g_free(d_cmd);
362
363 /*Read the user's response*/
364 response_number = dialog_get_answer_number();
365
366 /*Abort on anything but "Continue"*/
367 if (response_number == 1){
368 retval = 0;
369 }
370
371 } else {
372 /*Figure out if (and why) the remote system can't be trusted*/
373 st = SSL_get_verify_result(ssl_connection);
374 switch (st) {
375 case X509_V_OK: /*Everything is Kosher*/
376 retval = 0;
377 break;
378 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
379 /*Either self signed and untrusted*/
380 /*Extract CN from certificate name information*/
381 cn = strstr(remote_cert->name, "/CN=") + 4;
382 if (cn == NULL)
383 break;
384
385 if ((cn_end = strstr(cn, "/")) == NULL )
386 cn_end = cn + strlen(cn);
387
388 strncpy(buf, cn, (size_t) (cn_end - cn));
389
390 /*Add terminating NULL*/
391 buf[cn_end - cn] = 0;
392
393 msg = g_strdup_printf("The remote certificate is self-signed and "
394 "untrusted.\nFor address: %s", buf);
395 d_cmd = a_Dpip_build_cmd(
396 "cmd=%s msg=%s alt1=%s alt2=%s alt3=%s",
397 "dialog", msg, "Continue", "Cancel", "Trust Certificate");
398 sock_handler_write_str(sh, d_cmd, 1);
399 g_free(d_cmd);
400 g_free(msg);
401
402 response_number = dialog_get_answer_number();
403 switch (response_number){
404 case 1:
405 retval = 0;
406 break;
407 case 2:
408 break;
409 case 3:
410 /*Save certificate to a file here and recheck the chain*/
411 /*Potential security problems because we are writing
412 *to the filesystem*/
413 save_certificate_home(remote_cert);
414 retval = 1;
415 break;
416 default:
417 break;
418 }
419 break;
420 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
421 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
422 d_cmd = a_Dpip_build_cmd(
423 "cmd=%s msg=%s alt1=%s alt2=%s",
424 "dialog",
425 "The issuer for the remote certificate cannot be found\n"
426 "The authenticity of the remote certificate cannot be trusted",
427 "Continue", "Cancel");
428 sock_handler_write_str(sh, d_cmd, 1);
429 g_free(d_cmd);
430
431 response_number = dialog_get_answer_number();
432 if (response_number == 1) {
433 retval = 0;
434 }
435 break;
436
437 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
438 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
439 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
440 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
441 d_cmd = a_Dpip_build_cmd(
442 "cmd=%s msg=%s alt1=%s alt2=%s",
443 "dialog",
444 "The remote certificate signature could not be read\n"
445 "or is invalid and should not be trusted",
446 "Continue", "Cancel");
447 sock_handler_write_str(sh, d_cmd, 1);
448 g_free(d_cmd);
449
450 response_number = dialog_get_answer_number();
451 if (response_number == 1) {
452 retval = 0;
453 }
454 break;
455 case X509_V_ERR_CERT_NOT_YET_VALID:
456 case X509_V_ERR_CRL_NOT_YET_VALID:
457 d_cmd = a_Dpip_build_cmd(
458 "cmd=%s msg=%s alt1=%s alt2=%s",
459 "dialog",
460 "Part of the remote certificate is not yet valid\n"
461 "Certificates usually have a range of dates over which\n"
462 "they are to be considered valid, and the certificate\n"
463 "presented has a starting validity after today's date\n"
464 "You should be cautious about using this site",
465 "Continue", "Cancel");
466 sock_handler_write_str(sh, d_cmd, 1);
467 g_free(d_cmd);
468
469 response_number = dialog_get_answer_number();
470 if (response_number == 1) {
471 retval = 0;
472 }
473 break;
474 case X509_V_ERR_CERT_HAS_EXPIRED:
475 case X509_V_ERR_CRL_HAS_EXPIRED:
476 d_cmd = a_Dpip_build_cmd(
477 "cmd=%s msg=%s alt1=%s alt2=%s",
478 "dialog",
479 "The remote certificate has expired. The certificate\n"
480 "wasn't designed to last this long. You should avoid \n"
481 "this site.",
482 "Continue", "Cancel");
483 sock_handler_write_str(sh, d_cmd, 1);
484 g_free(d_cmd);
485 response_number = dialog_get_answer_number();
486 if (response_number == 1) {
487 retval = 0;
488 }
489 break;
490 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
491 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
492 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
493 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
494 d_cmd = a_Dpip_build_cmd(
495 "cmd=%s msg=%s alt1=%s alt2=%s",
496 "dialog",
497 "There was an error in the certificate presented.\n"
498 "Some of the certificate data was improperly formatted\n"
499 "making it impossible to determine if the certificate\n"
500 "is valid. You should not trust this certificate.",
501 "Continue", "Cancel");
502 sock_handler_write_str(sh, d_cmd, 1);
503 g_free(d_cmd);
504 response_number = dialog_get_answer_number();
505 if (response_number == 1) {
506 retval = 0;
507 }
508 break;
509 case X509_V_ERR_INVALID_CA:
510 case X509_V_ERR_INVALID_PURPOSE:
511 case X509_V_ERR_CERT_UNTRUSTED:
512 case X509_V_ERR_CERT_REJECTED:
513 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
514 d_cmd = a_Dpip_build_cmd(
515 "cmd=%s msg=%s alt1=%s alt2=%s",
516 "dialog",
517 "One of the certificates in the chain is being used\n"
518 "incorrectly (possibly due to configuration problems\n"
519 "with the remote system. The connection should not\n"
520 "be trusted",
521 "Continue", "Cancel");
522 sock_handler_write_str(sh, d_cmd, 1);
523 g_free(d_cmd);
524 response_number = dialog_get_answer_number();
525 if (response_number == 1) {
526 retval = 0;
527 }
528 break;
529 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
530 case X509_V_ERR_AKID_SKID_MISMATCH:
531 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
532 d_cmd = a_Dpip_build_cmd(
533 "cmd=%s msg=%s alt1=%s alt2=%s",
534 "dialog",
535 "Some of the information presented by the remote system\n"
536 "does not match other information presented\n"
537 "This may be an attempt to evesdrop on communications",
538 "Continue", "Cancel");
539 sock_handler_write_str(sh, d_cmd, 1);
540 g_free(d_cmd);
541 default: /*Need to add more options later*/
542 msg = g_strdup_printf(
543 "The remote certificate cannot be verified (code %ld)", st);
544 d_cmd = a_Dpip_build_cmd(
545 "cmd=%s msg=%s alt1=%s alt2=%s",
546 "dialog", msg, "Continue", "Cancel");
547 sock_handler_write_str(sh, d_cmd, 1);
548 g_free(d_cmd);
549 g_free(msg);
550 response_number = dialog_get_answer_number();
551 /*abort on anything but "Continue"*/
552 if (response_number == 1){
553 retval = 0;
554 }
555 }
556 X509_free(remote_cert);
557 remote_cert = 0;
558 }
559
560 return retval;
561 }
562
563 /*
564 * Save certificate with a hashed filename.
565 * Return: 0 on success, 1 on failure.
566 */
567 static int save_certificate_home(X509 * cert)
568 {
569 char buf[4096];
570
571 FILE * fp = NULL;
572 unsigned int i = 0;
573 int retval = 1;
574
575 /*Attempt to create .dillo/certs blindly - check later*/
576 g_snprintf(buf,4096,"%s/.dillo/", g_get_home_dir());
577 mkdir(buf, 01777);
578 g_snprintf(buf,4096,"%s/.dillo/certs/", g_get_home_dir());
579 mkdir(buf, 01777);
580
581 do{
582 g_snprintf(buf,4096,"%s/.dillo/certs/%lx.%u",
583 g_get_home_dir(), X509_subject_name_hash(cert), i);
584
585 fp=fopen(buf, "r");
586 if (fp == NULL){
587 /*File name doesn't exist so we can use it safely*/
588 fp=fopen(buf, "w");
589 if (fp == NULL){
590 g_printerr("Unable to open cert save file in home dir\n");
591 break;
592 } else {
593 PEM_write_X509(fp, cert);
594 fclose(fp);
595 g_printerr("Wrote certificate\n");
596 retval = 0;
597 break;
598 }
599 } else {
600 fclose(fp);
601 }
602 i++;
603 /*Don't loop too many times - just give up*/
604 } while( i < 1024 );
605
606 return retval;
607 }
608
609
610
611 #else
612
613
614 /*
615 * Call this function to display an error message if SSL support
616 * isn't available for some reason
617 */
618 static void no_ssl_support(void)
619 {
620 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
621 char *d_cmd;
622
623 /* Read the dpi command from STDIN */
624 dpip_tag = sock_handler_read(sh);
625
626 g_printerr("{In https.filter.dpi}\n");
627 g_printerr("no_ssl_support version\n");
628
629 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
630 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
631 http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
632
633 g_printerr("{ cmd: %s}\n", cmd);
634 g_printerr("{ url: %s}\n", url);
635 g_printerr("{ http_query:\n%s}\n", http_query);
636
637 g_printerr("{ sending dpip cmd...}\n");
638
639 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
640 sock_handler_write_str(sh, d_cmd, 1);
641 g_free(d_cmd);
642
643 g_printerr("{ dpip cmd sent.}\n");
644
645 g_printerr("{ sending HTML...}\n");
646
647 sock_handler_printf(sh, 1,
648 "Content-type: text/html\n\n"
649 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
650 "<html><body><pre>\n"
651 "<b>Hi!\n\n"
652 " This is the https dpi that just got a request to send\n"
653 " the following HTTP query:\n{</b>\n"
654 "<code>%s</code>\n"
655 "<b>}</b>\n\n"
656 " <b>*** Dillo's prototype plugin for https support"
657 " is disabled now ***</b>\n\n"
658 " If you want to test this <b>alpha</b> support code, just remove\n"
659 " line 65 from https.c, recompile and reinstall.\n\n"
660 " (beware that this https support is very limited now)\n\n"
661 " To use https and SSL, you must have \n"
662 " the OpenSSL development libraries installed. Check your\n"
663 " O/S distribution provider, or check out\n"
664 " <a href=\"http://www.openssl.org\">www.openssl.org</a>\n\n"
665 " --\n"
666 "</pre></body></html>\n",
667 http_query
668 );
669 g_printerr("{ HTML content sent.}\n");
670
671 g_free(cmd);
672 g_free(url);
673 g_free(http_query);
674 g_free(dpip_tag);
675
676 g_printerr("{ exiting https.dpi}\n");
677
678 }
679
680 #endif
681
682
683 /*---------------------------------------------------------------------------*/
684 int main(void)
685 {
686 /* Initialize the SockHandler for this filter dpi */
687 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
688
689 #ifdef ENABLE_SSL
690 yes_ssl_support();
691 #else
692 no_ssl_support();
693 #endif
694
695 /* Finish the SockHandler */
696 sock_handler_close(sh);
697 sock_handler_free(sh);
698
699 g_free(root_url);
700
701 g_printerr("{ exiting https.dpi}\n");
702
703 return 0;
704 }
705
0 AM_CPPFLAGS=-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
1 AM_CFLAGS = @GLIB_CFLAGS@
2
3 bin_PROGRAMS = dpid
4 dpid_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
5
6 EXTRA_DIST = dpidc
7 bin_SCRIPTS = dpidc
8
9 dpid_SOURCES = \
10 dpi.h \
11 dpi_service.h \
12 dpi_socket_dir.h \
13 dpid.h \
14 dpid_common.h \
15 misc_new.h \
16 dpi.c \
17 dpi_service.c \
18 dpi_socket_dir.c \
19 dpid.c \
20 dpid_common.c \
21 main.c \
22 misc_new.c
23
24 install-data-local :
25 $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
26 echo dpi_dir=$(libdir)/dillo/dpi > $(DESTDIR)$(sysconfdir)/dpidrc
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16
17 SOURCES = $(dpid_SOURCES)
18
19 srcdir = @srcdir@
20 top_srcdir = @top_srcdir@
21 VPATH = @srcdir@
22 pkgdatadir = $(datadir)/@PACKAGE@
23 pkglibdir = $(libdir)/@PACKAGE@
24 pkgincludedir = $(includedir)/@PACKAGE@
25 top_builddir = ..
26 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
27 INSTALL = @INSTALL@
28 install_sh_DATA = $(install_sh) -c -m 644
29 install_sh_PROGRAM = $(install_sh) -c
30 install_sh_SCRIPT = $(install_sh) -c
31 INSTALL_HEADER = $(INSTALL_DATA)
32 transform = $(program_transform_name)
33 NORMAL_INSTALL = :
34 PRE_INSTALL = :
35 POST_INSTALL = :
36 NORMAL_UNINSTALL = :
37 PRE_UNINSTALL = :
38 POST_UNINSTALL = :
39 build_triplet = @build@
40 host_triplet = @host@
41 target_triplet = @target@
42 bin_PROGRAMS = dpid$(EXEEXT)
43 subdir = dpid
44 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in TODO
45 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
46 am__aclocal_m4_deps = $(top_srcdir)/configure.in
47 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
48 $(ACLOCAL_M4)
49 mkinstalldirs = $(install_sh) -d
50 CONFIG_HEADER = $(top_builddir)/config.h
51 CONFIG_CLEAN_FILES =
52 am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"
53 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
54 PROGRAMS = $(bin_PROGRAMS)
55 am_dpid_OBJECTS = dpi.$(OBJEXT) dpi_service.$(OBJEXT) \
56 dpi_socket_dir.$(OBJEXT) dpid.$(OBJEXT) dpid_common.$(OBJEXT) \
57 main.$(OBJEXT) misc_new.$(OBJEXT)
58 dpid_OBJECTS = $(am_dpid_OBJECTS)
59 dpid_DEPENDENCIES = ../dpip/libDpip.a
60 binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
61 SCRIPTS = $(bin_SCRIPTS)
62 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
63 depcomp = $(SHELL) $(top_srcdir)/depcomp
64 am__depfiles_maybe = depfiles
65 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
66 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
67 CCLD = $(CC)
68 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
69 SOURCES = $(dpid_SOURCES)
70 DIST_SOURCES = $(dpid_SOURCES)
71 ETAGS = etags
72 CTAGS = ctags
73 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
74 ACLOCAL = @ACLOCAL@
75 AMDEP_FALSE = @AMDEP_FALSE@
76 AMDEP_TRUE = @AMDEP_TRUE@
77 AMTAR = @AMTAR@
78 AUTOCONF = @AUTOCONF@
79 AUTOHEADER = @AUTOHEADER@
80 AUTOMAKE = @AUTOMAKE@
81 AWK = @AWK@
82 CC = @CC@
83 CCDEPMODE = @CCDEPMODE@
84 CFLAGS = @CFLAGS@
85 CPP = @CPP@
86 CPPFLAGS = @CPPFLAGS@
87 CXX = @CXX@
88 CXXDEPMODE = @CXXDEPMODE@
89 CXXFLAGS = @CXXFLAGS@
90 CYGPATH_W = @CYGPATH_W@
91 DEFS = @DEFS@
92 DEPDIR = @DEPDIR@
93 DLGUI_FALSE = @DLGUI_FALSE@
94 DLGUI_TRUE = @DLGUI_TRUE@
95 ECHO_C = @ECHO_C@
96 ECHO_N = @ECHO_N@
97 ECHO_T = @ECHO_T@
98 EGREP = @EGREP@
99 EXEEXT = @EXEEXT@
100 GLIB_CFLAGS = @GLIB_CFLAGS@
101 GLIB_CONFIG = @GLIB_CONFIG@
102 GLIB_LIBS = @GLIB_LIBS@
103 GTK_CFLAGS = @GTK_CFLAGS@
104 GTK_CONFIG = @GTK_CONFIG@
105 GTK_LIBS = @GTK_LIBS@
106 INSTALL_DATA = @INSTALL_DATA@
107 INSTALL_PROGRAM = @INSTALL_PROGRAM@
108 INSTALL_SCRIPT = @INSTALL_SCRIPT@
109 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
110 LDFLAGS = @LDFLAGS@
111 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
112 LIBFLTK_LIBS = @LIBFLTK_LIBS@
113 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
114 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
115 LIBJPEG_LIBS = @LIBJPEG_LIBS@
116 LIBOBJS = @LIBOBJS@
117 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
118 LIBPNG_LIBS = @LIBPNG_LIBS@
119 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
120 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
121 LIBS = @LIBS@
122 LIBSSL_LIBS = @LIBSSL_LIBS@
123 LIBZ_LIBS = @LIBZ_LIBS@
124 LTLIBOBJS = @LTLIBOBJS@
125 MAKEINFO = @MAKEINFO@
126 OBJEXT = @OBJEXT@
127 PACKAGE = @PACKAGE@
128 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
129 PACKAGE_NAME = @PACKAGE_NAME@
130 PACKAGE_STRING = @PACKAGE_STRING@
131 PACKAGE_TARNAME = @PACKAGE_TARNAME@
132 PACKAGE_VERSION = @PACKAGE_VERSION@
133 PATH_SEPARATOR = @PATH_SEPARATOR@
134 RANLIB = @RANLIB@
135 SET_MAKE = @SET_MAKE@
136 SHELL = @SHELL@
137 STRIP = @STRIP@
138 VERSION = @VERSION@
139 ac_ct_CC = @ac_ct_CC@
140 ac_ct_CXX = @ac_ct_CXX@
141 ac_ct_RANLIB = @ac_ct_RANLIB@
142 ac_ct_STRIP = @ac_ct_STRIP@
143 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
144 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
145 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
146 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
147 am__include = @am__include@
148 am__leading_dot = @am__leading_dot@
149 am__quote = @am__quote@
150 am__tar = @am__tar@
151 am__untar = @am__untar@
152 bindir = @bindir@
153 build = @build@
154 build_alias = @build_alias@
155 build_cpu = @build_cpu@
156 build_os = @build_os@
157 build_vendor = @build_vendor@
158 datadir = @datadir@
159 exec_prefix = @exec_prefix@
160 host = @host@
161 host_alias = @host_alias@
162 host_cpu = @host_cpu@
163 host_os = @host_os@
164 host_vendor = @host_vendor@
165 includedir = @includedir@
166 infodir = @infodir@
167 install_sh = @install_sh@
168 libdir = @libdir@
169 libexecdir = @libexecdir@
170 localstatedir = @localstatedir@
171 mandir = @mandir@
172 mkdir_p = @mkdir_p@
173 oldincludedir = @oldincludedir@
174 prefix = @prefix@
175 program_transform_name = @program_transform_name@
176 sbindir = @sbindir@
177 sharedstatedir = @sharedstatedir@
178 sysconfdir = @sysconfdir@
179 target = @target@
180 target_alias = @target_alias@
181 target_cpu = @target_cpu@
182 target_os = @target_os@
183 target_vendor = @target_vendor@
184 AM_CPPFLAGS = -DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
185 AM_CFLAGS = @GLIB_CFLAGS@
186 dpid_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
187 EXTRA_DIST = dpidc
188 bin_SCRIPTS = dpidc
189 dpid_SOURCES = \
190 dpi.h \
191 dpi_service.h \
192 dpi_socket_dir.h \
193 dpid.h \
194 dpid_common.h \
195 misc_new.h \
196 dpi.c \
197 dpi_service.c \
198 dpi_socket_dir.c \
199 dpid.c \
200 dpid_common.c \
201 main.c \
202 misc_new.c
203
204 all: all-am
205
206 .SUFFIXES:
207 .SUFFIXES: .c .o .obj
208 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
209 @for dep in $?; do \
210 case '$(am__configure_deps)' in \
211 *$$dep*) \
212 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
213 && exit 0; \
214 exit 1;; \
215 esac; \
216 done; \
217 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dpid/Makefile'; \
218 cd $(top_srcdir) && \
219 $(AUTOMAKE) --gnu dpid/Makefile
220 .PRECIOUS: Makefile
221 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
222 @case '$?' in \
223 *config.status*) \
224 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
225 *) \
226 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
227 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
228 esac;
229
230 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
231 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
232
233 $(top_srcdir)/configure: $(am__configure_deps)
234 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
235 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
236 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
237 install-binPROGRAMS: $(bin_PROGRAMS)
238 @$(NORMAL_INSTALL)
239 test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
240 @list='$(bin_PROGRAMS)'; for p in $$list; do \
241 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
242 if test -f $$p \
243 ; then \
244 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
245 echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
246 $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
247 else :; fi; \
248 done
249
250 uninstall-binPROGRAMS:
251 @$(NORMAL_UNINSTALL)
252 @list='$(bin_PROGRAMS)'; for p in $$list; do \
253 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
254 echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
255 rm -f "$(DESTDIR)$(bindir)/$$f"; \
256 done
257
258 clean-binPROGRAMS:
259 -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
260 dpid$(EXEEXT): $(dpid_OBJECTS) $(dpid_DEPENDENCIES)
261 @rm -f dpid$(EXEEXT)
262 $(LINK) $(dpid_LDFLAGS) $(dpid_OBJECTS) $(dpid_LDADD) $(LIBS)
263 install-binSCRIPTS: $(bin_SCRIPTS)
264 @$(NORMAL_INSTALL)
265 test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
266 @list='$(bin_SCRIPTS)'; for p in $$list; do \
267 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
268 if test -f $$d$$p; then \
269 f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
270 echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
271 $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
272 else :; fi; \
273 done
274
275 uninstall-binSCRIPTS:
276 @$(NORMAL_UNINSTALL)
277 @list='$(bin_SCRIPTS)'; for p in $$list; do \
278 f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
279 echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
280 rm -f "$(DESTDIR)$(bindir)/$$f"; \
281 done
282
283 mostlyclean-compile:
284 -rm -f *.$(OBJEXT)
285
286 distclean-compile:
287 -rm -f *.tab.c
288
289 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi.Po@am__quote@
290 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi_service.Po@am__quote@
291 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi_socket_dir.Po@am__quote@
292 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpid.Po@am__quote@
293 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpid_common.Po@am__quote@
294 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
295 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_new.Po@am__quote@
296
297 .c.o:
298 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
299 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
300 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
301 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
302 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
303
304 .c.obj:
305 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
306 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
307 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
308 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
309 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
310 uninstall-info-am:
311
312 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
313 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
314 unique=`for i in $$list; do \
315 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
316 done | \
317 $(AWK) ' { files[$$0] = 1; } \
318 END { for (i in files) print i; }'`; \
319 mkid -fID $$unique
320 tags: TAGS
321
322 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
323 $(TAGS_FILES) $(LISP)
324 tags=; \
325 here=`pwd`; \
326 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
327 unique=`for i in $$list; do \
328 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
329 done | \
330 $(AWK) ' { files[$$0] = 1; } \
331 END { for (i in files) print i; }'`; \
332 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
333 test -n "$$unique" || unique=$$empty_fix; \
334 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
335 $$tags $$unique; \
336 fi
337 ctags: CTAGS
338 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
339 $(TAGS_FILES) $(LISP)
340 tags=; \
341 here=`pwd`; \
342 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
343 unique=`for i in $$list; do \
344 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
345 done | \
346 $(AWK) ' { files[$$0] = 1; } \
347 END { for (i in files) print i; }'`; \
348 test -z "$(CTAGS_ARGS)$$tags$$unique" \
349 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
350 $$tags $$unique
351
352 GTAGS:
353 here=`$(am__cd) $(top_builddir) && pwd` \
354 && cd $(top_srcdir) \
355 && gtags -i $(GTAGS_ARGS) $$here
356
357 distclean-tags:
358 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
359
360 distdir: $(DISTFILES)
361 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
362 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
363 list='$(DISTFILES)'; for file in $$list; do \
364 case $$file in \
365 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
366 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
367 esac; \
368 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
369 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
370 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
371 dir="/$$dir"; \
372 $(mkdir_p) "$(distdir)$$dir"; \
373 else \
374 dir=''; \
375 fi; \
376 if test -d $$d/$$file; then \
377 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
378 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
379 fi; \
380 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
381 else \
382 test -f $(distdir)/$$file \
383 || cp -p $$d/$$file $(distdir)/$$file \
384 || exit 1; \
385 fi; \
386 done
387 check-am: all-am
388 check: check-am
389 all-am: Makefile $(PROGRAMS) $(SCRIPTS)
390 installdirs:
391 for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"; do \
392 test -z "$$dir" || $(mkdir_p) "$$dir"; \
393 done
394 install: install-am
395 install-exec: install-exec-am
396 install-data: install-data-am
397 uninstall: uninstall-am
398
399 install-am: all-am
400 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
401
402 installcheck: installcheck-am
403 install-strip:
404 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
405 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
406 `test -z '$(STRIP)' || \
407 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
408 mostlyclean-generic:
409
410 clean-generic:
411
412 distclean-generic:
413 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
414
415 maintainer-clean-generic:
416 @echo "This command is intended for maintainers to use"
417 @echo "it deletes files that may require special tools to rebuild."
418 clean: clean-am
419
420 clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
421
422 distclean: distclean-am
423 -rm -rf ./$(DEPDIR)
424 -rm -f Makefile
425 distclean-am: clean-am distclean-compile distclean-generic \
426 distclean-tags
427
428 dvi: dvi-am
429
430 dvi-am:
431
432 html: html-am
433
434 info: info-am
435
436 info-am:
437
438 install-data-am: install-data-local
439
440 install-exec-am: install-binPROGRAMS install-binSCRIPTS
441
442 install-info: install-info-am
443
444 install-man:
445
446 installcheck-am:
447
448 maintainer-clean: maintainer-clean-am
449 -rm -rf ./$(DEPDIR)
450 -rm -f Makefile
451 maintainer-clean-am: distclean-am maintainer-clean-generic
452
453 mostlyclean: mostlyclean-am
454
455 mostlyclean-am: mostlyclean-compile mostlyclean-generic
456
457 pdf: pdf-am
458
459 pdf-am:
460
461 ps: ps-am
462
463 ps-am:
464
465 uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
466 uninstall-info-am
467
468 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
469 clean-generic ctags distclean distclean-compile \
470 distclean-generic distclean-tags distdir dvi dvi-am html \
471 html-am info info-am install install-am install-binPROGRAMS \
472 install-binSCRIPTS install-data install-data-am \
473 install-data-local install-exec install-exec-am install-info \
474 install-info-am install-man install-strip installcheck \
475 installcheck-am installdirs maintainer-clean \
476 maintainer-clean-generic mostlyclean mostlyclean-compile \
477 mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
478 uninstall-am uninstall-binPROGRAMS uninstall-binSCRIPTS \
479 uninstall-info-am
480
481
482 install-data-local :
483 $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
484 echo dpi_dir=$(libdir)/dillo/dpi > $(DESTDIR)$(sysconfdir)/dpidrc
485 # Tell versions [3.59,3.63) of GNU make to not export all variables.
486 # Otherwise a system limit (for SysV at least) may be exceeded.
487 .NOEXPORT:
0 Todo List
1
2 File dpi_service.c
3 This module should be removed because its original functions
4 have been removed or modified. Put these functions in dpid.c
5
6 File dpi_service.h
7 This module should be removed because its original functions
8 have been removed or modified. Put these functions in dpid.c
9
10 Add other file types, but first we need to add files associated
11 with a dpi to the design.
12
13 Global SRS_NAME
14 Should read this from dillorc
15
16 File dpid_common.h
17 The dpid error codes will be used in the next patch
18
19 Global main()
20 + add new plugins when they become available
21 __________________________________________________________
22
23 Remove global variables.
24
25 Use a singly linked list for dpi_attr_list
26
27 Allow dpis to be registered one at a time
28
29 Only run dpis listed in users dillorc
30
31 MAYBE Have dpid accept all connections to dpis (fixes inf loop if dpi crashes before accept)
0 /*
1 Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*! \file
19 * Access functions for ~/.dillo/dpi_socket_dir.
20 * The most useful function for dillo is a_Dpi_srs, it returns
21 * the full path to the dpid service request socket.
22 */
23
24 #include <errno.h>
25 #include <glib.h>
26 #include "dpid_common.h"
27 #include "dpi.h"
28 #include "misc_new.h"
29
30 /*! \Return
31 * Returns path to the dpi_socket_dir file
32 * Use g_free to free memory
33 */
34 char *a_Dpi_sockdir_file(void)
35 {
36 char *dpi_socket_dir = NULL, *dirfile_path = "/.dillo/dpi_socket_dir";
37
38 dpi_socket_dir = g_strconcat(a_Misc_get_home(), dirfile_path, NULL);
39 return dpi_socket_dir;
40 }
41
42 /*! Read socket directory path from ~/.dillo/dpi_socket_dir
43 * \Return
44 * socket directory path or NULL if the dpi_socket_dir file does not exist.
45 * \Note
46 * This function exits if ~/.dillo does not exist or
47 * if the dpi_socket_dir file cannot be opened for a
48 * reason other than it does not exist.
49 */
50
51 char *a_Dpi_rd_dpi_socket_dir(char *dirname)
52 {
53 FILE *dir;
54 char *sockdir = NULL, *rcpath;
55
56 rcpath = g_strconcat(a_Misc_get_home(), "/.dillo", NULL);
57
58 /* If .dillo does not exist it is an unrecoverable error */
59 if (access(rcpath, F_OK) == -1) {
60 ERRMSG("a_Dpi_rd_dpi_socket_dir", "access", errno);
61 fprintf(stderr, " - %s\n", rcpath);
62 exit(1);
63 }
64 g_free(rcpath);
65
66 if ((dir = fopen(dirname, "r")) != NULL) {
67 sockdir = a_Misc_get_line(dir);
68 fclose(dir);
69 } else if (errno == ENOENT) {
70 ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
71 fprintf(stderr, " - %s\n", dirname);
72 } else if (errno != ENOENT) {
73 ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
74 fprintf(stderr, " - %s\n", dirname);
75 exit(1);
76 }
77
78 return sockdir;
79 }
80
81 /*!
82 * \Modifies
83 * srs_name
84 * \Return
85 * The service request socket name with its complete path.
86 */
87 char *a_Dpi_srs(void)
88 {
89 char *dirfile_path, *sockdir, *srs_name;
90
91 dirfile_path = a_Dpi_sockdir_file();
92 sockdir = g_strstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path));
93 srs_name = g_strconcat(sockdir, "/", "dpid.srs", NULL);
94 g_free(sockdir);
95 g_free(dirfile_path);
96 return (srs_name);
97 }
0 /*! \file
1 * Access functions for ~/.dillo/dpi_socket_dir.
2 * The most useful function for dillo is a_Dpi_srs, it returns
3 * the full path to the dpid service request socket.
4 */
5
6 #ifndef DPI_H
7 #define DPI_H
8
9 #include <config.h>
10 #include <stdio.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <glib.h>
17
18 /* Check the Unix98 goodie */
19 #ifndef socklen_t
20 #define socklen_t guint32
21 #endif
22
23 /* Some systems may not have this one... */
24 #ifndef AF_LOCAL
25 #define AF_LOCAL AF_UNIX
26 #endif
27
28 /* This one is tricky, some sources state it should include the byte
29 * for the terminating NULL, and others say it shouldn't.
30 * The other way is to only use this one when a native SUN_LEN is not present,
31 * but as dillo has used this for a long time successfully, here it goes.
32 */
33 # define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
34 + strlen ((ptr)->sun_path))
35
36 /*!
37 * dpi commands
38 */
39 enum {
40 UNKNOWN_CMD,
41 BYE_CMD, /* "DpiBye" */
42 CHECK_SERVER_CMD, /* "check_server" */
43 REGISTER_ALL_CMD, /* "register_all" */
44 REGISTER_SERVICE_CMD /* "register_service" */
45 };
46
47
48 char *a_Dpi_sockdir_file(void);
49
50 char *a_Dpi_rd_dpi_socket_dir(char *dirname);
51
52 char *a_Dpi_srs(void);
53
54 #endif
0 /*
1 Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*! \file
19 * \todo
20 * This module should be removed because its original functions
21 * have been removed or modified.
22 * Put these functions in dpid.c
23 */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <glib.h>
30 #include "dpid_common.h"
31 #include "dpid.h"
32 #include "../dpip/dpip.h"
33
34 #ifdef TEST
35 #include "testdat.h"
36 #endif
37
38 /* exported functions */
39 char *get_dpi_dir(char *dpidrc);
40
41
42 /*
43 * Read a line of text up to the newline character, store it into a newly
44 * allocated string and return it.
45 * \Note
46 * Copied from dpi/bookmarks.c
47 */
48 static char *Get_line(FILE *stream)
49 {
50 guint i, size = 64;
51 int ch;
52 char *buf;
53
54 buf = g_new(char, size);
55
56 for (i = 0; (ch = fgetc(stream)) != EOF; ++i) {
57 if (i + 1 == size) {
58 size *= 2;
59 buf = g_realloc(buf, size);
60 }
61 if ((buf[i] = ch) == '\n' && ++i)
62 break;
63 }
64 buf[i] = 0;
65
66 if (i > 0) {
67 buf = g_realloc(buf, i + 1);
68 } else {
69 g_free(buf);
70 buf = NULL;
71 }
72 return buf;
73 }
74
75 /*! Get dpi directory path from dpidrc
76 * \Return
77 * dpi directory on success, NULL on failure
78 * \Important
79 * The dpi_dir definition in dpidrc must have no leading white space.
80 */
81 char *get_dpi_dir(char *dpidrc)
82 {
83 FILE *In;
84 gint len;
85 char *rcline = NULL, *value = NULL, *p;
86
87 if ((In = fopen(dpidrc, "r")) == NULL) {
88 ERRMSG("dpi_dir", "fopen", errno);
89 fprintf(stderr, " - %s\n", dpidrc);
90 return (NULL);
91 }
92
93 while ((rcline = Get_line(In)) != NULL) {
94 if (strncmp(rcline, "dpi_dir", 7) == 0)
95 break;
96 g_free(rcline);
97 }
98 fclose(In);
99
100 if (!rcline) {
101 ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
102 fprintf(stderr, "Put your dillo plugins path in %s\n", dpidrc);
103 fprintf(stderr, "eg. dpi_dir=/usr/local/lib/dillo/dpi ");
104 fprintf(stderr, "with no leading spaces.\n");
105 value = NULL;
106 } else {
107 len = (int) strlen(rcline);
108 if (len && rcline[len - 1] == '\n')
109 rcline[len - 1] = 0;
110
111 if ((p = strchr(rcline, '='))) {
112 while (*++p == ' ');
113 value = g_strdup(p);
114 } else {
115 ERRMSG("dpi_dir", "strchr", 0);
116 fprintf(stderr, " - '=' not found in %s\n", rcline);
117 value = NULL;
118 }
119 }
120
121 if (rcline)
122 g_free(rcline);
123 return (value);
124 }
125
126 /*! Send the list of available dpi IDs to a client
127 * \Return
128 * 1 on success, -1 on failure.
129 *
130 static int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num)
131 {
132 int i;
133 gchar *buf;
134 ssize_t wlen = 0;
135
136 for (i = 0; i < srv_num && wlen != -1; i++) {
137 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
138 "send_data", dpi_attr_list[i].id);
139 wlen = write(sock, d_cmd, strlen(d_cmd));
140 g_free(d_cmd);
141 }
142 if (wlen == -1) {
143 ERRMSG("send_service_list", "write", errno);
144 return (-1);
145 }
146 return (1);
147 }
148 */
0 /*! \file
1 * \todo
2 * This module should be removed because its original functions
3 * have been removed or modified.
4 * Put these functions in dpid.c
5 */
6
7 #ifndef DPI_SERVICE_H
8 #define DPI_SERVICE_H
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include "dpid.h"
14
15 char *Get_line(FILE *stream);
16
17 char *get_dpi_dir(char *dpidrc);
18
19 int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num);
20
21 #endif
0 /*
1 Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*! \file
19 * Create a per user temporary directory for dpi sockets
20 */
21
22 #include <errno.h>
23 #include "dpid_common.h"
24 #include "dpi.h"
25 #include "misc_new.h"
26 #include "dpi_socket_dir.h" /* for function prototypes */
27
28 /*! Save socket directory name in ~/.dillo/dpi_socket_dir
29 * \Return
30 * \li 1 on success
31 * \li -1 on failure
32 */
33 int w_dpi_socket_dir(char *dirname, char *sockdir)
34 {
35 FILE *dir;
36
37 if ((dir = fopen(dirname, "w")) == NULL) {
38 ERRMSG("w_dpi_socket_dir", "fopen", errno);
39 return (-1);
40 }
41 fprintf(dir, "%s", sockdir);
42 fclose(dir);
43 return (1);
44 }
45
46 /*! Test that socket directory exists and is a directory
47 * \Return
48 * \li 1 on success
49 * \li 0 on failure
50 * \bug Does not check permissions or that it's a symbolic link
51 * to another directory.
52 */
53 int tst_dir(char *dir)
54 {
55 char *dirtest;
56 gint ret = 0;
57
58 /* test for a directory */
59 dirtest = g_strconcat(dir, "/", NULL);
60 if (access(dirtest, F_OK) == -1) {
61 ERRMSG("tst_dir", "access", errno);
62 fprintf(stderr, " - %s\n", dirtest);
63 } else {
64 ret = 1;
65 }
66 g_free(dirtest);
67
68 return ret;
69 }
70
71 /*! Create socket directory
72 * \Return
73 * \li Socket directory path on success
74 * \li NULL on failure
75 */
76 char *mk_sockdir(void)
77 {
78 char *username, *template;
79
80 username = a_Misc_get_user();
81 template = g_strconcat("/tmp/", username, "-", "XXXXXX", NULL);
82 if (a_Misc_mkdtemp(template) == NULL) {
83 ERRMSG("mk_sockdir", "a_Misc_mkdtemp", 0);
84 fprintf(stderr, " - %s\n", template);
85 g_free(template);
86 return (NULL);
87 }
88 return template;
89 }
90
91 /*! Create socket directory if it does not exist and save its name in
92 * ~/.dillo/dpi_socket_dir.
93 * \Return
94 * \li Socket directory name on success
95 * \li NULL on failure.
96 */
97 char *init_sockdir(char *dpi_socket_dir)
98 {
99 char *sockdir = NULL;
100 int dir_ok = 0;
101
102 if ((sockdir = a_Dpi_rd_dpi_socket_dir(dpi_socket_dir)) == NULL) {
103 fprintf(stderr, "debug_msg - init_sockdir: ");
104 fprintf(stderr, "The dpi_socket_dir file does not exist\n");
105 } else {
106 if ((dir_ok = tst_dir(sockdir)) == 1) {
107 fprintf(stderr,
108 "debug_msg - init_sockdir: The socket directory ");
109 fprintf(stderr, "%s exists and is OK\n", sockdir);
110 } else {
111 fprintf(stderr,
112 "debug_msg - init_sockdir: The socket directory ");
113 fprintf(stderr, "%s does not exist or is not a directory\n",
114 sockdir);
115 g_free(sockdir);
116 }
117 }
118 if (!dir_ok) {
119 sockdir = mk_sockdir();
120 if (sockdir == NULL) {
121 ERRMSG("init_sockdir", "mk_sockdir", 0);
122 fprintf(stderr, " - Failed to create dpi socket directory\n");
123 } else if ((w_dpi_socket_dir(dpi_socket_dir, sockdir)) == -1) {
124 ERRMSG("init_sockdir", "w_dpi_socket_dir", 0);
125 fprintf(stderr, " - failed to save %s\n", sockdir);
126 g_free(sockdir);
127 sockdir = NULL;
128 }
129 }
130 return (sockdir);
131 }
0 /*! \file
1 * Create a per user temporary directory for dpi sockets
2 */
3
4 #ifndef DPI_SOCKET_DIR_H
5 #define DPI_SOCKET_DIR_H
6
7
8 int w_dpi_socket_dir(char *dirname, char *sockdir);
9
10 int tst_dir(char *dir);
11
12 char *mk_sockdir(void);
13
14 char *init_sockdir(char *dpi_socket_dir);
15
16 #endif
0 /*
1 Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*! \file
19 * Main functions to set-up dpi information and to initialise sockets
20 */
21 #include <errno.h>
22 #include <glib.h>
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 #include "dpid_common.h"
27 #include "dpid.h"
28 #include "dpi.h"
29 #include "dpi_socket_dir.h"
30 #include "dpi_service.h"
31 #include "misc_new.h"
32
33 #include "../dpip/dpip.h"
34
35 #define QUEUE 5
36
37 volatile sig_atomic_t caught_sigchld = 0;
38
39 /*! Close and remove the sockets in the
40 * given dpi attribute list
41 */
42 void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
43 {
44 gint i;
45
46 for (i = 0; i < numdpis; i++) {
47 a_Misc_close_fd(dpi_attr_list[i].socket);
48 (void) unlink(dpi_attr_list[i].sockpath);
49 }
50 }
51
52 /*! Close and remove inactive dpi sockets
53 * \Return
54 * Number of active dpis.
55 */
56 int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
57 {
58 int i, active = 0;
59
60 for (i = 0; i < numdpis; i++) {
61 if (dpi_attr_list[i].pid == 1) {
62 a_Misc_close_fd(dpi_attr_list[i].socket);
63 (void) unlink(dpi_attr_list[i].sockpath);
64 } else
65 active++;
66 }
67 return (active);
68 }
69
70 /*! Remove sockets
71 */
72 void cleanup(char *socket_dir)
73 {
74 DIR *dir;
75 struct dirent *dir_entry = NULL;
76 char *sockpath;
77
78 dir = opendir(socket_dir);
79 if (dir == NULL) {
80 ERRMSG("cleanup", "opendir", errno);
81 return;
82 }
83 while ( (dir_entry = readdir(dir)) != NULL ) {
84 if ( dir_entry->d_name[0] == '.' )
85 continue;
86 sockpath = g_strconcat(socket_dir, "/", dir_entry->d_name, NULL);
87 unlink(sockpath);
88 g_free(sockpath);
89 }
90 closedir(dir);
91 }
92
93 /*! Free memory used to describe
94 * a set of dpi attributes
95 */
96 void free_dpi_attr(struct dp *dpi_attr)
97 {
98 if (dpi_attr->id != NULL) {
99 g_free(dpi_attr->id);
100 dpi_attr->id = NULL;
101 }
102 if (dpi_attr->path != NULL) {
103 g_free(dpi_attr->path);
104 dpi_attr->path = NULL;
105 }
106 if (dpi_attr->sockpath != NULL) {
107 g_free(dpi_attr->sockpath);
108 dpi_attr->sockpath = NULL;
109 }
110 }
111
112 /*! Free memory used by the plugin list
113 */
114 void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis)
115 {
116 int i;
117 struct dp *dpi_attr_list = *dpi_attr_list_ptr;
118
119 if (dpi_attr_list == NULL)
120 return;
121
122 for (i = 0; i < numdpis; i++)
123 free_dpi_attr(dpi_attr_list + i);
124
125 g_free(dpi_attr_list);
126 *dpi_attr_list_ptr = NULL;
127 }
128
129 /*! \todo
130 * Remove terminator and est_terminator unless we really want to clean up
131 * on abnormal exit.
132 */
133 #if 0
134 /*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
135 */
136 void terminator(int sig)
137 {
138 (void) signal(SIGCHLD, SIG_DFL);
139 cleanup();
140 (void) signal(sig, SIG_DFL);
141 (void) raise(sig);
142 _exit(0);
143 }
144
145 /*! Establish handler for termination signals
146 * and register cleanup with atexit */
147 void est_terminator(void)
148 {
149 struct sigaction act;
150 sigset_t block;
151
152 (void) sigemptyset(&block);
153 (void) sigaddset(&block, SIGINT);
154 (void) sigaddset(&block, SIGQUIT);
155 (void) sigaddset(&block, SIGTERM);
156 (void) sigaddset(&block, SIGSEGV);
157
158 act.sa_handler = terminator;
159 act.sa_mask = block;
160 act.sa_flags = 0;
161
162 if (sigaction(SIGINT, &act, NULL) ||
163 sigaction(SIGQUIT, &act, NULL) ||
164 sigaction(SIGTERM, &act, NULL) || sigaction(SIGSEGV, &act, NULL)) {
165 ERRMSG("est_terminator", "sigaction", errno);
166 exit(1);
167 }
168
169 if (atexit(cleanup) != 0) {
170 ERRMSG("est_terminator", "atexit", 0);
171 fprintf(stderr, "Hey! atexit failed, how did that happen?\n");
172 exit(1);
173 }
174 }
175 #endif
176
177 /*! Identify a given file
178 * Currently there is only one file type associated with dpis.
179 * More file types will be added as needed
180 */
181 enum file_type get_file_type(gchar *file_name)
182 {
183 gchar *dot = strrchr(file_name, '.');
184
185 if (dot && !strcmp(dot, ".dpi"))
186 return DPI_FILE; /* Any filename ending in ".dpi" */
187 else {
188 fprintf(stderr, "get_file_type: Unknown file type for %s\n", file_name);
189 return UNKNOWN_FILE;
190 }
191 }
192
193 /*! Scans a service directory in dpi_dir and fills dpi_attr
194 * \Note
195 * Caller must allocate memory for dpi_attr.
196 * \Return
197 * \li 0 on success
198 * \li -1 on failure
199 * \todo
200 * Add other file types, but first we need to add files associated with a dpi
201 * to the design.
202 */
203 int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
204 {
205 char *service_dir = NULL;
206 struct stat statinfo;
207 enum file_type ftype;
208 int retval = -1;
209 DIR *dir_stream;
210 struct dirent *dir_entry = NULL;
211
212 service_dir = g_strconcat(dpi_dir, "/", service, NULL);
213 if (stat(service_dir, &statinfo) == -1) {
214 ERRMSG("get_dpi_attr", "stat", errno);
215 fprintf(stderr, "file=%s\n", service_dir);
216 } else if ( (dir_stream = opendir(service_dir)) == NULL) {
217 ERRMSG("get_dpi_attr", "opendir", errno);
218 } else {
219 /* Scan the directory loking for dpi files.
220 * (currently there's only the dpi program, but in the future
221 * there may also be helper scripts.) */
222 while ( (dir_entry = readdir(dir_stream)) != NULL) {
223 if (dir_entry->d_name[0] == '.')
224 continue;
225
226 ftype = get_file_type(dir_entry->d_name);
227 switch (ftype) {
228 case DPI_FILE:
229 dpi_attr->path =
230 g_strconcat(service_dir, "/", dir_entry->d_name, NULL);
231 dpi_attr->id = g_strdup(service);
232 dpi_attr->sockpath = NULL;
233 dpi_attr->pid = 1;
234 if (strstr(dpi_attr->path, ".filter") != NULL)
235 dpi_attr->filter = 1;
236 else
237 dpi_attr->filter = 0;
238 retval = 0;
239 break;
240 default:
241 break;
242 }
243 }
244 closedir(dir_stream);
245
246 if (retval != 0)
247 fprintf(stderr,"get_dpi_attr: No dpi plug-in in %s/%s\n",
248 dpi_dir, service);
249 }
250 g_free(service_dir);
251 return retval;
252 }
253
254 /*! Register a service
255 * Retrieves attributes for "service" and stores them
256 * in dpi_attr. It looks for "service" in ~/.dillo/dpi
257 * first, and then in the system wide dpi directory.
258 * Caller must allocate memory for dpi_attr.
259 * \Return
260 * \li 0 on success
261 * \li -1 on failure
262 */
263 int register_service(struct dp *dpi_attr, char *service)
264 {
265 char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
266 int retval = -1;
267
268 /* g_get_home_dir makes it hard to test for mem leaks */
269 user_dpi_dir = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPI, NULL);
270 user_service_dir =
271 g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPI, "/", service, NULL);
272
273 dpidrc = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPIDRC, NULL);
274 if ( access(dpidrc, F_OK) == -1 ) {
275 if ( access(DPIDRC_SYS, F_OK) == -1 ) {
276 ERRMSG("register_service", "Error ", 0);
277 fprintf(stderr, "\n - There is no %s or %s file\n", dpidrc,
278 DPIDRC_SYS);
279 g_free(user_dpi_dir);
280 g_free(user_service_dir);
281 g_free(dpidrc);
282 return(-1);
283 }
284 g_free(dpidrc);
285 dpidrc = g_strdup(DPIDRC_SYS);
286 }
287
288 /* Check home dir for dpis */
289 if (access(user_service_dir, F_OK) == 0) {
290 get_dpi_attr(user_dpi_dir, service, dpi_attr);
291 retval = 0;
292 } else { /* Check system wide dpis */
293 if ((dir = get_dpi_dir(dpidrc)) != NULL) {
294 if (access(dir, F_OK) == 0) {
295 get_dpi_attr(dir, service, dpi_attr);
296 retval = 0;
297 } else {
298 ERRMSG("register_service", "get_dpi_attr failed\n", 0);
299 }
300 } else {
301 ERRMSG("register_service", "dpi_dir: Error getting dpi dir.\n", 0);
302 }
303 }
304 g_free(user_dpi_dir);
305 g_free(user_service_dir);
306 g_free(dpidrc);
307 g_free(dir);
308 return (retval);
309 }
310
311 /*!
312 * Create dpi directory for available
313 * plugins and create plugin list.
314 * \Return
315 * \li Returns number of available plugins on success
316 * \li -1 on failure
317 */
318 int register_all(struct dp **attlist)
319 {
320 DIR *user_dir_stream, *sys_dir_stream;
321 char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
322 char *basename=NULL;
323 struct dirent *user_dirent, *sys_dirent;
324 int j, st, not_in_user_list;
325 int snum, usr_srv_num;
326 size_t dp_sz = sizeof(struct dp);
327
328 if (*attlist != NULL) {
329 ERRMSG("register_all", "attlist parameter should be NULL\n", 0);
330 return -1;
331 }
332
333 user_dpidir = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPI, NULL);
334 if (access(user_dpidir, F_OK) == -1) {
335 /* no dpis in user's space */
336 g_free(user_dpidir);
337 user_dpidir = NULL;
338 }
339 dpidrc = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPIDRC, NULL);
340 if (access(dpidrc, F_OK) == -1) {
341 g_free(dpidrc);
342 dpidrc = g_strdup(DPIDRC_SYS);
343 if (access(dpidrc, F_OK) == -1) {
344 g_free(dpidrc);
345 dpidrc = NULL;
346 }
347 }
348 if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
349 sys_dpidir = NULL;
350 g_free(dpidrc);
351
352 if (!user_dpidir && !sys_dpidir) {
353 ERRMSG("register_all", "Fatal error ", 0);
354 fprintf(stderr, "\n - Can't find the directory for dpis.\n");
355 exit(1);
356 }
357
358 /* Get list of services in user's .dillo/dpi directory */
359 snum = usr_srv_num = 0;
360 if (user_dpidir && (user_dir_stream = opendir(user_dpidir)) != NULL) {
361 while ((user_dirent = readdir(user_dir_stream)) != NULL) {
362 if (user_dirent->d_name[0] == '.')
363 continue;
364 *attlist = (struct dp *) g_realloc(*attlist, (snum + 1) * dp_sz);
365 st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]);
366 if (st == 0)
367 snum++;
368 }
369 usr_srv_num = snum;
370 closedir(user_dir_stream);
371 }
372 if (sys_dpidir && (sys_dir_stream = opendir(sys_dpidir)) != NULL) {
373 /* if system service is not in user list then add it */
374 while ((sys_dirent = readdir(sys_dir_stream)) != NULL) {
375 if (sys_dirent->d_name[0] == '.')
376 continue;
377 not_in_user_list = 1;
378 for (j = 0; j < usr_srv_num; j++) {
379 basename = g_basename((*attlist)[j].path);
380 if (strcmp(sys_dirent->d_name, basename) == 0) {
381 not_in_user_list = 0;
382 break;
383 }
384 }
385 if (not_in_user_list) {
386 *attlist = (struct dp *) g_realloc(*attlist, (snum + 1) * dp_sz);
387 st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
388 if (st == 0)
389 snum++;
390 }
391 }
392 closedir(sys_dir_stream);
393 }
394
395 g_free(sys_dpidir);
396 g_free(user_dpidir);
397
398 /* todo: do we consider snum == 0 an error?
399 * (if so, we should return -1 ) */
400 return (snum);
401 }
402
403 /*! Initialise the service request socket
404 * \Return:
405 * \li Number of sockets (1 == success)
406 * \li -1 on failure
407 */
408 int init_srs_socket(char *sockdir)
409 {
410 int retval = -1;
411 struct sockaddr_un srs_sa;
412 size_t sun_path_len;
413 socklen_t addr_sz;
414
415 srs_name = g_strconcat(sockdir, "/", SRS_NAME, NULL);
416 FD_ZERO(&sock_set);
417
418 /* Initialise srs, service request socket on startup */
419 if ((srs = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
420 ERRMSG("init_srs_socket", "socket", errno);
421 return (retval); /* avoids nesting ifs too deeply */
422 }
423 /* Set srs to close on exec */
424 fcntl(srs, F_SETFD, FD_CLOEXEC | fcntl(srs, F_GETFD));
425
426 srs_sa.sun_family = AF_LOCAL;
427
428 sun_path_len = sizeof(srs_sa.sun_path);
429 if ( strlen(srs_name) > sun_path_len) {
430 ERRMSG("init_srs_socket", "srs_name is too long", 0);
431 fprintf(stderr, "\n - it should be <= %lu chars", (gulong)sun_path_len);
432 fprintf(stderr, "\n - srs_name = %s\n", srs_name);
433 return(retval);
434 }
435 strncpy(srs_sa.sun_path, srs_name, sun_path_len);
436 addr_sz = (socklen_t) D_SUN_LEN(&srs_sa);
437
438 if ((bind(srs, (struct sockaddr *) &srs_sa, addr_sz)) == -1) {
439 if (errno == EADDRINUSE) {
440 ERRMSG("init_srs_socket", "bind", errno);
441 fprintf(stderr, "srs_sa.sun_path = %s\n", srs_sa.sun_path);
442 dpi_errno = dpid_srs_addrinuse;
443 } else {
444 ERRMSG("init_srs_socket", "bind", errno);
445 fprintf(stderr, "srs_sa.sun_path = %s\n", srs_sa.sun_path);
446 }
447 } else if (chmod(srs_sa.sun_path, S_IRUSR | S_IWUSR) == -1) {
448 ERRMSG("init_srs_socket", "chmod", errno);
449 fprintf(stderr, "srs_sa.sun_path = %s\n", srs_sa.sun_path);
450 } else if (listen(srs, QUEUE) == -1) {
451 ERRMSG("init_srs_socket", "listen", errno);
452 } else {
453 retval = 1;
454 }
455
456 FD_SET(srs, &sock_set);
457 return (retval);
458 }
459
460 /*! Initialise a single dpi socket
461 * \Return
462 * \li 1 on success
463 * \li -1 on failure
464 */
465 int init_dpi_socket(struct dp *dpi_attr, char *sockdir)
466 {
467 int caught_error = 0, s;
468 char *dpi_nm; /* pointer to basename in dpi_attr->path */
469 struct sockaddr_un sa;
470 size_t sp_len;
471 socklen_t addr_sz;
472 size_t sock_buflen = 8192;
473
474 sp_len = sizeof(sa.sun_path);
475 if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
476 ERRMSG("init_all_dpi_sockets", "socket", errno);
477 return (-1); /* avoids nested ifs */
478 }
479 /* Set the socket FD to close on exec */
480 fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD));
481
482 /* set some buffering to increase the transfer's speed */
483 setsockopt(s, SOL_SOCKET, SO_SNDBUF,
484 &sock_buflen, (socklen_t)sizeof(sock_buflen));
485
486 dpi_attr->socket = s;
487 dpi_attr->sa.sun_family = AF_LOCAL;
488 dpi_nm = g_basename(dpi_attr->path);
489
490 dpi_attr->sockpath = g_strconcat(sockdir, "/", dpi_nm, NULL);
491 if ( strlen(dpi_attr->sockpath) > sp_len) {
492 ERRMSG("init_all_dpi_sockets", "socket path is too long", 0);
493 fprintf(stderr, "\n - it should be <= %lu chars", (gulong)sp_len);
494 fprintf(stderr, "\n - socket path = %s\n", dpi_attr->sockpath);
495 return(-1);
496 }
497 strncpy(dpi_attr->sa.sun_path, dpi_attr->sockpath, sp_len);
498 addr_sz = (socklen_t) D_SUN_LEN(&dpi_attr->sa);
499
500 if ((bind(s, (struct sockaddr *) &dpi_attr->sa, addr_sz)) == -1) {
501 ERRMSG("init_all_dpi_sockets", "bind", errno);
502 fprintf(stderr, "%s\n", dpi_attr->sa.sun_path);
503 caught_error = 1;
504 } else if (chmod(dpi_attr->sa.sun_path, S_IRUSR | S_IWUSR) == -1) {
505 ERRMSG("init_all_dpi_sockets", "chmod", errno);
506 fprintf(stderr, "%s\n", dpi_attr->sa.sun_path);
507 caught_error = 1;
508 } else if (listen(s, QUEUE) == -1) {
509 ERRMSG("init_all_dpi_sockets", "listen", errno);
510 caught_error = 1;
511 }
512
513 if (caught_error) {
514 return (-1);
515 } else {
516 FD_SET(s, &sock_set);
517 return (1);
518 }
519 }
520
521 /*! Setup sockets for the plugins and add them to
522 * to the set of sockets (sock_set) watched by select.
523 * \Return
524 * \li Number of sockets on success
525 * \li -1 on failure
526 * \Modifies
527 * dpi_attr_list.sa, dpi_attr_list.socket, numsocks, sock_set, srs
528 * \Uses
529 * numdpis, srs, srs_name
530 */
531 int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir)
532 {
533 int i;
534 struct sockaddr_un sa;
535 size_t sp_len;
536
537 sp_len = sizeof(sa.sun_path);
538
539 /* Initialise sockets for each dpi */
540 for (i = 0; i < numdpis; i++) {
541 if (init_dpi_socket(dpi_attr_list + i, sockdir) == -1)
542 return (-1);
543 numsocks++;
544 }
545
546 return (numsocks);
547 }
548
549 /*! SIGCHLD handler
550 */
551 void dpi_sigchld(int sig)
552 {
553 caught_sigchld = 1;
554 }
555
556 /*! Called by main loop when caught_sigchld == 1 */
557 void handle_sigchld(void)
558 {
559 int i, status;
560
561 /* For all of the dpis in the current list
562 * add the ones that have exited to the set of sockets being
563 * watched by 'select'.
564 */
565 for (i = 0; i < numdpis; i++) {
566 if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
567 dpi_attr_list[i].pid = 1;
568 FD_SET(dpi_attr_list[i].socket, &sock_set);
569 numsocks++;
570 }
571 }
572
573 /* Wait for any old dpis that have exited */
574 while (waitpid(-1, &status, WNOHANG) > 0)
575 ;
576 }
577
578 /*! Establish SIGCHLD handler */
579 void est_dpi_sigchld(void)
580 {
581 struct sigaction sigact;
582 sigset_t set;
583
584 (void) sigemptyset(&set);
585 sigact.sa_handler = dpi_sigchld;
586 sigact.sa_mask = set;
587 sigact.sa_flags = SA_NOCLDSTOP;
588 if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
589 ERRMSG("est_dpi_sigchld", "sigaction", errno);
590 exit(1);
591 }
592 }
593
594 /*! Send DpiBye command to all active non-filter dpis
595 */
596 void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
597 {
598 static char *DpiBye_cmd = NULL;
599 int i, dpi_socket;
600 struct sockaddr_un dpi_addr;
601 struct sockaddr_un sa;
602 size_t sun_path_len, addr_len;
603
604 if (!DpiBye_cmd)
605 DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
606
607 sun_path_len = sizeof(sa.sun_path);
608 addr_len = sizeof(dpi_addr);
609
610 dpi_addr.sun_family = AF_LOCAL;
611
612 for (i = 0; i < numdpis; i++) {
613 /* Skip inactive dpis and filters */
614 if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
615 continue;
616
617 if ((dpi_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
618 ERRMSG("stop_active_dpis", "socket", errno);
619 }
620 if ( strlen(dpi_attr_list[i].sockpath) > sun_path_len) {
621 ERRMSG("stop_active_dpis", "socket path is too long", 0);
622 fprintf(stderr,"\n - it should be <= %lu chars",(gulong)sun_path_len);
623 fprintf(stderr,"\n - socket path = %s\n", dpi_attr_list[i].sockpath);
624 }
625 strncpy(dpi_addr.sun_path, dpi_attr_list[i].sockpath, sun_path_len);
626 if (connect(dpi_socket, (struct sockaddr *) &dpi_addr, addr_len) == -1) {
627 ERRMSG("stop_active_dpis", "connect", errno);
628 fprintf(stderr, "%s\n", dpi_addr.sun_path);
629 }
630 (void) write(dpi_socket, DpiBye_cmd, strlen(DpiBye_cmd));
631 a_Misc_close_fd(dpi_socket);
632 }
633 }
634
635 /*! Removes dpis in dpi_attr_list from the
636 * set of sockets watched by select and
637 * closes their sockets.
638 */
639 void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
640 {
641 int i;
642
643 for (i = 0; i < numdpis; i++) {
644 FD_CLR(dpi_attr_list[i].socket, &sock_set);
645 a_Misc_close_fd(dpi_attr_list[i].socket);
646 }
647 }
648
649 /*! Registers available dpis and stops active non-filter dpis.
650 * Called when dpid receives
651 * cmd='register' service='all'
652 * command
653 * \Return
654 * Number of available dpis
655 */
656 int register_all_cmd(char *sockdir)
657 {
658 stop_active_dpis(dpi_attr_list, numdpis);
659 rm_dpi_sockets(dpi_attr_list, numdpis);
660 free_plugin_list(&dpi_attr_list, numdpis);
661 numdpis = 0;
662 numsocks = 1; /* the srs socket */
663 FD_ZERO(&sock_set);
664 FD_SET(srs, &sock_set);
665 numdpis = register_all(&dpi_attr_list);
666 numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir);
667 return (numdpis);
668 }
669
670 /*!
671 * Get value of msg field from dpi_tag
672 * \Return
673 * message on success, NULL on failure
674 */
675 char *get_message(int sock, char *dpi_tag)
676 {
677 char *msg, *d_cmd;
678
679 msg = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "msg");
680 if (msg == NULL) {
681 ERRMSG("get_message", "failed to parse msg\n", 0);
682 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
683 "DpiError", "Failed to parse request");
684 (void) CKD_WRITE(sock, d_cmd);
685 g_free(d_cmd);
686 }
687 return (msg);
688 }
689
690 /*!
691 * Send socket path that matches dpi_id to client
692 */
693 void send_sockpath(gint sock, gchar *dpi_tag, struct dp *dpi_attr_list)
694 {
695 gint i;
696 gchar *dpi_id;
697 char *d_cmd;
698
699 g_return_if_fail((dpi_id = get_message(sock, dpi_tag)) != NULL);
700
701 for (i = 0; i < numdpis; i++)
702 if (strstr(dpi_attr_list[i].id, dpi_id))
703 break;
704 if (i < numdpis) {
705 /* found */
706 if (access(dpi_attr_list[i].path, F_OK) == -1) {
707 ERRMSG("send_sockpath", "access", errno);
708 fprintf(stderr, " - %s\n", dpi_attr_list[i].sockpath);
709 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
710 "DpiError", "Plugin currently unavailable");
711 (void) CKD_WRITE(sock, d_cmd);
712 g_free(d_cmd);
713 } else {
714 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
715 "send_data", dpi_attr_list[i].sockpath);
716 (void) CKD_WRITE(sock, d_cmd);
717 g_free(d_cmd);
718 }
719 }
720
721 g_free(dpi_id);
722 }
0 /*! \file
1 * Main functions to set-up dpi information and to initialise sockets
2 */
3
4 #ifndef DPID_H
5 #define DPID_H
6
7 #include <assert.h>
8 #include <signal.h>
9 #include <fcntl.h>
10 #include <sys/stat.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <sys/socket.h>
18 #include <sys/time.h>
19 #include <sys/un.h>
20 #include <errno.h>
21 #include <glib.h>
22
23 #define PATH_LEN 50
24 #define CMDLEN 20
25 #define MSGLEN 50
26 /*! \todo: Should read this from dillorc */
27 #define SRS_NAME "dpid.srs"
28 char *srs_name;
29
30 /*! dpid service request socket */
31 int srs;
32
33 /*! plugin state information
34 */
35 struct dp {
36 char *id;
37 char *path;
38 char *sockpath;
39 int socket;
40 struct sockaddr_un sa;
41 pid_t pid;
42 int filter;
43 };
44
45 /*! Number of available plugins */
46 int numdpis;
47
48 /*! Number of sockets being watched */
49 int numsocks;
50
51 /*! State information for each plugin. */
52 struct dp *dpi_attr_list;
53
54 /*! Set of sockets watched for connections */
55 fd_set sock_set;
56
57 /*! Set to 1 by the SIGCHLD handler dpi_sigchld */
58 extern volatile sig_atomic_t caught_sigchld;
59
60 void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
61
62 int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
63
64 void cleanup(char *socket_dir);
65
66 void free_dpi_attr(struct dp *dpi_attr);
67
68 void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis);
69
70 enum file_type get_file_type(gchar *file_name);
71
72 int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr);
73
74 int register_service(struct dp *dpi_attr, char *service);
75
76 int register_all(struct dp **attlist);
77
78 int init_srs_socket(char *sockdir);
79
80 int init_dpi_socket(struct dp *dpi_attr, char *sockdir);
81
82 int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir);
83
84 void dpi_sigchld(int sig);
85
86 void handle_sigchld(void);
87
88 void est_dpi_sigchld(void);
89
90 void stop_active_dpis(struct dp *dpi_attr_list, int numdpis);
91
92 void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
93
94 int register_all_cmd(char *sockdir);
95
96 char *get_message(int sock, char *dpi_tag);
97
98 void send_sockpath(gint sock, gchar * dpi_tag, struct dp *dpi_attr_list);
99
100 #endif
0 #include <stdio.h>
1 #include <string.h>
2 #include <unistd.h>
3 #include <glib.h>
4 #include "dpid_common.h"
5
6 /*
7 * Send a verbose error message.
8 */
9 void errmsg(char *caller, char *called, int errornum, char *file, int line)
10 {
11 fprintf(stderr, "%s:%d: %s: %s : ", file, line, caller, called);
12 if (errornum > 0)
13 fprintf(stderr, "%s\n", g_strerror(errornum));
14 }
15
16 /*! Selector function for scandir
17 * Do not scan files starting with '.'
18 */
19 int no_dotfiles(const struct dirent *filedat)
20 {
21 if (filedat->d_name[0] == '.')
22 return 0;
23 else
24 return 1;
25 }
26
27 /*!
28 * Provides an error checked write command.
29 * Call this via the CKD_WRITE macro
30 * \return write return value
31 */
32 ssize_t ckd_write(int fd, char *msg, char *file, int line)
33 {
34 ssize_t ret;
35
36 do {
37 ret = write(fd, msg, strlen(msg));
38 } while (ret == -1 && errno == EINTR);
39 if (ret == -1) {
40 fprintf(stderr, "%s:%d: write: %s\n", file, line, g_strerror(errno));
41 }
42 return (ret);
43 }
0 #ifndef DPID_COMMON_H
1 #define DPID_COMMON_H
2
3 /*! \file
4 * Declares common functions, global variables, and types.
5 *
6 * \todo
7 * The dpid error codes will be used in
8 * the next patch
9 */
10
11 #include <errno.h>
12 #include <sys/types.h>
13 #include <dirent.h>
14
15 #define dotDILLO_DPI ".dillo/dpi"
16 #define dotDILLO_DPIDRC ".dillo/dpidrc"
17 #define ERRMSG(CALLER, CALLED, ERR)\
18 errmsg(CALLER, CALLED, ERR, __FILE__, __LINE__)
19
20 /*!
21 * Macro for calling the ckd_write function
22 */
23 #define CKD_WRITE(fd, msg) ckd_write(fd, msg, __FILE__, __LINE__)
24
25
26 /*! Error codes for dpid */
27 enum {
28 no_errors,
29 dpid_srs_addrinuse /* dpid service request socket address already in use */
30 } dpi_errno;
31
32 /*! Intended for identifying dillo plugins
33 * and related files
34 */
35 enum file_type {
36 DPI_FILE, /*! Any file name containing .dpi */
37 UNKNOWN_FILE
38 };
39
40
41 void errmsg(char *caller, char *called, int errornum, char *file, int line);
42
43 int no_dotfiles(const struct dirent *filedat);
44
45 ssize_t ckd_write(int fd, char *msg, char *file, int line);
46
47 #endif
0 #!/usr/bin/perl -w
1 # Author: Ferdi Franceschini
2 #
3 # dpid control program
4 # Currently allows
5 # register: Tells dpid to register all available dpis
6 # stop: Stops dpid.
7
8 use strict;
9 use IO::Socket::UNIX;
10
11 # Get socket directory name
12 open(DSD, "<$ENV{HOME}/.dillo/dpi_socket_dir");
13 my $dir = <DSD>;
14 close(DSD);
15
16 my $socket = IO::Socket::UNIX->new(Peer => "$dir/dpid.srs", Type => SOCK_STREAM, Timeout => 1000 ) or die "new: $@";
17
18 $socket->autoflush(1);
19
20 my %dpi_command = (
21 "register" => "<dpi cmd='register_all' '>",
22 "stop" => "<dpi cmd='DpiBye' '>",
23 );
24
25 if ( exists($dpi_command{$ARGV[0]}) ) {
26 print $socket $dpi_command{$ARGV[0]};
27 } else {
28 close($socket);
29 print "Usage: dpidc register|stop\n";
30 }
0 /*
1 Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 #include <errno.h>
19 #include <unistd.h>
20 #include <limits.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <glib.h>
24 #include "dpid_common.h"
25 #include "dpid.h"
26 #include "dpi.h"
27 #include "dpi_socket_dir.h"
28 #include "misc_new.h"
29 #include "../dpip/dpip.h"
30
31 sigset_t mask_sigchld;
32
33
34 /* Start a dpi filter plugin after accepting the pending connection
35 * \Return
36 * \li Child process ID on success
37 * \li 0 on failure
38 */
39 static int start_filter_plugin(struct dp dpi_attr)
40 {
41 int newsock, old_stdout=-1, old_stdin=-1;
42 socklen_t csz;
43 struct sockaddr_un clnt_addr;
44 pid_t pid;
45
46 csz = (socklen_t) sizeof(clnt_addr);
47
48 newsock = accept(dpi_attr.socket, (struct sockaddr *) &clnt_addr, &csz);
49 if (newsock == -1)
50 ERRMSG("start_plugin", "accept", errno);
51
52 dup2(STDIN_FILENO, old_stdin);
53 if (dup2(newsock, STDIN_FILENO) == -1) {
54 ERRMSG("start_plugin", "dup2", errno);
55 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
56 exit(1);
57 }
58
59 dup2(STDOUT_FILENO, old_stdout);
60 if (dup2(newsock, STDOUT_FILENO) == -1) {
61 ERRMSG("start_plugin", "dup2", errno);
62 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
63 exit(1);
64 }
65 if ((pid = fork()) == -1) {
66 ERRMSG("main", "fork", errno);
67 return 0;
68 }
69 if ( pid == 0) {
70 /* Child, start plugin */
71 if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) {
72 ERRMSG("start_plugin", "execl", errno);
73 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
74 exit(1);
75 }
76 }
77
78 /* Parent, Close sockets fix stdio and return pid */
79 if (a_Misc_close_fd(newsock) == -1) {
80 ERRMSG("start_plugin", "close", errno);
81 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
82 exit(1);
83 }
84 a_Misc_close_fd(STDIN_FILENO);
85 a_Misc_close_fd(STDOUT_FILENO);
86 dup2(old_stdin, STDIN_FILENO);
87 dup2(old_stdout, STDOUT_FILENO);
88 return pid;
89 }
90
91 static void start_server_plugin(struct dp dpi_attr)
92 {
93 if (dup2(dpi_attr.socket, STDIN_FILENO) == -1) {
94 ERRMSG("start_plugin", "dup2", errno);
95 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
96 exit(1);
97 }
98 if (a_Misc_close_fd(dpi_attr.socket) == -1) {
99 ERRMSG("start_plugin", "close", errno);
100 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
101 exit(1);
102 }
103 if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) {
104 ERRMSG("start_plugin", "execl", errno);
105 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
106 exit(1);
107 }
108 }
109
110 /*!
111 * Read service request from sock
112 * \Return
113 * pointer to dynamically allocated request tag
114 */
115 static char *get_request(int sock)
116 {
117 char *req, buf[10];
118 size_t buflen;
119 size_t rqsz;
120 ssize_t rdln;
121
122 req = NULL;
123 buf[0] = '\0';
124 buflen = sizeof(buf) / sizeof(buf[0]);
125
126 (void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
127 for (rqsz = 0; (rdln = read(sock, buf, buflen)) != 0; rqsz += rdln) {
128 if (rdln == -1)
129 break;
130 req = (char *) realloc(req, rqsz + rdln + 1);
131 if (rqsz == 0)
132 req[0] = '\0';
133 strncat(req, buf, (size_t) rdln);
134 }
135 (void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
136 if (rdln == -1) {
137 ERRMSG("get_request", "read", errno);
138 }
139
140 return (req);
141 }
142
143 /*!
144 * Get value of cmd field in dpi_tag
145 * \Return
146 * command code on success, -1 on failure
147 */
148 static int get_command(int sock, char *dpi_tag, struct dp *dpi_attr_list)
149 {
150 char *cmd, *d_cmd;
151 int COMMAND;
152
153 if (dpi_tag == NULL) {
154 ERRMSG("get_command", "dpid tag is NULL\n", 0);
155 return (-1);
156 }
157
158 cmd = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "cmd");
159
160 if (cmd == NULL) {
161 ERRMSG("get_command", "a_Dpip_get_attr", 0);
162 fprintf(stderr, ": dpid failed to parse cmd in %s\n", dpi_tag);
163 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
164 "DpiError", "Failed to parse request");
165 (void) CKD_WRITE(sock, d_cmd);
166 g_free(d_cmd);
167 COMMAND = -1;
168 } else if (strcmp("DpiBye", cmd) == 0) {
169 COMMAND = BYE_CMD;
170 } else if (strcmp("check_server", cmd) == 0) {
171 COMMAND = CHECK_SERVER_CMD;
172 } else if (strcmp("register_all", cmd) == 0) {
173 COMMAND = REGISTER_ALL_CMD;
174 } else if (strcmp("register_service", cmd) == 0) {
175 COMMAND = REGISTER_SERVICE_CMD;
176 } else { /* Error unknown command */
177 COMMAND = UNKNOWN_CMD;
178 }
179
180 g_free(cmd);
181 return (COMMAND);
182 }
183
184 /*
185 * Check whether a dpi server is running
186 */
187 int server_is_running(char *server_id)
188 {
189 int i;
190
191 /* Search in the set of running servers */
192 for (i = 0; i < numdpis; i++) {
193 if (!dpi_attr_list[i].filter && dpi_attr_list[i].pid > 1 &&
194 strcmp(dpi_attr_list[i].id, server_id) == 0)
195 return 1;
196 }
197 return 0;
198 }
199
200
201 /*
202 * Get MAX open FD limit (yes, it's tricky --Jcid).
203 */
204 static int get_open_max(void)
205 {
206 #ifdef OPEN_MAX
207 return OPEN_MAX;
208 #else
209 int ret = sysconf(_SC_OPEN_MAX);
210 if (ret < 0)
211 ret = 256;
212 return ret;
213 #endif
214 }
215
216 /*! \todo
217 * \li Add a dpid_idle_timeout variable to dpidrc
218 * \bug Infinite loop if plugin crashes before it accepts a connection
219 */
220 int main(void)
221 {
222 int i, n = 0, open_max;
223 char *dirname = NULL, *sockdir = NULL;
224 int dpid_idle_timeout = 60 * 60; /* default, in seconds */
225 struct timeval select_timeout;
226 sigset_t mask_none;
227 fd_set selected_set;
228
229 dpi_attr_list = NULL;
230 /* daemon(0,0); */ /* Use 0,1 for feedback */
231 /* todo: call setsid() ?? */
232
233 /* Allow read and write access, but only for the user.
234 * todo: can this cause trouble with umount? */
235 umask(0077);
236 /* todo: make dpid work on any directory. */
237 /* chdir("/"); */
238
239 /* close inherited file descriptors */
240 open_max = get_open_max();
241 for (i = 3; i < open_max; i++)
242 a_Misc_close_fd(i);
243
244 /* this sleep used to unmask a race condition */
245 /* sleep(2); */
246
247 dpi_errno = no_errors;
248
249 /* Get list of available dpis */
250 numdpis = register_all(&dpi_attr_list);
251
252 /* Get name of socket directory */
253 dirname = a_Dpi_sockdir_file();
254 if ((sockdir = init_sockdir(dirname)) == NULL) {
255 ERRMSG("main", "init_sockdir", 0);
256 fprintf(stderr, "Failed to create socket directory\n");
257 exit(1);
258 }
259
260 /* Remove any sockets that may have been leftover from a crash */
261 cleanup(sockdir);
262 /* Initialise sockets */
263 if ((numsocks = init_srs_socket(sockdir)) == -1) {
264 switch (dpi_errno) {
265 case dpid_srs_addrinuse:
266 fprintf(stderr, "dpid refuses to start, possibly because:\n");
267 fprintf(stderr, "\t1) An instance of dpid is already running.\n");
268 fprintf(stderr, "\t2) A previous dpid didn't clean up on exit.\n");
269 exit(1);
270 default:
271 ERRMSG("main", "init_srs_sockets failed\n", 0);
272 exit(1);
273 }
274 }
275 numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir);
276 /* est_terminator(); Do we still want to clean up on an abnormal exit? */
277
278 est_dpi_sigchld();
279
280 (void) sigemptyset(&mask_sigchld);
281 (void) sigaddset(&mask_sigchld, SIGCHLD);
282 (void) sigemptyset(&mask_none);
283 (void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
284
285 printf("dpid started\n");
286 /* Start main loop */
287 while (1) {
288 do {
289 (void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
290 if (caught_sigchld) {
291 handle_sigchld();
292 caught_sigchld = 0;
293 }
294 (void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
295 select_timeout.tv_sec = dpid_idle_timeout;
296 select_timeout.tv_usec = 0;
297 selected_set = sock_set;
298 n = select(FD_SETSIZE, &selected_set, NULL, NULL, &select_timeout);
299 if ( n == 0 ) { /* select timed out, try to exit */
300 /* BUG: This is a workaround for dpid not to exit when the
301 * downloads server is active. The proper way to handle it is with
302 * a dpip command that asks the server whether it's busy.
303 * Note: the cookies server may lose session info too. */
304 if (server_is_running("downloads"))
305 continue;
306
307 stop_active_dpis(dpi_attr_list, numdpis);
308 cleanup(sockdir);
309 exit(0);
310 }
311 } while (n == -1 && errno == EINTR);
312
313 /* g_mem_profile(); */
314 if (n == -1) {
315 ERRMSG("main", "select", errno);
316 exit(1);
317 }
318 /* If the service req socket is selected then service the req. */
319 if (FD_ISSET(srs, &selected_set)) {
320 int sock;
321 socklen_t csz;
322 struct sockaddr_un clnt_addr;
323 char *req = NULL;
324
325 --n;
326 g_assert(n >= 0);
327 csz = (socklen_t) sizeof(clnt_addr);
328 sock = accept(srs, (struct sockaddr *) &clnt_addr, &csz);
329 if (sock == -1) {
330 ERRMSG("main", "accept", errno);
331 fprintf(stderr, "accept on srs socket failed\n");
332 fprintf(stderr, "service pending connections, and continue\n");
333 } else {
334 int command;
335
336 req = get_request(sock);
337 command = get_command(sock, req, dpi_attr_list);
338 switch (command) {
339 case BYE_CMD:
340 stop_active_dpis(dpi_attr_list, numdpis);
341 cleanup(sockdir);
342 exit(0);
343 break;
344 case CHECK_SERVER_CMD:
345 send_sockpath(sock, req, dpi_attr_list);
346 break;
347 case REGISTER_ALL_CMD:
348 register_all_cmd(sockdir);
349 break;
350 case UNKNOWN_CMD:
351 {
352 char *d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
353 "DpiError", "Unknown command");
354 (void) CKD_WRITE(sock, d_cmd);
355 g_free(d_cmd);
356 ERRMSG("main", "Unknown command", 0);
357 fprintf(stderr, " for request: %s\n", req);
358 break;
359 }
360 case -1:
361 ERRMSG("main", "get_command failed\n", 0);
362 break;
363 }
364 if (req)
365 free(req);
366 a_Misc_close_fd(sock);
367 }
368 }
369
370 /* While there's a request on one of the plugin sockets
371 * find the matching plugin and start it. */
372 for (i = 0; n > 0 && i < numdpis; i++) {
373 if (FD_ISSET(dpi_attr_list[i].socket, &selected_set)) {
374 --n;
375 g_assert(n >= 0);
376
377 if (dpi_attr_list[i].filter) {
378 /* start a dpi filter plugin and continue watching its socket
379 * for new connections */
380 (void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
381 start_filter_plugin(dpi_attr_list[i]);
382 } else {
383 /* start a dpi server plugin but don't wait for new connections
384 * on its socket */
385 numsocks--;
386 g_assert(numsocks >= 0);
387 FD_CLR(dpi_attr_list[i].socket, &sock_set);
388 if ((dpi_attr_list[i].pid = fork()) == -1) {
389 ERRMSG("main", "fork", errno);
390 /* exit(1); */
391 } else if (dpi_attr_list[i].pid == 0) {
392 (void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
393 start_server_plugin(dpi_attr_list[i]);
394 }
395 }
396 }
397 }
398 }
399 }
0 #include <stdio.h>
1 #include <time.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <glib.h>
10 #include "dpid_common.h"
11
12 #include "misc_new.h" /* for function prototypes */
13
14 /* define to 1 when checking for memory leaks
15 * \todo
16 * Eliminate need for a_Misc_get_home and a_Misc_get_user when testing for
17 * memory leaks by using ld --wrap option to replace g_get_home_dir and
18 * g_get_user_name with wrapper functions.
19 */
20 #ifndef TEST
21 #define TEST 0
22 #endif
23
24
25 /*
26 * Close a FD handling EINTR.
27 */
28 gint a_Misc_close_fd(gint fd)
29 {
30 gint st;
31
32 do {
33 st = close(fd);
34 } while (st < 0 && errno == EINTR);
35 return st;
36 }
37
38 /*
39 * Return the user's home directory.
40 * Don't free the returned string!
41 */
42 gchar *a_Misc_get_home(void)
43 {
44 gchar *ret;
45 ret = (TEST) ? getenv("HOME") : g_get_home_dir();
46 return ret;
47 }
48
49 /*
50 * Return the user.
51 * Don't free the returned string!
52 */
53 gchar *a_Misc_get_user(void)
54 {
55 gchar *ret;
56 ret = (TEST) ? getenv("USER") : g_get_user_name();
57 return ret;
58 }
59
60 /*
61 * Prepend the users home-dir to 'file' string i.e,
62 * pass in .dillo/bookmarks.html and it will return
63 * /home/imain/.dillo/bookmarks.html
64 *
65 * Remember to g_free() returned value!
66 * copied from misc.c
67 */
68 gchar *a_Misc_prepend_user_home(const char *file)
69 {
70 return (g_strconcat(a_Misc_get_home(), "/", file, NULL));
71 }
72
73 /*
74 * Read a line of text up to the newline character, store it into a newly
75 * allocated string and return it.
76 * (copied from dpi/bm_srv12.c)
77 */
78 char *a_Misc_get_line(FILE *stream)
79 {
80 guint i, size = 64;
81 int ch;
82 char *buf;
83
84 buf = g_new(char, size);
85
86 for (i = 0; (ch = fgetc(stream)) != EOF; ++i) {
87 if (i + 1 == size) {
88 size *= 2;
89 buf = g_realloc(buf, size);
90 }
91 if ((buf[i] = ch) == '\n' && ++i)
92 break;
93 }
94 buf[i] = 0;
95
96 if (i > 0) {
97 buf = g_realloc(buf, i + 1);
98 } else {
99 g_free(buf);
100 buf = NULL;
101 }
102 return buf;
103 }
104
105 /*! Reads a dpi tag from a socket
106 * \li Continues after a signal interrupt
107 * \Return
108 * Gstring pointer to tag on success, NULL on failure
109 * \important Caller is responsible for freeing the returned GString *
110 */
111 GString *a_Misc_rdtag(int socket)
112 {
113 char c = '\0';
114 ssize_t rdlen;
115 GString *tag;
116
117 tag = g_string_new(NULL);
118
119 errno = 0;
120
121 do {
122 rdlen = read(socket, &c, 1);
123 if (rdlen == -1 && errno != EINTR)
124 break;
125 g_string_append_c(tag, c);
126 } while (c != '>');
127
128 if (rdlen == -1) {
129 perror("a_Misc_rdtag");
130 g_string_free(tag, TRUE);
131 return (NULL);
132 }
133 return (tag);
134 }
135
136 /*!
137 * Read a dpi tag from sock
138 * \return
139 * pointer to dynamically allocated request tag
140 */
141 char *a_Misc_readtag(int sock)
142 {
143 char *tag, c, buf[10];
144 size_t buflen, i;
145 size_t taglen = 0, tagmem = 10;
146 ssize_t rdln = 1;
147
148 tag = NULL;
149 buf[0] = '\0';
150 buflen = sizeof(buf) / sizeof(buf[0]);
151 /* new start */
152 tag = (char *) g_malloc(tagmem + 1);
153 for (i = 0; (rdln = read(sock, &c, 1)) != 0; i++) {
154 if (i == tagmem) {
155 tagmem += tagmem;
156 tag = (char *) g_realloc(tag, tagmem + 1);
157 }
158 tag[i] = c;
159 taglen += rdln;
160 if (c == '>') {
161 tag[i + 1] = '\0';
162 break;
163 }
164 }
165 /* new end */
166 if (rdln == -1) {
167 ERRMSG("a_Misc_readtag", "read", errno);
168 }
169
170 return (tag);
171 }
172
173 /*! Reads a dpi tag from a socket without hanging on read.
174 * \li Continues after a signal interrupt
175 * \Return
176 * \li 1 on success
177 * \li 0 if input is not available within timeout microseconds.
178 * \li -1 on failure
179 * \important Caller is responsible for freeing the returned GString *
180 */
181 /* Is this useful?
182 int a_Misc_nohang_rdtag(int socket, int timeout, GString **tag)
183 {
184 int n_fd;
185 fd_set sock_set, select_set;
186 struct timeval tout;
187
188 FD_ZERO(&sock_set);
189 FD_SET(socket, &sock_set);
190
191 errno = 0;
192 do {
193 select_set = sock_set;
194 tout.tv_sec = 0;
195 tout.tv_usec = timeout;
196 n_fd = select(socket + 1, &select_set, NULL, NULL, &tout);
197 } while (n_fd == -1 && errno == EINTR);
198
199 if (n_fd == -1) {
200 fprintf(stderr, "%s:%d: a_Misc_nohang_rdtag: %s\n",
201 __FILE__, __LINE__, g_strerror(errno));
202 return(-1);
203 }
204 if (n_fd == 0) {
205 return(0);
206 } else {
207 *tag = a_Misc_rdtag(socket);
208 return(1);
209 }
210 }
211 */
212
213 /*
214 * Alternative to mkdtemp().
215 * Not as strong as mkdtemp, but enough for creating a directory.
216 * (adapted from dietlibc)
217 */
218 char *a_Misc_mkdtemp(char *template)
219 {
220 char *tmp = template + strlen(template) - 6;
221 int i;
222 unsigned int random;
223
224 if (tmp < template)
225 goto error;
226 for (i = 0; i < 6; ++i)
227 if (tmp[i] != 'X') {
228 error:
229 errno = EINVAL;
230 return 0;
231 }
232 srand((guint)(time(0) ^ getpid()));
233 for (;;) {
234 random = (unsigned) rand();
235 for (i = 0; i < 6; ++i) {
236 int hexdigit = (random >> (i * 5)) & 0x1f;
237
238 tmp[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
239 }
240 if (mkdir(template, 0700) == 0)
241 break;
242 if (errno == EEXIST)
243 continue;
244 return 0;
245 }
246 return template;
247 }
0 #ifndef MISC_NEW_H
1 #define MISC_NEW_H
2
3 #include <glib.h>
4
5 gint a_Misc_close_fd(gint fd);
6
7 gchar *a_Misc_get_home(void);
8
9 gchar *a_Misc_get_user(void);
10
11 gchar *a_Misc_prepend_user_home(const char *file);
12
13 char *a_Misc_get_line(FILE *stream);
14
15 GString *a_Misc_rdtag(int socket);
16
17 char *a_Misc_readtag(int sock);
18
19 char *a_Misc_mkdtemp(char *template);
20
21 #endif
0 AM_CFLAGS = @GLIB_CFLAGS@
1 AM_LIBS = @GLIB_LIBS@
2
3 noinst_LIBRARIES = libDpip.a
4
5 libDpip_a_SOURCES = \
6 dpip.h \
7 dpip.c
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(libDpip_a_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 subdir = dpip
42 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
43 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
44 am__aclocal_m4_deps = $(top_srcdir)/configure.in
45 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
46 $(ACLOCAL_M4)
47 mkinstalldirs = $(install_sh) -d
48 CONFIG_HEADER = $(top_builddir)/config.h
49 CONFIG_CLEAN_FILES =
50 LIBRARIES = $(noinst_LIBRARIES)
51 AR = ar
52 ARFLAGS = cru
53 libDpip_a_AR = $(AR) $(ARFLAGS)
54 libDpip_a_LIBADD =
55 am_libDpip_a_OBJECTS = dpip.$(OBJEXT)
56 libDpip_a_OBJECTS = $(am_libDpip_a_OBJECTS)
57 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
58 depcomp = $(SHELL) $(top_srcdir)/depcomp
59 am__depfiles_maybe = depfiles
60 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
61 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
62 CCLD = $(CC)
63 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
64 SOURCES = $(libDpip_a_SOURCES)
65 DIST_SOURCES = $(libDpip_a_SOURCES)
66 ETAGS = etags
67 CTAGS = ctags
68 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
69 ACLOCAL = @ACLOCAL@
70 AMDEP_FALSE = @AMDEP_FALSE@
71 AMDEP_TRUE = @AMDEP_TRUE@
72 AMTAR = @AMTAR@
73 AUTOCONF = @AUTOCONF@
74 AUTOHEADER = @AUTOHEADER@
75 AUTOMAKE = @AUTOMAKE@
76 AWK = @AWK@
77 CC = @CC@
78 CCDEPMODE = @CCDEPMODE@
79 CFLAGS = @CFLAGS@
80 CPP = @CPP@
81 CPPFLAGS = @CPPFLAGS@
82 CXX = @CXX@
83 CXXDEPMODE = @CXXDEPMODE@
84 CXXFLAGS = @CXXFLAGS@
85 CYGPATH_W = @CYGPATH_W@
86 DEFS = @DEFS@
87 DEPDIR = @DEPDIR@
88 DLGUI_FALSE = @DLGUI_FALSE@
89 DLGUI_TRUE = @DLGUI_TRUE@
90 ECHO_C = @ECHO_C@
91 ECHO_N = @ECHO_N@
92 ECHO_T = @ECHO_T@
93 EGREP = @EGREP@
94 EXEEXT = @EXEEXT@
95 GLIB_CFLAGS = @GLIB_CFLAGS@
96 GLIB_CONFIG = @GLIB_CONFIG@
97 GLIB_LIBS = @GLIB_LIBS@
98 GTK_CFLAGS = @GTK_CFLAGS@
99 GTK_CONFIG = @GTK_CONFIG@
100 GTK_LIBS = @GTK_LIBS@
101 INSTALL_DATA = @INSTALL_DATA@
102 INSTALL_PROGRAM = @INSTALL_PROGRAM@
103 INSTALL_SCRIPT = @INSTALL_SCRIPT@
104 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
105 LDFLAGS = @LDFLAGS@
106 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
107 LIBFLTK_LIBS = @LIBFLTK_LIBS@
108 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
109 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
110 LIBJPEG_LIBS = @LIBJPEG_LIBS@
111 LIBOBJS = @LIBOBJS@
112 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
113 LIBPNG_LIBS = @LIBPNG_LIBS@
114 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
115 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
116 LIBS = @LIBS@
117 LIBSSL_LIBS = @LIBSSL_LIBS@
118 LIBZ_LIBS = @LIBZ_LIBS@
119 LTLIBOBJS = @LTLIBOBJS@
120 MAKEINFO = @MAKEINFO@
121 OBJEXT = @OBJEXT@
122 PACKAGE = @PACKAGE@
123 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
124 PACKAGE_NAME = @PACKAGE_NAME@
125 PACKAGE_STRING = @PACKAGE_STRING@
126 PACKAGE_TARNAME = @PACKAGE_TARNAME@
127 PACKAGE_VERSION = @PACKAGE_VERSION@
128 PATH_SEPARATOR = @PATH_SEPARATOR@
129 RANLIB = @RANLIB@
130 SET_MAKE = @SET_MAKE@
131 SHELL = @SHELL@
132 STRIP = @STRIP@
133 VERSION = @VERSION@
134 ac_ct_CC = @ac_ct_CC@
135 ac_ct_CXX = @ac_ct_CXX@
136 ac_ct_RANLIB = @ac_ct_RANLIB@
137 ac_ct_STRIP = @ac_ct_STRIP@
138 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
139 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
140 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
141 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
142 am__include = @am__include@
143 am__leading_dot = @am__leading_dot@
144 am__quote = @am__quote@
145 am__tar = @am__tar@
146 am__untar = @am__untar@
147 bindir = @bindir@
148 build = @build@
149 build_alias = @build_alias@
150 build_cpu = @build_cpu@
151 build_os = @build_os@
152 build_vendor = @build_vendor@
153 datadir = @datadir@
154 exec_prefix = @exec_prefix@
155 host = @host@
156 host_alias = @host_alias@
157 host_cpu = @host_cpu@
158 host_os = @host_os@
159 host_vendor = @host_vendor@
160 includedir = @includedir@
161 infodir = @infodir@
162 install_sh = @install_sh@
163 libdir = @libdir@
164 libexecdir = @libexecdir@
165 localstatedir = @localstatedir@
166 mandir = @mandir@
167 mkdir_p = @mkdir_p@
168 oldincludedir = @oldincludedir@
169 prefix = @prefix@
170 program_transform_name = @program_transform_name@
171 sbindir = @sbindir@
172 sharedstatedir = @sharedstatedir@
173 sysconfdir = @sysconfdir@
174 target = @target@
175 target_alias = @target_alias@
176 target_cpu = @target_cpu@
177 target_os = @target_os@
178 target_vendor = @target_vendor@
179 AM_CFLAGS = @GLIB_CFLAGS@
180 AM_LIBS = @GLIB_LIBS@
181 noinst_LIBRARIES = libDpip.a
182 libDpip_a_SOURCES = \
183 dpip.h \
184 dpip.c
185
186 all: all-am
187
188 .SUFFIXES:
189 .SUFFIXES: .c .o .obj
190 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
191 @for dep in $?; do \
192 case '$(am__configure_deps)' in \
193 *$$dep*) \
194 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
195 && exit 0; \
196 exit 1;; \
197 esac; \
198 done; \
199 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dpip/Makefile'; \
200 cd $(top_srcdir) && \
201 $(AUTOMAKE) --gnu dpip/Makefile
202 .PRECIOUS: Makefile
203 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
204 @case '$?' in \
205 *config.status*) \
206 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
207 *) \
208 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
209 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
210 esac;
211
212 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
213 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
214
215 $(top_srcdir)/configure: $(am__configure_deps)
216 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
217 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
218 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
219
220 clean-noinstLIBRARIES:
221 -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
222 libDpip.a: $(libDpip_a_OBJECTS) $(libDpip_a_DEPENDENCIES)
223 -rm -f libDpip.a
224 $(libDpip_a_AR) libDpip.a $(libDpip_a_OBJECTS) $(libDpip_a_LIBADD)
225 $(RANLIB) libDpip.a
226
227 mostlyclean-compile:
228 -rm -f *.$(OBJEXT)
229
230 distclean-compile:
231 -rm -f *.tab.c
232
233 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpip.Po@am__quote@
234
235 .c.o:
236 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
237 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
238 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
239 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
240 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
241
242 .c.obj:
243 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
244 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
245 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
246 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
247 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
248 uninstall-info-am:
249
250 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
251 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
252 unique=`for i in $$list; do \
253 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
254 done | \
255 $(AWK) ' { files[$$0] = 1; } \
256 END { for (i in files) print i; }'`; \
257 mkid -fID $$unique
258 tags: TAGS
259
260 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
261 $(TAGS_FILES) $(LISP)
262 tags=; \
263 here=`pwd`; \
264 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
265 unique=`for i in $$list; do \
266 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
267 done | \
268 $(AWK) ' { files[$$0] = 1; } \
269 END { for (i in files) print i; }'`; \
270 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
271 test -n "$$unique" || unique=$$empty_fix; \
272 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
273 $$tags $$unique; \
274 fi
275 ctags: CTAGS
276 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
277 $(TAGS_FILES) $(LISP)
278 tags=; \
279 here=`pwd`; \
280 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
281 unique=`for i in $$list; do \
282 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
283 done | \
284 $(AWK) ' { files[$$0] = 1; } \
285 END { for (i in files) print i; }'`; \
286 test -z "$(CTAGS_ARGS)$$tags$$unique" \
287 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
288 $$tags $$unique
289
290 GTAGS:
291 here=`$(am__cd) $(top_builddir) && pwd` \
292 && cd $(top_srcdir) \
293 && gtags -i $(GTAGS_ARGS) $$here
294
295 distclean-tags:
296 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
297
298 distdir: $(DISTFILES)
299 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
300 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
301 list='$(DISTFILES)'; for file in $$list; do \
302 case $$file in \
303 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
304 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
305 esac; \
306 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
307 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
308 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
309 dir="/$$dir"; \
310 $(mkdir_p) "$(distdir)$$dir"; \
311 else \
312 dir=''; \
313 fi; \
314 if test -d $$d/$$file; then \
315 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
316 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
317 fi; \
318 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
319 else \
320 test -f $(distdir)/$$file \
321 || cp -p $$d/$$file $(distdir)/$$file \
322 || exit 1; \
323 fi; \
324 done
325 check-am: all-am
326 check: check-am
327 all-am: Makefile $(LIBRARIES)
328 installdirs:
329 install: install-am
330 install-exec: install-exec-am
331 install-data: install-data-am
332 uninstall: uninstall-am
333
334 install-am: all-am
335 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
336
337 installcheck: installcheck-am
338 install-strip:
339 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
340 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
341 `test -z '$(STRIP)' || \
342 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
343 mostlyclean-generic:
344
345 clean-generic:
346
347 distclean-generic:
348 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
349
350 maintainer-clean-generic:
351 @echo "This command is intended for maintainers to use"
352 @echo "it deletes files that may require special tools to rebuild."
353 clean: clean-am
354
355 clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
356
357 distclean: distclean-am
358 -rm -rf ./$(DEPDIR)
359 -rm -f Makefile
360 distclean-am: clean-am distclean-compile distclean-generic \
361 distclean-tags
362
363 dvi: dvi-am
364
365 dvi-am:
366
367 html: html-am
368
369 info: info-am
370
371 info-am:
372
373 install-data-am:
374
375 install-exec-am:
376
377 install-info: install-info-am
378
379 install-man:
380
381 installcheck-am:
382
383 maintainer-clean: maintainer-clean-am
384 -rm -rf ./$(DEPDIR)
385 -rm -f Makefile
386 maintainer-clean-am: distclean-am maintainer-clean-generic
387
388 mostlyclean: mostlyclean-am
389
390 mostlyclean-am: mostlyclean-compile mostlyclean-generic
391
392 pdf: pdf-am
393
394 pdf-am:
395
396 ps: ps-am
397
398 ps-am:
399
400 uninstall-am: uninstall-info-am
401
402 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
403 clean-noinstLIBRARIES ctags distclean distclean-compile \
404 distclean-generic distclean-tags distdir dvi dvi-am html \
405 html-am info info-am install install-am install-data \
406 install-data-am install-exec install-exec-am install-info \
407 install-info-am install-man install-strip installcheck \
408 installcheck-am installdirs maintainer-clean \
409 maintainer-clean-generic mostlyclean mostlyclean-compile \
410 mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
411 uninstall-am uninstall-info-am
412
413 # Tell versions [3.59,3.63) of GNU make to not export all variables.
414 # Otherwise a system limit (for SysV at least) may be exceeded.
415 .NOEXPORT:
0 /*
1 * File: dpip.c
2 *
3 * Copyright 2005 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 */
11
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <glib.h>
16
17 #include "dpip.h"
18
19 static char Quote = '\'';
20
21 /*
22 * Basically the syntax of a dpip tag is:
23 *
24 * "<"[*alpha] *(<name>"="Quote<escaped_value>Quote) " "Quote">"
25 *
26 * 1.- No space is allowed around the "=" sign between a name and its value.
27 * 2.- The Quote character is not allowed in <name>.
28 * 3.- Attribute values stuff Quote as QuoteQuote.
29 *
30 * e.g. (with ' as Quote):
31 *
32 * <a='b' b='c' '> OK
33 * <dpi a='b i' b='12' '> OK
34 * <a='>' '> OK
35 * <a='ain''t no doubt' '> OK
36 * <a='ain''t b=''no'' b='' doubt' '> OK
37 * <a = '>' '> Wrong
38 *
39 * Notes:
40 *
41 * Restriction #1 is for easy finding of end of tag (EOT=Space+Quote+>).
42 * Restriction #2 can be removed, but what for? ;)
43 * The functions here provide for this functionality.
44 */
45
46 typedef enum {
47 SEEK_NAME,
48 MATCH_NAME,
49 SKIP_VALUE,
50 SKIP_QUOTE,
51 FOUND
52 } DpipTagParsingState;
53
54 /* ------------------------------------------------------------------------- */
55
56 /*
57 * Printf like function for building dpip commands.
58 * It takes care of dpip escaping of its arguments.
59 * NOTE : It ONLY accepts string parameters, and
60 * only one %s per parameter.
61 */
62 char *a_Dpip_build_cmd(const char *format, ...)
63 {
64 va_list argp;
65 char *p, *q, *s;
66 GString *cmd;
67
68 /* Don't allow Quote characters in attribute names */
69 if (strchr(format, Quote))
70 return NULL;
71
72 cmd = g_string_sized_new(64);
73 g_string_append_c(cmd, '<');
74 va_start(argp, format);
75 for (p = q = (char*)format; *q; ) {
76 p = strstr(q, "%s");
77 if (!p) {
78 g_string_append(cmd, q);
79 break;
80 } else {
81 /* Copy format's part */
82 while (q != p)
83 g_string_append_c(cmd, *q++);
84 q += 2;
85
86 g_string_append_c(cmd, Quote);
87 /* Stuff-copy of argument */
88 s = va_arg (argp, char *);
89 for ( ; *s; ++s) {
90 g_string_append_c(cmd, *s);
91 if (*s == Quote)
92 g_string_append_c(cmd, *s);
93 }
94 g_string_append_c(cmd, Quote);
95 }
96 }
97 va_end(argp);
98 g_string_append_c(cmd, ' ');
99 g_string_append_c(cmd, Quote);
100 g_string_append_c(cmd, '>');
101
102 p = cmd->str;
103 g_string_free(cmd, FALSE);
104 return p;
105 }
106
107 /*
108 * Task: given a tag and an attribute name, return its value.
109 * (stuffing of ' is removed here)
110 * Return value: the attribute value, or NULL if not present or malformed.
111 */
112 char *a_Dpip_get_attr(char *tag, size_t tagsize, char *attrname)
113 {
114 guint i, n = 0, found = 0;
115 char *p, *q, *start, *val = NULL;
116 DpipTagParsingState state = SEEK_NAME;
117
118 if (!attrname || !*attrname)
119 return NULL;
120
121 for (i = 1; i < tagsize && !found; ++i) {
122 switch (state) {
123 case SEEK_NAME:
124 if (tag[i] == attrname[0] && (tag[i-1] == ' ' || tag[i-1] == '<')) {
125 n = 1;
126 state = MATCH_NAME;
127 } else if (tag[i] == Quote && tag[i-1] == '=')
128 state = SKIP_VALUE;
129 break;
130 case MATCH_NAME:
131 if (tag[i] == attrname[n])
132 ++n;
133 else if (tag[i] == '=' && !attrname[n])
134 state = FOUND;
135 else
136 state = SEEK_NAME;
137 break;
138 case SKIP_VALUE:
139 if (tag[i] == Quote)
140 state = (tag[i+1] == Quote) ? SKIP_QUOTE : SEEK_NAME;
141 break;
142 case SKIP_QUOTE:
143 state = SKIP_VALUE;
144 break;
145 case FOUND:
146 found = 1;
147 break;
148 }
149 }
150
151 if (found) {
152 p = start = tag + i;
153 while ((q = strchr(p, Quote)) && q[1] == Quote)
154 p = q + 2;
155 if (q && q[1] == ' ') {
156 val = g_strndup(start, (guint)(q - start));
157 for (p = q = val; (*q = *p); ++p, ++q)
158 if (*p == Quote && p[1] == p[0])
159 ++p;
160 }
161 }
162 return val;
163 }
164
165 /* ------------------------------------------------------------------------- */
166
0 /*
1 * Library for dealing with dpip tags (dillo plugin protocol tags).
2 */
3
4 #ifndef __DPIP_H__
5 #define __DPIP_H__
6
7 #ifdef __cplusplus
8 extern "C" {
9 #endif /* __cplusplus */
10
11
12 /*
13 * Printf like function for building dpip commands.
14 * It takes care of dpip escaping of its arguments.
15 * NOTE : It ONLY accepts string parameters, and
16 * only one %s per parameter.
17 */
18 char *a_Dpip_build_cmd(const char *format, ...);
19
20 /*
21 * Task: given a tag and an attribute name, return its value.
22 * (dpip character escaping is removed here)
23 * Return value: the attribute value, or NULL if not present or malformed.
24 */
25 char *a_Dpip_get_attr(char *tag, size_t tagsize, char *attrname);
26
27
28 #ifdef __cplusplus
29 }
30 #endif /* __cplusplus */
31
32 #endif /* __DPIP_H__ */
33
0 #!/bin/sh
1 #
2 # Install the dpi framework programs inside the user's account.
3 #
4
5 BASE="$HOME/.dillo"
6 BASE2="$BASE/dpi"
7
8 if [ -r $BASE/dpi_socket_dir ] ; then
9 rm -r `cat $BASE/dpi_socket_dir`
10 rm $BASE/dpi_socket_dir
11 fi
12
13 if [ ! -x dpid/dpid ] ; then
14 echo "You MUST run this script after make."
15 exit 1
16 fi
17
18 if [ ! -d $BASE ] ; then
19 mkdir $BASE
20 fi
21 if [ ! -d $BASE2 ] ; then
22 mkdir $BASE2
23 fi
24
25 cp dpid/dpid dpid/dpidc $BASE
26 strip $BASE/dpid
27
28 cd dpi
29 for F in *.dpi ; do
30 D="`echo $F | sed 's/\..*$//'`"
31 if [ ! -d $BASE2/$D ] ; then
32 mkdir $BASE2/$D
33 fi
34 cp $F $BASE2/$D
35 strip $BASE2/$D/$F
36 done
37 cd ..
38
0 #!/bin/sh
1 # install - install a program, script, or datafile
2
3 scriptversion=2005-02-02.21
4
5 # This originates from X11R5 (mit/util/scripts/install.sh), which was
6 # later released in X11R6 (xc/config/util/install.sh) with the
7 # following copyright and license.
8 #
9 # Copyright (C) 1994 X Consortium
10 #
11 # Permission is hereby granted, free of charge, to any person obtaining a copy
12 # of this software and associated documentation files (the "Software"), to
13 # deal in the Software without restriction, including without limitation the
14 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
15 # sell copies of the Software, and to permit persons to whom the Software is
16 # furnished to do so, subject to the following conditions:
17 #
18 # The above copyright notice and this permission notice shall be included in
19 # all copies or substantial portions of the Software.
20 #
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
25 # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
26 # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #
28 # Except as contained in this notice, the name of the X Consortium shall not
29 # be used in advertising or otherwise to promote the sale, use or other deal-
30 # ings in this Software without prior written authorization from the X Consor-
31 # tium.
32 #
33 #
34 # FSF changes to this file are in the public domain.
35 #
36 # Calling this script install-sh is preferred over install.sh, to prevent
37 # `make' implicit rules from creating a file called install from it
38 # when there is no Makefile.
39 #
40 # This script is compatible with the BSD install script, but was written
41 # from scratch. It can only install one file at a time, a restriction
42 # shared with many OS's install programs.
43
44 # set DOITPROG to echo to test this script
45
46 # Don't use :- since 4.3BSD and earlier shells don't like it.
47 doit="${DOITPROG-}"
48
49 # put in absolute paths if you don't have them in your path; or use env. vars.
50
51 mvprog="${MVPROG-mv}"
52 cpprog="${CPPROG-cp}"
53 chmodprog="${CHMODPROG-chmod}"
54 chownprog="${CHOWNPROG-chown}"
55 chgrpprog="${CHGRPPROG-chgrp}"
56 stripprog="${STRIPPROG-strip}"
57 rmprog="${RMPROG-rm}"
58 mkdirprog="${MKDIRPROG-mkdir}"
59
60 chmodcmd="$chmodprog 0755"
61 chowncmd=
62 chgrpcmd=
63 stripcmd=
64 rmcmd="$rmprog -f"
65 mvcmd="$mvprog"
66 src=
67 dst=
68 dir_arg=
69 dstarg=
70 no_target_directory=
71
72 usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
73 or: $0 [OPTION]... SRCFILES... DIRECTORY
74 or: $0 [OPTION]... -t DIRECTORY SRCFILES...
75 or: $0 [OPTION]... -d DIRECTORIES...
76
77 In the 1st form, copy SRCFILE to DSTFILE.
78 In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
79 In the 4th, create DIRECTORIES.
80
81 Options:
82 -c (ignored)
83 -d create directories instead of installing files.
84 -g GROUP $chgrpprog installed files to GROUP.
85 -m MODE $chmodprog installed files to MODE.
86 -o USER $chownprog installed files to USER.
87 -s $stripprog installed files.
88 -t DIRECTORY install into DIRECTORY.
89 -T report an error if DSTFILE is a directory.
90 --help display this help and exit.
91 --version display version info and exit.
92
93 Environment variables override the default commands:
94 CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
95 "
96
97 while test -n "$1"; do
98 case $1 in
99 -c) shift
100 continue;;
101
102 -d) dir_arg=true
103 shift
104 continue;;
105
106 -g) chgrpcmd="$chgrpprog $2"
107 shift
108 shift
109 continue;;
110
111 --help) echo "$usage"; exit $?;;
112
113 -m) chmodcmd="$chmodprog $2"
114 shift
115 shift
116 continue;;
117
118 -o) chowncmd="$chownprog $2"
119 shift
120 shift
121 continue;;
122
123 -s) stripcmd=$stripprog
124 shift
125 continue;;
126
127 -t) dstarg=$2
128 shift
129 shift
130 continue;;
131
132 -T) no_target_directory=true
133 shift
134 continue;;
135
136 --version) echo "$0 $scriptversion"; exit $?;;
137
138 *) # When -d is used, all remaining arguments are directories to create.
139 # When -t is used, the destination is already specified.
140 test -n "$dir_arg$dstarg" && break
141 # Otherwise, the last argument is the destination. Remove it from $@.
142 for arg
143 do
144 if test -n "$dstarg"; then
145 # $@ is not empty: it contains at least $arg.
146 set fnord "$@" "$dstarg"
147 shift # fnord
148 fi
149 shift # arg
150 dstarg=$arg
151 done
152 break;;
153 esac
154 done
155
156 if test -z "$1"; then
157 if test -z "$dir_arg"; then
158 echo "$0: no input file specified." >&2
159 exit 1
160 fi
161 # It's OK to call `install-sh -d' without argument.
162 # This can happen when creating conditional directories.
163 exit 0
164 fi
165
166 for src
167 do
168 # Protect names starting with `-'.
169 case $src in
170 -*) src=./$src ;;
171 esac
172
173 if test -n "$dir_arg"; then
174 dst=$src
175 src=
176
177 if test -d "$dst"; then
178 mkdircmd=:
179 chmodcmd=
180 else
181 mkdircmd=$mkdirprog
182 fi
183 else
184 # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
185 # might cause directories to be created, which would be especially bad
186 # if $src (and thus $dsttmp) contains '*'.
187 if test ! -f "$src" && test ! -d "$src"; then
188 echo "$0: $src does not exist." >&2
189 exit 1
190 fi
191
192 if test -z "$dstarg"; then
193 echo "$0: no destination specified." >&2
194 exit 1
195 fi
196
197 dst=$dstarg
198 # Protect names starting with `-'.
199 case $dst in
200 -*) dst=./$dst ;;
201 esac
202
203 # If destination is a directory, append the input filename; won't work
204 # if double slashes aren't ignored.
205 if test -d "$dst"; then
206 if test -n "$no_target_directory"; then
207 echo "$0: $dstarg: Is a directory" >&2
208 exit 1
209 fi
210 dst=$dst/`basename "$src"`
211 fi
212 fi
213
214 # This sed command emulates the dirname command.
215 dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
216
217 # Make sure that the destination directory exists.
218
219 # Skip lots of stat calls in the usual case.
220 if test ! -d "$dstdir"; then
221 defaultIFS='
222 '
223 IFS="${IFS-$defaultIFS}"
224
225 oIFS=$IFS
226 # Some sh's can't handle IFS=/ for some reason.
227 IFS='%'
228 set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
229 shift
230 IFS=$oIFS
231
232 pathcomp=
233
234 while test $# -ne 0 ; do
235 pathcomp=$pathcomp$1
236 shift
237 if test ! -d "$pathcomp"; then
238 $mkdirprog "$pathcomp"
239 # mkdir can fail with a `File exist' error in case several
240 # install-sh are creating the directory concurrently. This
241 # is OK.
242 test -d "$pathcomp" || exit
243 fi
244 pathcomp=$pathcomp/
245 done
246 fi
247
248 if test -n "$dir_arg"; then
249 $doit $mkdircmd "$dst" \
250 && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
251 && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
252 && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
253 && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
254
255 else
256 dstfile=`basename "$dst"`
257
258 # Make a couple of temp file names in the proper directory.
259 dsttmp=$dstdir/_inst.$$_
260 rmtmp=$dstdir/_rm.$$_
261
262 # Trap to clean up those temp files at exit.
263 trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
264 trap '(exit $?); exit' 1 2 13 15
265
266 # Copy the file name to the temp name.
267 $doit $cpprog "$src" "$dsttmp" &&
268
269 # and set any options; do chmod last to preserve setuid bits.
270 #
271 # If any of these fail, we abort the whole thing. If we want to
272 # ignore errors from any of these, just make sure not to ignore
273 # errors from the above "$doit $cpprog $src $dsttmp" command.
274 #
275 { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
276 && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
277 && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
278 && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
279
280 # Now rename the file to the real destination.
281 { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
282 || {
283 # The rename failed, perhaps because mv can't rename something else
284 # to itself, or perhaps because mv is so ancient that it does not
285 # support -f.
286
287 # Now remove or move aside any old file at destination location.
288 # We try this two ways since rm can't unlink itself on some
289 # systems and the destination file might be busy for other
290 # reasons. In this case, the final cleanup might fail but the new
291 # file should still install successfully.
292 {
293 if test -f "$dstdir/$dstfile"; then
294 $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
295 || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
296 || {
297 echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
298 (exit 1); exit 1
299 }
300 else
301 :
302 fi
303 } &&
304
305 # Now rename the file to the real destination.
306 $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
307 }
308 }
309 fi || { (exit 1); exit 1; }
310 done
311
312 # The final little trick to "correctly" pass the exit status to the exit trap.
313 {
314 (exit 0); exit 0
315 }
316
317 # Local variables:
318 # eval: (add-hook 'write-file-hooks 'time-stamp)
319 # time-stamp-start: "scriptversion="
320 # time-stamp-format: "%:y-%02m-%02d.%02H"
321 # time-stamp-end: "$"
322 # End:
0 #! /bin/sh
1 # Common stub for a few missing GNU programs while installing.
2
3 scriptversion=2005-02-08.22
4
5 # Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005
6 # Free Software Foundation, Inc.
7 # Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
8
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2, or (at your option)
12 # any later version.
13
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 # 02111-1307, USA.
23
24 # As a special exception to the GNU General Public License, if you
25 # distribute this file as part of a program that contains a
26 # configuration script generated by Autoconf, you may include it under
27 # the same distribution terms that you use for the rest of that program.
28
29 if test $# -eq 0; then
30 echo 1>&2 "Try \`$0 --help' for more information"
31 exit 1
32 fi
33
34 run=:
35
36 # In the cases where this matters, `missing' is being run in the
37 # srcdir already.
38 if test -f configure.ac; then
39 configure_ac=configure.ac
40 else
41 configure_ac=configure.in
42 fi
43
44 msg="missing on your system"
45
46 case "$1" in
47 --run)
48 # Try to run requested program, and just exit if it succeeds.
49 run=
50 shift
51 "$@" && exit 0
52 # Exit code 63 means version mismatch. This often happens
53 # when the user try to use an ancient version of a tool on
54 # a file that requires a minimum version. In this case we
55 # we should proceed has if the program had been absent, or
56 # if --run hadn't been passed.
57 if test $? = 63; then
58 run=:
59 msg="probably too old"
60 fi
61 ;;
62
63 -h|--h|--he|--hel|--help)
64 echo "\
65 $0 [OPTION]... PROGRAM [ARGUMENT]...
66
67 Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
68 error status if there is no known handling for PROGRAM.
69
70 Options:
71 -h, --help display this help and exit
72 -v, --version output version information and exit
73 --run try to run the given command, and emulate it if it fails
74
75 Supported PROGRAM values:
76 aclocal touch file \`aclocal.m4'
77 autoconf touch file \`configure'
78 autoheader touch file \`config.h.in'
79 automake touch all \`Makefile.in' files
80 bison create \`y.tab.[ch]', if possible, from existing .[ch]
81 flex create \`lex.yy.c', if possible, from existing .c
82 help2man touch the output file
83 lex create \`lex.yy.c', if possible, from existing .c
84 makeinfo touch the output file
85 tar try tar, gnutar, gtar, then tar without non-portable flags
86 yacc create \`y.tab.[ch]', if possible, from existing .[ch]
87
88 Send bug reports to <bug-automake@gnu.org>."
89 exit $?
90 ;;
91
92 -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
93 echo "missing $scriptversion (GNU Automake)"
94 exit $?
95 ;;
96
97 -*)
98 echo 1>&2 "$0: Unknown \`$1' option"
99 echo 1>&2 "Try \`$0 --help' for more information"
100 exit 1
101 ;;
102
103 esac
104
105 # Now exit if we have it, but it failed. Also exit now if we
106 # don't have it and --version was passed (most likely to detect
107 # the program).
108 case "$1" in
109 lex|yacc)
110 # Not GNU programs, they don't have --version.
111 ;;
112
113 tar)
114 if test -n "$run"; then
115 echo 1>&2 "ERROR: \`tar' requires --run"
116 exit 1
117 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
118 exit 1
119 fi
120 ;;
121
122 *)
123 if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
124 # We have it, but it failed.
125 exit 1
126 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
127 # Could not run --version or --help. This is probably someone
128 # running `$TOOL --version' or `$TOOL --help' to check whether
129 # $TOOL exists and not knowing $TOOL uses missing.
130 exit 1
131 fi
132 ;;
133 esac
134
135 # If it does not exist, or fails to run (possibly an outdated version),
136 # try to emulate it.
137 case "$1" in
138 aclocal*)
139 echo 1>&2 "\
140 WARNING: \`$1' is $msg. You should only need it if
141 you modified \`acinclude.m4' or \`${configure_ac}'. You might want
142 to install the \`Automake' and \`Perl' packages. Grab them from
143 any GNU archive site."
144 touch aclocal.m4
145 ;;
146
147 autoconf)
148 echo 1>&2 "\
149 WARNING: \`$1' is $msg. You should only need it if
150 you modified \`${configure_ac}'. You might want to install the
151 \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
152 archive site."
153 touch configure
154 ;;
155
156 autoheader)
157 echo 1>&2 "\
158 WARNING: \`$1' is $msg. You should only need it if
159 you modified \`acconfig.h' or \`${configure_ac}'. You might want
160 to install the \`Autoconf' and \`GNU m4' packages. Grab them
161 from any GNU archive site."
162 files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
163 test -z "$files" && files="config.h"
164 touch_files=
165 for f in $files; do
166 case "$f" in
167 *:*) touch_files="$touch_files "`echo "$f" |
168 sed -e 's/^[^:]*://' -e 's/:.*//'`;;
169 *) touch_files="$touch_files $f.in";;
170 esac
171 done
172 touch $touch_files
173 ;;
174
175 automake*)
176 echo 1>&2 "\
177 WARNING: \`$1' is $msg. You should only need it if
178 you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
179 You might want to install the \`Automake' and \`Perl' packages.
180 Grab them from any GNU archive site."
181 find . -type f -name Makefile.am -print |
182 sed 's/\.am$/.in/' |
183 while read f; do touch "$f"; done
184 ;;
185
186 autom4te)
187 echo 1>&2 "\
188 WARNING: \`$1' is needed, but is $msg.
189 You might have modified some files without having the
190 proper tools for further handling them.
191 You can get \`$1' as part of \`Autoconf' from any GNU
192 archive site."
193
194 file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
195 test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
196 if test -f "$file"; then
197 touch $file
198 else
199 test -z "$file" || exec >$file
200 echo "#! /bin/sh"
201 echo "# Created by GNU Automake missing as a replacement of"
202 echo "# $ $@"
203 echo "exit 0"
204 chmod +x $file
205 exit 1
206 fi
207 ;;
208
209 bison|yacc)
210 echo 1>&2 "\
211 WARNING: \`$1' $msg. You should only need it if
212 you modified a \`.y' file. You may need the \`Bison' package
213 in order for those modifications to take effect. You can get
214 \`Bison' from any GNU archive site."
215 rm -f y.tab.c y.tab.h
216 if [ $# -ne 1 ]; then
217 eval LASTARG="\${$#}"
218 case "$LASTARG" in
219 *.y)
220 SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
221 if [ -f "$SRCFILE" ]; then
222 cp "$SRCFILE" y.tab.c
223 fi
224 SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
225 if [ -f "$SRCFILE" ]; then
226 cp "$SRCFILE" y.tab.h
227 fi
228 ;;
229 esac
230 fi
231 if [ ! -f y.tab.h ]; then
232 echo >y.tab.h
233 fi
234 if [ ! -f y.tab.c ]; then
235 echo 'main() { return 0; }' >y.tab.c
236 fi
237 ;;
238
239 lex|flex)
240 echo 1>&2 "\
241 WARNING: \`$1' is $msg. You should only need it if
242 you modified a \`.l' file. You may need the \`Flex' package
243 in order for those modifications to take effect. You can get
244 \`Flex' from any GNU archive site."
245 rm -f lex.yy.c
246 if [ $# -ne 1 ]; then
247 eval LASTARG="\${$#}"
248 case "$LASTARG" in
249 *.l)
250 SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
251 if [ -f "$SRCFILE" ]; then
252 cp "$SRCFILE" lex.yy.c
253 fi
254 ;;
255 esac
256 fi
257 if [ ! -f lex.yy.c ]; then
258 echo 'main() { return 0; }' >lex.yy.c
259 fi
260 ;;
261
262 help2man)
263 echo 1>&2 "\
264 WARNING: \`$1' is $msg. You should only need it if
265 you modified a dependency of a manual page. You may need the
266 \`Help2man' package in order for those modifications to take
267 effect. You can get \`Help2man' from any GNU archive site."
268
269 file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
270 if test -z "$file"; then
271 file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
272 fi
273 if [ -f "$file" ]; then
274 touch $file
275 else
276 test -z "$file" || exec >$file
277 echo ".ab help2man is required to generate this page"
278 exit 1
279 fi
280 ;;
281
282 makeinfo)
283 echo 1>&2 "\
284 WARNING: \`$1' is $msg. You should only need it if
285 you modified a \`.texi' or \`.texinfo' file, or any other file
286 indirectly affecting the aspect of the manual. The spurious
287 call might also be the consequence of using a buggy \`make' (AIX,
288 DU, IRIX). You might want to install the \`Texinfo' package or
289 the \`GNU make' package. Grab either from any GNU archive site."
290 # The file to touch is that specified with -o ...
291 file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
292 if test -z "$file"; then
293 # ... or it is the one specified with @setfilename ...
294 infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
295 file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile`
296 # ... or it is derived from the source name (dir/f.texi becomes f.info)
297 test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
298 fi
299 touch $file
300 ;;
301
302 tar)
303 shift
304
305 # We have already tried tar in the generic part.
306 # Look for gnutar/gtar before invocation to avoid ugly error
307 # messages.
308 if (gnutar --version > /dev/null 2>&1); then
309 gnutar "$@" && exit 0
310 fi
311 if (gtar --version > /dev/null 2>&1); then
312 gtar "$@" && exit 0
313 fi
314 firstarg="$1"
315 if shift; then
316 case "$firstarg" in
317 *o*)
318 firstarg=`echo "$firstarg" | sed s/o//`
319 tar "$firstarg" "$@" && exit 0
320 ;;
321 esac
322 case "$firstarg" in
323 *h*)
324 firstarg=`echo "$firstarg" | sed s/h//`
325 tar "$firstarg" "$@" && exit 0
326 ;;
327 esac
328 fi
329
330 echo 1>&2 "\
331 WARNING: I can't seem to be able to run \`tar' with the given arguments.
332 You may want to install GNU tar or Free paxutils, or check the
333 command line arguments."
334 exit 1
335 ;;
336
337 *)
338 echo 1>&2 "\
339 WARNING: \`$1' is needed, and is $msg.
340 You might have modified some files without having the
341 proper tools for further handling them. Check the \`README' file,
342 it often tells you about the needed prerequisites for installing
343 this package. You may also peek at any GNU archive site, in case
344 some other package would contain this missing \`$1' program."
345 exit 1
346 ;;
347 esac
348
349 exit 0
350
351 # Local variables:
352 # eval: (add-hook 'write-file-hooks 'time-stamp)
353 # time-stamp-start: "scriptversion="
354 # time-stamp-format: "%:y-%02m-%02d.%02H"
355 # time-stamp-end: "$"
356 # End:
0 /*
1 * File: IO.c
2 *
3 * Copyright (C) 2000, 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Dillo's signal driven IO engine
13 */
14
15 #include <pthread.h>
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <sys/stat.h>
23 #include <sys/uio.h>
24 #include <sys/socket.h>
25 #include <gdk/gdk.h>
26 #include "../msg.h"
27 #include "../chain.h"
28 #include "../list.h"
29 #include "../klist.h"
30 #include "IO.h"
31
32 #define DEBUG_LEVEL 5
33 #include "../debug.h"
34
35
36 /*
37 * Symbolic defines for shutdown() function
38 * (Not defined in the same header file, for all distros --Jcid)
39 */
40 #define IO_StopRd 0
41 #define IO_StopWr 1
42 #define IO_StopRdWr 2
43
44
45 /*
46 * Local data
47 */
48 static Klist_t *ValidIOs = NULL; /* Active IOs list. It holds pointers to
49 * IOData_t structures. */
50
51 /*
52 * Forward declarations
53 */
54 void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
55 void *Data1, void *Data2);
56
57 /*
58 * Debug procedure...
59
60 static void IO_print_cond_status(gchar *str, GIOCondition cond,
61 GIOChannel *gio, gint key)
62 {
63 MSG("%s FD=%d key=%d [", str, g_io_channel_unix_get_fd(gio), key);
64 MSG(cond & G_IO_IN ? "G_IO_IN " : "");
65 MSG(cond & G_IO_OUT ? "G_IO_OUT " : "");
66 MSG(cond & G_IO_PRI ? "G_IO_PRI " : "");
67 MSG(cond & G_IO_ERR ? "G_IO_ERR " : "");
68 MSG(cond & G_IO_HUP ? "G_IO_HUP " : "");
69 MSG(cond & G_IO_NVAL ? "G_IO_NVAL " : "");
70 MSG("]\n");
71 }
72 */
73
74
75 /* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
76
77 /*
78 * Return a newly created, and initialized, 'io' struct
79 */
80 IOData_t *a_IO_new(gint op, gint fd)
81 {
82 IOData_t *io = g_new0(IOData_t, 1);
83 io->Op = op;
84 io->FD = fd;
85 io->GioCh = g_io_channel_unix_new(fd);
86 io->Flags = 0;
87 io->Key = 0;
88 return io;
89 }
90
91 /*
92 * Set the transfer buffer.
93 * (if we allocate it, we free it)
94 */
95 void a_IO_set_buf(IOData_t *io, void *Buf, size_t BufSize)
96 {
97 if (io->Flags & IOFlag_FreeIOBuf) {
98 g_free(io->BufStart);
99 io->Flags &= ~IOFlag_FreeIOBuf;
100 }
101
102 if (!Buf && BufSize) {
103 io->BufStart = io->Buf = g_malloc(BufSize);
104 io->BufSize = BufSize;
105 io->Flags |= IOFlag_FreeIOBuf;
106
107 } else {
108 io->BufStart = io->Buf = Buf;
109 io->BufSize = BufSize;
110 }
111 }
112
113 /*
114 * Add a new chunk to the transfer buffer.
115 * (we allocate it, so we free it)
116 */
117 void a_IO_add_buf(IOData_t *io, void *Buf, size_t BufSize)
118 {
119 size_t offset;
120
121 if (BufSize == 0) {
122 /* This is the last chunk */
123 io->Flags |= IOFlag_ForceClose;
124 return;
125 }
126
127 offset = (io->Buf) ? (gchar*)io->Buf - (gchar*)io->BufStart : 0;
128 io->BufStart = g_realloc(io->BufStart, offset + io->BufSize + BufSize);
129 memcpy((gchar*)io->BufStart + offset + io->BufSize, Buf, BufSize);
130 io->Buf = (gchar*)io->BufStart + offset;
131 io->BufSize += BufSize;
132 io->Flags |= IOFlag_FreeIOBuf;
133 }
134
135 /*
136 * Return transfer buffer.
137 *
138 void a_IO_get_buf(IOData_t *io, void **Buf, size_t *BufSize)
139 {
140 *Buf = io->Buf;
141 *BufSize = io->BufSize;
142 }
143 */
144
145 typedef struct {
146 pthread_t thrID; /* Thread id */
147 pthread_mutex_t mut;
148 pthread_cond_t cond;
149
150 gint FD;
151 void *Key;
152
153 void *Buf1Start;
154 void *Buf1;
155 size_t Buf1Size;
156 void *Buf2;
157 size_t Buf2Size;
158
159 gint LastChunk;
160 gint CloseFD;
161 gint Done;
162 } thr_data_t;
163
164 /* Active data for threaded chunk transfers */
165 static thr_data_t **ThrData = NULL;
166 static gint ThrDataSize = 0;
167 static gint ThrDataMax = 8;
168
169 /*
170 * Create a new ThrData node
171 */
172 static thr_data_t *IO_thrdata_new(gint FD, void *Key)
173 {
174 thr_data_t *data;
175
176 data = g_new(thr_data_t, 1);
177 pthread_mutex_init(&data->mut, NULL);
178 pthread_cond_init(&data->cond, NULL);
179 data->FD = FD;
180 data->Key = Key;
181 data->Buf1Start = data->Buf1 = data->Buf2 = NULL;
182 data->Buf1Size = data->Buf2Size = 0;
183 data->LastChunk = 0;
184 data->CloseFD = 0;
185 data->Done = 0;
186 return data;
187 }
188
189 /*
190 * Free a ThrData node
191 */
192 static void IO_thrdata_free(thr_data_t *td)
193 {
194 /* EBUSY should not happen: IO_thrdata_free is called after the
195 * thread is done */
196 while (pthread_cond_destroy(&td->cond) == EBUSY) {
197 g_warning("IO_thrdata_free: EBUSY\n");
198 if (pthread_cancel(td->thrID) == ESRCH)
199 break;
200 }
201
202 pthread_mutex_destroy(&td->mut);
203 g_free(td);
204 }
205
206 /*
207 * Search data node for a FD
208 * - The FD is searched using a Key because using the FD itself may fail
209 * due to a race condition between the FD close and its reuse.
210 * - This function also frees already closed data.
211 */
212 static thr_data_t *IO_thrdata_find(void *Key)
213 {
214 gint i, idx = -1;
215
216 _MSG("TL(%p): [", Key);
217 for (i = 0; i < ThrDataSize; ++i) {
218 _MSG(" %d%s", ThrData[i]->FD, ThrData[i]->Done ? "D" : "");
219 if (ThrData[i]->Done) {
220 IO_thrdata_free(ThrData[i]);
221 a_List_remove(ThrData, i, ThrDataSize);
222 --i;
223 } else if (ThrData[i]->Key == Key) {
224 idx = i;
225 _MSG("*");
226 }
227 }
228 _MSG("]\n");
229
230 if (idx != -1)
231 return ThrData[idx];
232 return NULL;
233 }
234
235
236 /*
237 * Write the data buffer through a FD.
238 * [This function runs on its own thread]
239 */
240 static void *IO_write_chunk(void *ptr)
241 {
242 gint lock = 0;
243 thr_data_t *data = ptr;
244 ssize_t St;
245 gint st, done;
246
247 _MSG("thr::\n");
248 _MSG(" [thrID:%lu]\n", (gulong)data->thrID);
249
250 while (1) {
251 _MSG("thr:: trying to lock mutex\n");
252 if (!lock) {
253 pthread_mutex_lock(&data->mut);
254 lock = 1;
255 }
256 _MSG("thr:: mutex locked!\n");
257 _MSG("thr:: Buf1:%p Buf2:%p LastChunk:%d Done:%d\n",
258 data->Buf1, data->Buf2, data->LastChunk, data->Done);
259 if (data->Buf2) {
260 /* Buf1 := Buf2; Buf2 := NULL */
261 g_free(data->Buf1Start);
262 data->Buf1Start = data->Buf1 = data->Buf2;
263 data->Buf1Size = data->Buf2Size;
264 data->Buf2 = NULL;
265 data->Buf2Size = 0;
266 _MSG("thr:: Buf1:%p Buf2:%p LastChunk:%d Done:%d\n",
267 data->Buf1, data->Buf2, data->LastChunk, data->Done);
268 pthread_mutex_unlock(&data->mut);
269 lock = 0;
270 _MSG("thr:: mutex unlocked!\n");
271
272 /*** write all ***/
273 done = 0;
274 while (!done) {
275 St = write(data->FD, data->Buf1, data->Buf1Size);
276 _MSG("thr:: St=%d\n", St);
277 if (St < 0) {
278 if (errno == EINTR) {
279 continue;
280 } else {
281 perror("IO_write_chunk");
282 return NULL;
283 }
284 } else if ((size_t)St < data->Buf1Size) {
285 /* Not all data written */
286 data->Buf1 = (gchar *)data->Buf1 + St;
287 data->Buf1Size -= St;
288 } else {
289 /* All data in buffer written */
290 done = 1;
291 }
292 }
293 }
294
295 /* Buf1 was written, prepare the next step... */
296 if (!lock) {
297 pthread_mutex_lock(&data->mut);
298 lock = 1;
299 }
300 if (data->Buf2)
301 continue;
302 else if (data->LastChunk) {
303 /* Only pipes are closed, sockets are left for the server to close */
304 if (data->CloseFD) {
305 do
306 st = close(data->FD);
307 while (st < 0 && errno == EINTR);
308 }
309 g_free(data->Buf1Start);
310 data->Done = 1;
311 pthread_mutex_unlock(&data->mut);
312 _MSG("thr:: LastChunk:%d Done:%d --Bailing out!\n",
313 data->LastChunk, data->Done);
314 return NULL;
315 } else {
316 _MSG("thr:: going to cond_wait...\n");
317 pthread_cond_wait(&data->cond, &data->mut);
318 lock = 1;
319 }
320
321 }/* while (1) */
322 }
323
324 /*
325 * Write a data chunk from a pthread
326 * (!Buf && !BufSize -> LastChunk -> close tansfer, don't close FD)
327 * (!Buf && BufSize == 1 -> LastChunk -> close tansfer, close FD)
328 */
329 void a_IO_write_chunk(gint FD, void *Key, void *Buf, size_t BufSize)
330 {
331 thr_data_t *data;
332 gint new_thread = 0;
333 static pthread_attr_t thrATTR;
334 static gint thrATTRInitialized = 0;
335
336 /* set the thread attribute to the detached state */
337 if (!thrATTRInitialized) {
338 pthread_attr_init(&thrATTR);
339 pthread_attr_setdetachstate(&thrATTR, PTHREAD_CREATE_DETACHED);
340 thrATTRInitialized = 1;
341 }
342
343 /* Search data node for this FD */
344 data = IO_thrdata_find(Key);
345 _MSG(" a_IO_write_chunk: data=%p Buf=%p, BufSize=%d, FD=%d\n",
346 data, Buf, BufSize, FD);
347
348 if (!data && Buf) {
349 data = IO_thrdata_new(FD, Key);
350 a_List_add(ThrData, ThrDataSize, ThrDataMax);
351 ThrData[ThrDataSize] = data;
352 ThrDataSize++;
353 new_thread = 1;
354 }
355
356 pthread_mutex_lock(&data->mut);
357 if (Buf) {
358 data->Buf2 = g_realloc(data->Buf2, data->Buf2Size + BufSize);
359 memcpy((gchar*)data->Buf2 + data->Buf2Size, Buf, BufSize);
360 data->Buf2Size = data->Buf2Size + BufSize;
361 } else {
362 data->LastChunk = 1;
363 data->CloseFD = (BufSize == 1) ? 1 : 0;
364 }
365 pthread_cond_signal(&data->cond);
366 pthread_mutex_unlock(&data->mut);
367
368 if (new_thread)
369 pthread_create(&data->thrID, &thrATTR, IO_write_chunk, data);
370 }
371
372
373 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
374
375 /*
376 * Register an IO in ValidIOs
377 */
378 static void IO_ins(IOData_t *io)
379 {
380 io->Key = a_Klist_insert(&ValidIOs, (gpointer)io);
381 }
382
383 /*
384 * Remove an IO from ValidIOs
385 */
386 static void IO_del(IOData_t *io)
387 {
388 a_Klist_remove(ValidIOs, io->Key);
389 }
390
391 /*
392 * Return a io by its Key (NULL if not found)
393 */
394 static IOData_t *IO_get(gint Key)
395 {
396 return a_Klist_get_data(ValidIOs, Key);
397 }
398
399 /*
400 * Free an 'io' struct
401 */
402 static void IO_free(IOData_t *io)
403 {
404 g_return_if_fail(IO_get(io->Key) == NULL);
405
406 if (io->Flags & IOFlag_FreeIOBuf)
407 a_IO_set_buf(io, NULL, 0);
408 g_free(io);
409 }
410
411 /*
412 * Close an open FD, and remove io controls.
413 * (This function can be used for Close and Abort operations)
414 */
415 static void IO_close_fd(IOData_t *io, gint CloseCode)
416 {
417 gint st;
418
419 /* With HTTP, if we close the writing part, the reading one also gets
420 * closed! (other clients may set 'IOFlag_ForceClose') */
421 if ((io->Flags & IOFlag_ForceClose) || (CloseCode != IO_StopWr))
422 do
423 st = close(io->FD);
424 while (st < 0 && errno == EINTR);
425
426 /* Remove this IOData_t reference, from our ValidIOs list
427 * We don't deallocate it here, just remove from the list.*/
428 IO_del(io);
429
430 /* Stop the polling on this FD */
431 g_source_remove(io->watch_id);
432 }
433
434 /*
435 * Abort an open FD.
436 * This function is called to abort a FD connection due to an IO error
437 * or just because the connection is not required anymore.
438 */
439 static gboolean IO_abort(IOData_t *io)
440 {
441 /* Close and finish this FD's activity */
442 IO_close_fd(io, IO_StopRdWr);
443
444 return FALSE;
445 }
446
447 /*
448 * Read data from a file descriptor into a specific buffer
449 */
450 static gboolean IO_read(IOData_t *io)
451 {
452 ssize_t St;
453 gboolean ret, DataPending;
454
455 DEBUG_MSG(3, " IO_read\n");
456
457 do {
458 ret = FALSE;
459 DataPending = FALSE;
460
461 St = read(io->FD, io->Buf, io->BufSize);
462 io->Status = St;
463 DEBUG_MSG(3, " IO_read: %s [errno %d] [St %ld]\n",
464 g_strerror(errno), errno, (glong)St);
465
466 if ( St < 0 ) {
467 /* Error */
468 io->Status = -errno;
469 if (errno == EINTR)
470 continue;
471 else if (errno == EAGAIN)
472 ret = TRUE;
473
474 } else if ( St == 0 ) {
475 /* All data read (EOF) */
476 IO_close_fd(io, IO_StopRd);
477 a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
478
479 } else if ( (size_t)St < io->BufSize ){
480 /* We have all the new data */
481 a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
482 ret = TRUE;
483
484 } else { /* BytesRead == io->BufSize */
485 /* We have new data, and maybe more... */
486 a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
487 DataPending = TRUE;
488 }
489 } while (DataPending);
490
491 return ret;
492 }
493
494 /*
495 * Write data, from a specific buffer, into a file descriptor
496 * todo: Implement IOWrites.
497 */
498 static gboolean IO_write(IOData_t *io)
499 {
500 ssize_t St;
501 gboolean ret, DataPending;
502
503 DEBUG_MSG(3, " IO_write\n");
504
505 do {
506 ret = FALSE;
507 DataPending = FALSE;
508 St = write(io->FD, io->Buf, io->BufSize);
509 io->Status = St;
510
511 DEBUG_MSG(3, " IO_write: %s [errno %d] [St %ld]\n",
512 g_strerror(errno), errno, (glong)St);
513
514 if ( St < 0 ) {
515 /* Error */
516 io->Status = -errno;
517 if (errno == EINTR) {
518 continue;
519 } else if (errno == EAGAIN) {
520 DEBUG_MSG(4, " IO_write: EAGAIN\n");
521 ret = TRUE;
522 }
523 } else if ( (size_t)St < io->BufSize ){
524 /* Not all data written */
525 io->BufSize -= St;
526 io->Buf = (gchar *)io->Buf + St;
527 DataPending = TRUE;
528 DEBUG_MSG(4, " IO_write: Changing io->Buf (%ld)\n", (glong)St);
529
530 } else {
531 /* All data in buffer written */
532 if ( io->Op == IOWrite ) {
533 /* Single write */
534 IO_close_fd(io, IO_StopWr);
535 a_IO_ccc(OpEnd, 1, FWD, io->Info, io, NULL);
536 } else if ( io->Op == IOWrites ) {
537 /* Writing in small chunks */
538 /* clear the buffer, and wait for a new chunk */
539 a_IO_set_buf(io, NULL, 0);
540 if (io->Flags & IOFlag_ForceClose) {
541 IO_close_fd(io, IO_StopWr);
542 a_IO_ccc(OpEnd, 1, FWD, io->Info, io, NULL);
543 }
544 }
545 }
546 } while (DataPending);
547
548 return ret;
549 }
550
551 /*
552 * Handle background IO for a given FD (reads | writes)
553 * (This function gets called by glib when there's activity in the FD)
554 */
555 static gboolean IO_callback(GIOChannel *src, GIOCondition cond, gpointer data)
556 {
557 gboolean ret = FALSE;
558 gint io_key = GPOINTER_TO_INT(data);
559 IOData_t *io = IO_get(io_key);
560
561 /* IO_print_cond_status("IO_callback: ", cond, src, io_key); */
562
563 /* There should be no more glib events on already closed FDs --Jcid */
564 if ( io == NULL ) {
565 g_warning("IO_callback: call on already closed io!\n");
566 g_assert_not_reached();
567 return FALSE;
568 }
569
570 if ( cond & (G_IO_IN | G_IO_HUP) ){ /* Read */
571 ret = IO_read(io);
572 } else if ( cond & G_IO_OUT ){ /* Write */
573 ret = IO_write(io);
574 io = IO_get(io_key); /* IO_write may have freed 'io' */
575 if (io && io->Status == -EAGAIN)
576 ret = TRUE; /* wait for another G_IO_OUT event... */
577 }
578
579 if ( cond & G_IO_ERR ){ /* Error */
580 /* IO_read/IO_write may free 'io' */
581 if ((io = IO_get(io_key))) {
582 io->Status = -EIO;
583 ret = IO_abort(io);
584 } else {
585 ret = FALSE;
586 }
587 } else if ( cond & (G_IO_PRI | G_IO_NVAL) ){
588 /* Ignore these exceptional conditions */
589 ret = FALSE;
590 }
591
592 return ret;
593 }
594
595 /*
596 * Receive an IO request (IORead | IOWrite | IOWrites),
597 * Set the GIOChannel and let it flow!
598 */
599 static void IO_submit(IOData_t *r_io)
600 {
601 /* Insert this IO in ValidIOs */
602 IO_ins(r_io);
603
604 /* Set FD to background and to close on exec. */
605 fcntl(r_io->FD, F_SETFL,
606 O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
607 fcntl(r_io->FD, F_SETFD,
608 FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
609
610 if ( r_io->Op == IORead ) {
611 r_io->watch_id =
612 g_io_add_watch(r_io->GioCh, G_IO_IN | G_IO_ERR | G_IO_HUP,
613 IO_callback, GINT_TO_POINTER (r_io->Key));
614 g_io_channel_unref(r_io->GioCh);
615
616 } else if ( r_io->Op == IOWrite || r_io->Op == IOWrites ) {
617 r_io->watch_id =
618 g_io_add_watch(r_io->GioCh, G_IO_OUT | G_IO_ERR,
619 IO_callback, GINT_TO_POINTER (r_io->Key));
620 g_io_channel_unref(r_io->GioCh);
621 }
622 }
623
624 /*
625 * Receive IO request (IORead | IOWrite | IOWrites),
626 * and either start or keep it flowing.
627 */
628 static void IO_send(IOData_t *io)
629 {
630 if (!io->Key)
631 IO_submit(io);
632 }
633
634
635 /*
636 * CCC function for the IO module
637 * ( Data1 = IOData_t* ; Data2 = NULL )
638 */
639 void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
640 void *Data1, void *Data2)
641 {
642 IOData_t *io = Data1;
643
644 a_Chain_debug_msg("a_IO_ccc", Op, Branch, Dir);
645
646 if (Branch == 1) {
647 if (Dir == BCK) {
648 /* Write data */
649 switch (Op) {
650 case OpStart:
651 io->Info = Info;
652 Info->LocalKey = io;
653 break;
654 case OpSend:
655 /* this part submits the io */
656 IO_send(io);
657 break;
658 case OpAbort:
659 io = Info->LocalKey;
660 IO_abort(io);
661 IO_free(io);
662 g_free(Info);
663 break;
664 }
665 } else { /* FWD */
666 /* Write-data status */
667 switch (Op) {
668 case OpEnd:
669 a_Chain_fcb(OpEnd, Info, io, NULL);
670 IO_free(io);
671 break;
672 case OpAbort:
673 a_Chain_fcb(OpAbort, Info, NULL, NULL);
674 IO_free(io);
675 break;
676 }
677 }
678
679 } else if (Branch == 2) {
680 if (Dir == BCK) {
681 /* This part catches the reader's messages */
682 switch (Op) {
683 case OpStart:
684 Info->LocalKey = io;
685 io->Info = Info;
686 IO_submit(io);
687 break;
688 case OpAbort:
689 io = Info->LocalKey;
690 IO_abort(io);
691 IO_free(io);
692 g_free(Info);
693 break;
694 }
695 } else { /* FWD */
696 /* Send read-data */
697 switch (Op) {
698 case OpStart:
699 io->Info = Info;
700 Info->LocalKey = io;
701 a_Chain_link_new(Info, a_IO_ccc, FWD, a_Cache_ccc, 2, 2);
702 a_Chain_fcb(OpStart, Info, io, io->ExtData);
703 IO_submit(io);
704 break;
705 case OpSend:
706 a_Chain_fcb(OpSend, Info, io, NULL);
707 break;
708 case OpEnd:
709 a_Chain_fcb(OpEnd, Info, io, NULL);
710 IO_free(io);
711 break;
712 case OpAbort:
713 MSG(" Not implemented\n");
714 break;
715 }
716 }
717
718 } else if (Branch == 3) {
719 if (Dir == BCK) {
720 /* Write data using a thread */
721 switch (Op) {
722 case OpStart:
723 {
724 gint *fd = g_new(gint, 1);
725 *fd = *(int*)Data1; /* SockFD */
726 Info->LocalKey = fd;
727 break;
728 }
729 case OpEnd:
730 _MSG(" Info=%p Info->LocalKey=%p FD=%d Data1=%p\n",
731 Info, Info->LocalKey, *(int*)Info->LocalKey, Data1);
732 a_IO_write_chunk(*(int*)Info->LocalKey, Info,
733 NULL, (Data1) ? (size_t)1 : (size_t)0);
734 g_free(Info->LocalKey);
735 break;
736 case OpSend:
737 {
738 /* this part submits the data to the thread */
739 DataBuf *dbuf = Data1;
740 a_IO_write_chunk(*(int*)Info->LocalKey, Info,
741 dbuf->Buf, (size_t)dbuf->Size);
742 break;
743 }
744 case OpAbort:
745 g_free(Info->LocalKey);
746 g_free(Info);
747 break;
748 }
749 } else { /* FWD */
750 /* Write-data status */
751 switch (Op) {
752 case OpEnd:
753 a_Chain_fcb(OpEnd, Info, io, NULL);
754 IO_free(io);
755 break;
756 case OpAbort:
757 a_Chain_fcb(OpAbort, Info, NULL, NULL);
758 IO_free(io);
759 break;
760 }
761 }
762 }
763 }
764
0 #ifndef __IO_H__
1 #define __IO_H__
2
3 #include<unistd.h>
4 #include<sys/uio.h>
5 #include <glib.h>
6
7 #include "../chain.h"
8
9 /*
10 * IO Operations
11 */
12 #define IORead 0
13 #define IOWrite 1
14 #define IOWrites 2
15 #define IOClose 3
16 #define IOAbort 4
17
18 /*
19 * IO Flags
20 */
21 #define IOFlag_FreeIOBuf (1 << 1)
22 #define IOFlag_ForceClose (1 << 2)
23 #define IOFlag_SingleWrite (1 << 3)
24
25 /*
26 * IO constants
27 */
28 #define IOBufLen 4096
29 #define IOBufLen_Http 4096
30 #define IOBufLen_File 4096
31 #define IOBufLen_Proto 4096
32 #define IOBufLen_About 4096
33
34
35 typedef struct {
36 gint Key; /* Primary Key (for klist) */
37 gint Op; /* IORead | IOWrite | IOWrites */
38 gint FD; /* Current File Descriptor */
39 gint Flags; /* Flag array (look definitions above) */
40 glong Status; /* Number of bytes read, or -errno code */
41
42 void *Buf; /* Buffer place */
43 size_t BufSize; /* Buffer length */
44 void *BufStart; /* PRIVATE: only used inside IO.c! */
45
46 void *ExtData; /* External data reference (not used by IO.c) */
47 void *Info; /* CCC Info structure for this IO */
48 GIOChannel *GioCh; /* IO channel */
49 guint watch_id; /* glib's event source id */
50 } IOData_t;
51
52
53 /*
54 * Exported functions
55 */
56 IOData_t* a_IO_new(gint op, gint fd);
57 void a_IO_set_buf(IOData_t *io, void *Buf, size_t BufSize);
58 void a_IO_add_buf(IOData_t *io, void *Buf, size_t BufSize);
59 /* Note: a_IO_ccc() is defined in Url.h together with the *_ccc() set */
60
61 void a_IO_write_chunk(gint FD, void *Key, void *Buf, size_t BufSize);
62
63 #endif /* __IO_H__ */
64
0 AM_CFLAGS = @GTK_CFLAGS@
1 AM_LIBS = @GTK_LIBS@
2
3 noinst_LIBRARIES = libDio.a
4
5 libDio_a_SOURCES = \
6 mime.c \
7 mime.h \
8 about.c \
9 Url.c \
10 Url.h \
11 proto.c \
12 http.c \
13 dpi.c \
14 IO.c \
15 IO.h
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(libDio_a_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ../..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 subdir = src/IO
42 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
43 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
44 am__aclocal_m4_deps = $(top_srcdir)/configure.in
45 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
46 $(ACLOCAL_M4)
47 mkinstalldirs = $(install_sh) -d
48 CONFIG_HEADER = $(top_builddir)/config.h
49 CONFIG_CLEAN_FILES =
50 LIBRARIES = $(noinst_LIBRARIES)
51 AR = ar
52 ARFLAGS = cru
53 libDio_a_AR = $(AR) $(ARFLAGS)
54 libDio_a_LIBADD =
55 am_libDio_a_OBJECTS = mime.$(OBJEXT) about.$(OBJEXT) Url.$(OBJEXT) \
56 proto.$(OBJEXT) http.$(OBJEXT) dpi.$(OBJEXT) IO.$(OBJEXT)
57 libDio_a_OBJECTS = $(am_libDio_a_OBJECTS)
58 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
59 depcomp = $(SHELL) $(top_srcdir)/depcomp
60 am__depfiles_maybe = depfiles
61 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
62 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
63 CCLD = $(CC)
64 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
65 SOURCES = $(libDio_a_SOURCES)
66 DIST_SOURCES = $(libDio_a_SOURCES)
67 ETAGS = etags
68 CTAGS = ctags
69 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
70 ACLOCAL = @ACLOCAL@
71 AMDEP_FALSE = @AMDEP_FALSE@
72 AMDEP_TRUE = @AMDEP_TRUE@
73 AMTAR = @AMTAR@
74 AUTOCONF = @AUTOCONF@
75 AUTOHEADER = @AUTOHEADER@
76 AUTOMAKE = @AUTOMAKE@
77 AWK = @AWK@
78 CC = @CC@
79 CCDEPMODE = @CCDEPMODE@
80 CFLAGS = @CFLAGS@
81 CPP = @CPP@
82 CPPFLAGS = @CPPFLAGS@
83 CXX = @CXX@
84 CXXDEPMODE = @CXXDEPMODE@
85 CXXFLAGS = @CXXFLAGS@
86 CYGPATH_W = @CYGPATH_W@
87 DEFS = @DEFS@
88 DEPDIR = @DEPDIR@
89 DLGUI_FALSE = @DLGUI_FALSE@
90 DLGUI_TRUE = @DLGUI_TRUE@
91 ECHO_C = @ECHO_C@
92 ECHO_N = @ECHO_N@
93 ECHO_T = @ECHO_T@
94 EGREP = @EGREP@
95 EXEEXT = @EXEEXT@
96 GLIB_CFLAGS = @GLIB_CFLAGS@
97 GLIB_CONFIG = @GLIB_CONFIG@
98 GLIB_LIBS = @GLIB_LIBS@
99 GTK_CFLAGS = @GTK_CFLAGS@
100 GTK_CONFIG = @GTK_CONFIG@
101 GTK_LIBS = @GTK_LIBS@
102 INSTALL_DATA = @INSTALL_DATA@
103 INSTALL_PROGRAM = @INSTALL_PROGRAM@
104 INSTALL_SCRIPT = @INSTALL_SCRIPT@
105 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
106 LDFLAGS = @LDFLAGS@
107 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
108 LIBFLTK_LIBS = @LIBFLTK_LIBS@
109 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
110 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
111 LIBJPEG_LIBS = @LIBJPEG_LIBS@
112 LIBOBJS = @LIBOBJS@
113 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
114 LIBPNG_LIBS = @LIBPNG_LIBS@
115 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
116 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
117 LIBS = @LIBS@
118 LIBSSL_LIBS = @LIBSSL_LIBS@
119 LIBZ_LIBS = @LIBZ_LIBS@
120 LTLIBOBJS = @LTLIBOBJS@
121 MAKEINFO = @MAKEINFO@
122 OBJEXT = @OBJEXT@
123 PACKAGE = @PACKAGE@
124 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
125 PACKAGE_NAME = @PACKAGE_NAME@
126 PACKAGE_STRING = @PACKAGE_STRING@
127 PACKAGE_TARNAME = @PACKAGE_TARNAME@
128 PACKAGE_VERSION = @PACKAGE_VERSION@
129 PATH_SEPARATOR = @PATH_SEPARATOR@
130 RANLIB = @RANLIB@
131 SET_MAKE = @SET_MAKE@
132 SHELL = @SHELL@
133 STRIP = @STRIP@
134 VERSION = @VERSION@
135 ac_ct_CC = @ac_ct_CC@
136 ac_ct_CXX = @ac_ct_CXX@
137 ac_ct_RANLIB = @ac_ct_RANLIB@
138 ac_ct_STRIP = @ac_ct_STRIP@
139 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
140 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
141 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
142 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
143 am__include = @am__include@
144 am__leading_dot = @am__leading_dot@
145 am__quote = @am__quote@
146 am__tar = @am__tar@
147 am__untar = @am__untar@
148 bindir = @bindir@
149 build = @build@
150 build_alias = @build_alias@
151 build_cpu = @build_cpu@
152 build_os = @build_os@
153 build_vendor = @build_vendor@
154 datadir = @datadir@
155 exec_prefix = @exec_prefix@
156 host = @host@
157 host_alias = @host_alias@
158 host_cpu = @host_cpu@
159 host_os = @host_os@
160 host_vendor = @host_vendor@
161 includedir = @includedir@
162 infodir = @infodir@
163 install_sh = @install_sh@
164 libdir = @libdir@
165 libexecdir = @libexecdir@
166 localstatedir = @localstatedir@
167 mandir = @mandir@
168 mkdir_p = @mkdir_p@
169 oldincludedir = @oldincludedir@
170 prefix = @prefix@
171 program_transform_name = @program_transform_name@
172 sbindir = @sbindir@
173 sharedstatedir = @sharedstatedir@
174 sysconfdir = @sysconfdir@
175 target = @target@
176 target_alias = @target_alias@
177 target_cpu = @target_cpu@
178 target_os = @target_os@
179 target_vendor = @target_vendor@
180 AM_CFLAGS = @GTK_CFLAGS@
181 AM_LIBS = @GTK_LIBS@
182 noinst_LIBRARIES = libDio.a
183 libDio_a_SOURCES = \
184 mime.c \
185 mime.h \
186 about.c \
187 Url.c \
188 Url.h \
189 proto.c \
190 http.c \
191 dpi.c \
192 IO.c \
193 IO.h
194
195 all: all-am
196
197 .SUFFIXES:
198 .SUFFIXES: .c .o .obj
199 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
200 @for dep in $?; do \
201 case '$(am__configure_deps)' in \
202 *$$dep*) \
203 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
204 && exit 0; \
205 exit 1;; \
206 esac; \
207 done; \
208 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/IO/Makefile'; \
209 cd $(top_srcdir) && \
210 $(AUTOMAKE) --gnu src/IO/Makefile
211 .PRECIOUS: Makefile
212 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
213 @case '$?' in \
214 *config.status*) \
215 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
216 *) \
217 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
218 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
219 esac;
220
221 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
222 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
223
224 $(top_srcdir)/configure: $(am__configure_deps)
225 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
226 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
227 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
228
229 clean-noinstLIBRARIES:
230 -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
231 libDio.a: $(libDio_a_OBJECTS) $(libDio_a_DEPENDENCIES)
232 -rm -f libDio.a
233 $(libDio_a_AR) libDio.a $(libDio_a_OBJECTS) $(libDio_a_LIBADD)
234 $(RANLIB) libDio.a
235
236 mostlyclean-compile:
237 -rm -f *.$(OBJEXT)
238
239 distclean-compile:
240 -rm -f *.tab.c
241
242 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IO.Po@am__quote@
243 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Url.Po@am__quote@
244 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/about.Po@am__quote@
245 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi.Po@am__quote@
246 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Po@am__quote@
247 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mime.Po@am__quote@
248 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Po@am__quote@
249
250 .c.o:
251 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
252 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
253 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
254 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
255 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
256
257 .c.obj:
258 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
259 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
260 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
261 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
262 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
263 uninstall-info-am:
264
265 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
266 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
267 unique=`for i in $$list; do \
268 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
269 done | \
270 $(AWK) ' { files[$$0] = 1; } \
271 END { for (i in files) print i; }'`; \
272 mkid -fID $$unique
273 tags: TAGS
274
275 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
276 $(TAGS_FILES) $(LISP)
277 tags=; \
278 here=`pwd`; \
279 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
280 unique=`for i in $$list; do \
281 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
282 done | \
283 $(AWK) ' { files[$$0] = 1; } \
284 END { for (i in files) print i; }'`; \
285 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
286 test -n "$$unique" || unique=$$empty_fix; \
287 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
288 $$tags $$unique; \
289 fi
290 ctags: CTAGS
291 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
292 $(TAGS_FILES) $(LISP)
293 tags=; \
294 here=`pwd`; \
295 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
296 unique=`for i in $$list; do \
297 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
298 done | \
299 $(AWK) ' { files[$$0] = 1; } \
300 END { for (i in files) print i; }'`; \
301 test -z "$(CTAGS_ARGS)$$tags$$unique" \
302 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
303 $$tags $$unique
304
305 GTAGS:
306 here=`$(am__cd) $(top_builddir) && pwd` \
307 && cd $(top_srcdir) \
308 && gtags -i $(GTAGS_ARGS) $$here
309
310 distclean-tags:
311 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
312
313 distdir: $(DISTFILES)
314 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
315 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
316 list='$(DISTFILES)'; for file in $$list; do \
317 case $$file in \
318 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
319 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
320 esac; \
321 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
322 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
323 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
324 dir="/$$dir"; \
325 $(mkdir_p) "$(distdir)$$dir"; \
326 else \
327 dir=''; \
328 fi; \
329 if test -d $$d/$$file; then \
330 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
331 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
332 fi; \
333 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
334 else \
335 test -f $(distdir)/$$file \
336 || cp -p $$d/$$file $(distdir)/$$file \
337 || exit 1; \
338 fi; \
339 done
340 check-am: all-am
341 check: check-am
342 all-am: Makefile $(LIBRARIES)
343 installdirs:
344 install: install-am
345 install-exec: install-exec-am
346 install-data: install-data-am
347 uninstall: uninstall-am
348
349 install-am: all-am
350 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
351
352 installcheck: installcheck-am
353 install-strip:
354 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
355 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
356 `test -z '$(STRIP)' || \
357 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
358 mostlyclean-generic:
359
360 clean-generic:
361
362 distclean-generic:
363 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
364
365 maintainer-clean-generic:
366 @echo "This command is intended for maintainers to use"
367 @echo "it deletes files that may require special tools to rebuild."
368 clean: clean-am
369
370 clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
371
372 distclean: distclean-am
373 -rm -rf ./$(DEPDIR)
374 -rm -f Makefile
375 distclean-am: clean-am distclean-compile distclean-generic \
376 distclean-tags
377
378 dvi: dvi-am
379
380 dvi-am:
381
382 html: html-am
383
384 info: info-am
385
386 info-am:
387
388 install-data-am:
389
390 install-exec-am:
391
392 install-info: install-info-am
393
394 install-man:
395
396 installcheck-am:
397
398 maintainer-clean: maintainer-clean-am
399 -rm -rf ./$(DEPDIR)
400 -rm -f Makefile
401 maintainer-clean-am: distclean-am maintainer-clean-generic
402
403 mostlyclean: mostlyclean-am
404
405 mostlyclean-am: mostlyclean-compile mostlyclean-generic
406
407 pdf: pdf-am
408
409 pdf-am:
410
411 ps: ps-am
412
413 ps-am:
414
415 uninstall-am: uninstall-info-am
416
417 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
418 clean-noinstLIBRARIES ctags distclean distclean-compile \
419 distclean-generic distclean-tags distdir dvi dvi-am html \
420 html-am info info-am install install-am install-data \
421 install-data-am install-exec install-exec-am install-info \
422 install-info-am install-man install-strip installcheck \
423 installcheck-am installdirs maintainer-clean \
424 maintainer-clean-generic mostlyclean mostlyclean-compile \
425 mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
426 uninstall-am uninstall-info-am
427
428 # Tell versions [3.59,3.63) of GNU make to not export all variables.
429 # Otherwise a system limit (for SysV at least) may be exceeded.
430 .NOEXPORT:
0 /*
1 * File: Url.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Randall Maas <randym@acm.org>,
5 * Copyright (C) 1999, 2000 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /* Heavily modified by Jcid, Dec 1999 & Jun 2000
14 *
15 * This module selects the apropriate CCC-function for a given URL.
16 */
17
18
19 #include <glib.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include "../list.h"
23 #include "../misc.h"
24 #include "Url.h"
25
26 typedef struct {
27 const char *Name; /* Method name */
28 UrlOpener_t Data; /* Pointer to a function */
29 } UrlMethod_t;
30
31 typedef struct {
32 const char *Name; /* Method name */
33 ChainFunction_t cccFunction; /* Pointer to a CCC function */
34 } UrlMethod2_t;
35
36
37 /*
38 * Local data
39 */
40 /*
41 static gint UrlOpenersSize = 0, UrlOpenersMax = 8;
42 static UrlOpener_t *UrlOpeners = NULL;
43
44 static gint UrlMethodsSize = 0, UrlMethodsMax = 8;
45 static UrlMethod_t *UrlMethods = NULL;
46 */
47
48 /*
49 * Add a method for Url handling
50 * (currently we have File, About & Http)
51 * -- Currently not used
52 */
53 /*
54 static gint Url_add_method(const char *Name, UrlOpener_t Method)
55 {
56 a_List_add(UrlMethods, UrlMethodsSize, UrlMethodsMax);
57 UrlMethods[UrlMethodsSize].Name = Name;
58 UrlMethods[UrlMethodsSize].Data = Method;
59 UrlMethodsSize++;
60 return 0;
61 }
62 */
63
64 /*
65 * Adds method 'F' to the list of URL openers.
66 * UrlOpeners are called in LIFO order until success.
67 * -- Currently not used
68 */
69 /*
70 static gint Url_add_opener(UrlOpener_t F)
71 {
72 a_List_add(UrlOpeners, UrlOpenersSize, UrlOpenersMax);
73 UrlOpeners[UrlOpenersSize] = F;
74 UrlOpenersSize++;
75 return 0;
76 }
77 */
78
79 /*
80 * Search the cccList for a matching ccc function.
81 */
82 ChainFunction_t a_Url_get_ccc_funct(const DilloUrl *Url)
83 {
84 static UrlMethod2_t cccList[] = { {"http" , a_Http_ccc},
85 {"file" , a_Dpi_ccc},
86 {"about", a_About_ccc},
87 {"dpi" , a_Dpi_ccc},
88 {"ftp" , a_Dpi_ccc},
89 {"https" , a_Dpi_ccc},
90 {"data" , a_Dpi_ccc} };
91 #define LSIZE (sizeof(cccList) / sizeof(cccList[0]))
92
93 const gchar *Key;
94 gint KeyLen, j;
95 size_t i;
96
97 /* Use the method inside the URL as a Key to decide what cccFunct matches */
98 if ( !URL_SCHEME_(Url) )
99 return NULL;
100
101 Key = URL_SCHEME_(Url);
102 KeyLen = strlen(Key);
103
104 for ( i = 0; i < LSIZE; ++i ) {
105 for ( j = 0; j < KeyLen; ++j )
106 if ( tolower(Key[j]) != cccList[i].Name[j] )
107 break;
108 if ( j == KeyLen )
109 return cccList[i].cccFunction;
110 }
111 return NULL;
112 }
113
0 #ifndef __IO_URL_H__
1 #define __IO_URL_H__
2
3 #include "../chain.h"
4 #include "../url.h"
5
6
7 #ifdef __cplusplus
8 extern "C" {
9 #endif /* __cplusplus */
10
11 /* Returns a file descriptor to read data from */
12 typedef gint (*UrlOpener_t) (const DilloUrl *url, void *data);
13
14 /*
15 * Module functions
16 */
17 ChainFunction_t a_Url_get_ccc_funct(const DilloUrl *Url);
18
19
20 /*
21 * External functions
22 */
23 extern void a_Http_freeall(void);
24 gint a_Http_init(void);
25 gint a_Http_proxy_auth(void);
26 void a_Http_set_proxy_passwd(gchar *str);
27 gchar *a_Http_make_query_str(const DilloUrl *url, gboolean use_proxy);
28
29 void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
30 void *Data1, void *Data2);
31 void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
32 void *Data1, void *Data2);
33 void a_IO_ccc (int Op, int Branch, int Dir, ChainLink *Info,
34 void *Data1, void *Data2);
35 void a_Dpi_ccc (int Op, int Branch, int Dir, ChainLink *Info,
36 void *Data1, void *Data2);
37
38 char *a_Dpi_send_blocking_cmd(const gchar *server_name, const gchar *cmd);
39 void a_Dpi_bye_dpid(void);
40
41
42 #ifdef __cplusplus
43 }
44 #endif /* __cplusplus */
45
46 #endif /* __IO_URL_H__ */
47
0 /*
1 * File: about.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999, 2001 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <config.h>
13 #include "IO.h"
14 #include "Url.h"
15 #include "../nav.h"
16 #include "../web.h"
17
18 typedef struct _SplashInfo SplashInfo_t;
19
20 struct _SplashInfo {
21 gint FD_Read;
22 gint FD_Write;
23 };
24
25
26 /*
27 * HTML text for startup screen
28 */
29 static char *Splash=
30 "Content-type: text/html\n"
31 "\n"
32 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
33 "<html>\n"
34 "<head>\n"
35 "<title>Splash screen for dillo-" VERSION "</title>\n"
36 "</head>\n"
37 "<body bgcolor='#778899' text='#000000' link='#000000' vlink='#000000'>\n"
38 "\n"
39 "\n"
40 "<!-- the head of the page -->\n"
41 "\n"
42 "<table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
43 " <tr><td>\n"
44 " <table border='1' cellspacing='1' cellpadding='0'>\n"
45 " <tr>\n"
46 " <td bgcolor='#000000'>\n"
47 " <table width='100%' border='0' bgcolor='#ffffff'>\n"
48 " <tr>\n"
49 " <td valign='top' align='left'>\n"
50 " <h1>&nbsp;Welcome to Dillo " VERSION "&nbsp;</h1>\n"
51 " </table>\n"
52 " </table>\n"
53 "</table>\n"
54 "\n"
55 "<br>\n"
56 "\n"
57 "\n"
58 "<!-- the main layout table, definition -->\n"
59 "\n"
60 "<table width='100%' border='0' cellspacing='0' cellpadding='0'>\n"
61 "<tr><td valign='top' width='150' align='center'>\n"
62 "\n"
63 "\n"
64 "<!-- The navigation bar -->\n"
65 "\n"
66 "<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
67 "<tr>\n"
68 " <td>\n"
69 " <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
70 " <tr>\n"
71 " <td colspan='1' bgcolor='#CCCCCC'>Dillo\n"
72 " <tr>\n"
73 " <td bgcolor='#FFFFFF'>\n"
74 " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
75 " <table border='0' cellspacing='0' cellpadding='2'><tr>\n"
76 " <td>\n"
77 " <td>\n"
78 " <a href='http://www.dillo.org/dillo-help.html'>\n"
79 " Help</a>\n"
80 " <tr>\n"
81 " <td>&nbsp;&nbsp;\n"
82 " <td>\n"
83 " <a href='http://www.dillo.org/'>Home</a>\n"
84 " <tr>\n"
85 " <td>&nbsp;&nbsp;\n"
86 " <td>\n"
87 " <a href='http://www.dillo.org/funding/objectives.html'>\n"
88 " Objectives</a>\n"
89 " <tr>\n"
90 " <td>&nbsp;&nbsp;\n"
91 " <td>\n"
92 " <a href='http://www.dillo.org/ChangeLog.html'>\n"
93 " ChangeLog</a>\n"
94 " <tr>\n"
95 " <td>&nbsp;&nbsp;\n"
96 " <td>\n"
97 " <a href='http://www.dillo.org/interview.html'>\n"
98 " Interview</a>\n"
99 " <tr>\n"
100 " <td>&nbsp;&nbsp;\n"
101 " <td>\n"
102 " <a href='http://www.dillo.org/D_authors.html'>\n"
103 " Authors</a>\n"
104 " <tr>\n"
105 " <td>&nbsp;&nbsp;\n"
106 " <td>\n"
107 " <a href='http://www.dillo.org/donations.html'>\n"
108 " Donate</a>\n"
109 " </table>\n"
110 " </table>\n"
111 " </table>\n"
112 "</table>\n"
113 "\n"
114 "<br>\n"
115 "\n"
116 "<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
117 "<tr>\n"
118 " <td>\n"
119 " <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
120 " <tr>\n"
121 " <td colspan='1' bgcolor='#CCCCCC'>Magazines\n"
122 "\n"
123 " <tr>\n"
124 " <td bgcolor='#FFFFFF'>\n"
125 " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
126 " <table border='0' cellpadding='2'>\n"
127 " <tr>\n"
128 " <td>&nbsp;&nbsp;\n"
129 " <td>\n"
130 " <a href='http://lwn.net/'>LWN</a>\n"
131 " <tr>\n"
132 " <td>&nbsp;&nbsp;\n"
133 " <td>\n"
134 " <a href='http://slashdot.org/'>Slashdot</a>\n"
135 " <tr>\n"
136 " <td>&nbsp;&nbsp;\n"
137 " <td>\n"
138 " <a href='http://www.kuro5hin.org/?op=section;section=__all__'>KuroShin</a>\n"
139 " <tr>\n"
140 " <td>&nbsp;&nbsp;\n"
141 " <td>\n"
142 " <a href='http://www.nexusmagazine.com/'>Nexus&nbsp;M.</a>\n"
143 " <tr>\n"
144 " <td>&nbsp;&nbsp;\n"
145 " <td>\n"
146 " <a href='http://www.gnu-darwin.org/update.html'>Monster News</a>\n"
147 " <tr>\n"
148 " <td>&nbsp;&nbsp;\n"
149 " <td>\n"
150 " <a href='http://www.theregister.co.uk/index.html'>The Register</a>\n"
151 " </table>\n"
152 " </table>\n"
153 " </table>\n"
154 "</table>\n"
155 "\n"
156 "<br>\n"
157 "\n"
158 "<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
159 "<tr>\n"
160 " <td>\n"
161 " <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
162 " <tr>\n"
163 " <td colspan='1' bgcolor='#CCCCCC'>Additional Stuff\n"
164 "\n"
165 " <tr>\n"
166 " <td bgcolor='#FFFFFF'>\n"
167 " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
168 " <table border='0' cellpadding='2'><tr>\n"
169 " <td>&nbsp;&nbsp;\n"
170 " <td><a href='http://www.google.com/'>Google</a>\n"
171 " <tr>\n"
172 " <td>&nbsp;&nbsp;\n"
173 " <td><a href='http://www.wikipedia.org/'>Wikipedia</a>\n"
174 " <tr>\n"
175 " <td>&nbsp;&nbsp;\n"
176 " <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n"
177 " <tr>\n"
178 " <td>&nbsp;&nbsp;\n"
179 " <td><a href='http://freshmeat.net/'>FreshMeat</a>\n"
180 " <tr>\n"
181 " <td>&nbsp;&nbsp;\n"
182 " <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU\n"
183 " project</a>\n"
184 " <tr>\n"
185 " <td>&nbsp;&nbsp;\n"
186 " <td><a href='http://www.linuxfund.org/'>LinuxFund</a>\n"
187 " </table>\n"
188 " </table>\n"
189 " </table>\n"
190 "</table>\n"
191 "\n"
192 "<br>\n"
193 "\n"
194 "<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
195 "<tr>\n"
196 " <td>\n"
197 " <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
198 " <tr>\n"
199 " <td colspan='1' bgcolor='#CCCCCC'>Essential Readings\n"
200 "\n"
201 " <tr>\n"
202 " <td bgcolor='#FFFFFF'>\n"
203 " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
204 " <table border='0' cellpadding='2'>\n"
205 " <tr><td>&nbsp;&nbsp;\n"
206 " <td><a href='http://www.violence.de'>Peace&amp;Violence</a>\n"
207 " <tr><td>&nbsp;&nbsp;\n"
208 " <td><a href='http://www.fsf.org/philosophy/right-to-read.html'>"
209 " Right to Read</a>\n"
210 " </table>\n"
211 " </table>\n"
212 " </table>\n"
213 "</table>\n"
214 "\n"
215 "<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></table>\n"
216 "\n"
217 "\n"
218 "<!-- the main layout table, a small vertical spacer -->\n"
219 "\n"
220 "<td width='20'><td valign='top'>\n"
221 "\n"
222 "\n"
223 "<!-- Main Part of the page -->\n"
224 "\n"
225 "<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
226 "<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
227 "<tr>\n"
228 " <td bgcolor='#CCCCCC'>\n"
229 " <h4>Free Software</h4>\n"
230 "<tr>\n"
231 " <td bgcolor='#FFFFFF'>\n"
232 " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
233 " <p>\n"
234 " Dillo is Free Software in the terms of the GPL.\n"
235 " This means you have four basic freedoms:\n"
236 " <ul>\n"
237 " <li>Freedom to use the program any way you see fit.\n"
238 " <li>Freedom to study and modify the source code.\n"
239 " <li>Freedom to make backup copies.\n"
240 " <li>Freedom to redistribute it.\n"
241 " </ul>\n"
242 " The <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>\n"
243 " is the legal mechanism that gives you these freedoms.\n"
244 " It also protects them from being taken away: any derivative work\n"
245 " based on the program must be under the GPL.<br>\n"
246 " </table>\n"
247 "</table>\n"
248 "</table>\n"
249 "\n"
250 "<br>\n"
251 "\n"
252 "<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
253 "<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
254 "<tr>\n"
255 " <td bgcolor='#CCCCCC'>\n"
256 " <h4>Release overview</h4>\n"
257 " April 26, 2006\n"
258 "<tr>\n"
259 " <td bgcolor='#FFFFFF'>\n"
260 " <table border='0' cellspacing='0' cellpadding='5'>\n"
261 " <tr>\n"
262 " <td>\n"
263 "<p>\n"
264 "This release is hopefully the last from the GTK1 series. The port to\n"
265 "<a href='http://www.fltk.org/'>FLTK2</a> is almost finished now!\n"
266 "<p>\n"
267 "This release comes with extensive work on the plugins side. A new dpip\n"
268 "library, a new FLTK2-based GUI for downloads!, a \"<code>data:</code>\"\n"
269 "URI handler, a important bug fix in the FTP plugin, and a cookies server\n"
270 "dpi that enables all Dillo instances to use cookies at the same time!\n"
271 "<p>\n"
272 "There's also the \"<code>./configure --disable-threaded-dns</code>\"\n"
273 "option (mainly for some non reentrant BSDs), among many other improvements.\n"
274 "<p>\n"
275 "Remember that dillo project uses a release model where every new\n"
276 "browser shall be better than the former.\n"
277 "<EM>Keep up with the latest one!</EM>\n"
278 " </table>\n"
279 "</table>\n"
280 "</table>\n"
281 "\n"
282 "<br>\n"
283 "\n"
284 "<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
285 "<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
286 "<tr>\n"
287 " <td bgcolor='#CCCCCC'>\n"
288 " <h4>ChangeLog highlights</h4>\n"
289 " (Extracted from the\n"
290 " <a href='http://www.dillo.org/ChangeLog.html'>full\n"
291 " ChangeLog</a>)\n"
292 "<tr>\n"
293 " <td bgcolor='#FFFFFF'>\n"
294 " <table border='0' cellspacing='0' cellpadding='5'>\n"
295 " <tr>\n"
296 " <td>\n"
297 "<ul>\n"
298 "<li> Designed and implemented a dpi protocol library (libDpip.a in /dpip).\n"
299 "<li> Ported the bookmarks, download, file, https, ftp and hello plugins,\n"
300 " plus the dpid daemon and the rest of the source tree to use it.\n"
301 "<li> Improved the dpi buffer reception to handle split buffers (This was\n"
302 " required for handling arbitrary data streams with dpip).\n"
303 "<li> Fixed a serious bug with the FTP plugin that led to two downloads\n"
304 " of the same file when left-clicking a non-viewable file.\n"
305 "<li> Improved the accuracy of the illegal-character error reporting\n"
306 " for URLs.\n"
307 "<li> Added dpi/downloads.cc (Default FLTK2-based GUI for downloads dpi).\n"
308 "<li> Added \"./configure --disable-dlgui\" to build without FLTK2-GUI\n"
309 " downloads.\n"
310 "<li> Fixed dpip's tag syntax and its parsing to accept any value string.\n"
311 "<li> Added DOCTYPE parsing (for better bug-meter error messages).\n"
312 "<li> Fixed bookmarks dpi to escape ' in URLs and &<>\"' in titles\n"
313 " (BUG#655).\n"
314 "<li> Added a check for malicious image sizes in IMG tags.\n"
315 "\n"
316 "<li> Added a datauri dpi to handle \"data:\" URIs (RFC-2397).\n"
317 "<li> Moved the cookies management into a dpi server: cookies.dpi.\n"
318 "<li> Removed the restriction of only one dillo with cookies enabled!\n"
319 "<li> Added \"./configure --disable-threaded-dns\" (for some non\n"
320 " reentrant BSDs).\n"
321 "</ul>\n"
322 " </table>\n"
323 "</table>\n"
324 "</table>\n"
325 "\n"
326 "<br>\n"
327 "\n"
328 "<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
329 "<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
330 "<tr>\n"
331 " <td bgcolor='#CCCCCC'>\n"
332 " <h4>Notes</h4>\n"
333 "<tr>\n"
334 " <td bgcolor='#FFFFFF'>\n"
335 " <table border='0' cellspacing='0' cellpadding='5'>\n"
336 " <tr>\n"
337 " <td>\n"
338 "<ul>\n"
339 " <li> There's a\n"
340 " <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
341 " (readable config) file within the tarball; It is well commented\n"
342 " and has plenty of options to customize dillo, so <STRONG>copy\n"
343 " it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
344 " modify to your taste.\n"
345 " <li> There's documentation for developers in the <CODE>/doc</CODE>\n"
346 " dir within the tarball; you can find directions on everything\n"
347 " else at the home page.\n"
348 " <li> Dillo has context sensitive menus using the\n"
349 " right mouse button (available on pages, links, images,\n"
350 " the Back and Forward buttons, and bug meter).\n"
351 " <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
352 " It's also very good for Internet searching (try Google!).\n"
353 " <li> This release is mainly intended <strong>for developers</strong>\n"
354 " and <em>advanced users</em>.\n"
355 " <li> Frames, Java and Javascript are not supported.\n"
356 "</ul>\n"
357 "<br>\n"
358 " </table>\n"
359 "</table>\n"
360 "</table>\n"
361 "\n"
362 "<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></table>\n"
363 "\n"
364 "\n"
365 "<!-- the main layout table, a small vertical spacer -->\n"
366 "\n"
367 "<td width='20'>\n"
368 "\n"
369 "\n"
370 "\n"
371 "<!-- The right column (info) -->\n"
372 "<td valign='top' align='center'>\n"
373 "\n"
374 "\n"
375 "\n"
376 "<!-- end of the main layout table -->\n"
377 "\n"
378 "\n"
379 "</table>\n"
380 "\n"
381 "<!-- footnotes -->\n"
382 "\n"
383 "<br><br><center>\n"
384 "<hr size='2'>\n"
385 "<hr size='2'>\n"
386 "</center>\n"
387 "</body>\n"
388 "</html>\n";
389
390
391
392 /*
393 * Send the splash screen through the IO using a pipe.
394 */
395 static gint About_send_splash(ChainLink *Info, DilloUrl *Url)
396 {
397 gint SplashPipe[2];
398 IOData_t *io1;
399 SplashInfo_t *SpInfo;
400
401 if (pipe(SplashPipe))
402 return -1;
403
404 SpInfo = g_new(SplashInfo_t, 1);
405 SpInfo->FD_Read = SplashPipe[0];
406 SpInfo->FD_Write = SplashPipe[1];
407 Info->LocalKey = SpInfo;
408
409 /* send splash */
410 io1 = a_IO_new(IOWrite, SpInfo->FD_Write);
411 a_IO_set_buf(io1, Splash, strlen(Splash));
412 io1->Flags |= (IOFlag_ForceClose + IOFlag_SingleWrite);
413 a_Chain_link_new(Info, a_About_ccc, BCK, a_IO_ccc, 1, 1);
414 a_Chain_bcb(OpStart, Info, io1, NULL);
415 a_Chain_bcb(OpSend, Info, io1, NULL);
416
417 /* Tell the cache to receive answer */
418 a_Chain_fcb(OpSend, Info, &SpInfo->FD_Read, NULL);
419 return SpInfo->FD_Read;
420 }
421
422 /*
423 * Push the right URL for each supported "about"
424 * ( Data1 = Requested URL; Data2 = Web structure )
425 */
426 static gint About_get(ChainLink *Info, void *Data1, void *Data2)
427 {
428 char *loc;
429 const char *tail;
430 DilloUrl *Url = Data1;
431 DilloWeb *web = Data2;
432 DilloUrl *LocUrl;
433
434 /* Don't allow the "about:" method for non-root URLs */
435 if (!(web->flags & WEB_RootUrl))
436 return -1;
437
438 tail = URL_PATH(Url);
439
440 if (!strcmp(tail, "splash")) {
441 return About_send_splash(Info, Url);
442 }
443
444 if (!strcmp(tail, "jwz"))
445 loc = "http://www.jwz.org/";
446 else if (!strcmp(tail, "raph"))
447 loc = "http://www.levien.com/";
448 else if (!strcmp(tail, "yosh"))
449 loc = "http://yosh.gimp.org/";
450 else if (!strcmp(tail, "snorfle"))
451 loc = "http://www.snorfle.net/";
452 else if (!strcmp(tail, "dillo"))
453 loc = "http://www.dillo.org/";
454 else if (!strcmp(tail, "help"))
455 loc = "http://www.dillo.org/dillo-help.html";
456 else
457 loc = "http://www.google.com/";
458
459 LocUrl = a_Url_new(loc, NULL, 0, 0, 0);
460 a_Nav_push(web->bw, LocUrl);
461 a_Url_free(LocUrl);
462 return -1;
463 }
464
465 /*
466 * CCC function for the ABOUT module
467 */
468 void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
469 void *Data1, void *Data2)
470 {
471 int FD;
472
473 a_Chain_debug_msg("a_About_ccc", Op, Branch, Dir);
474
475 if ( Branch == 1 ) {
476 /* Start about method */
477 if (Dir == BCK) {
478 switch (Op) {
479 case OpStart:
480 /* (Data1 = Url; Data2 = Web) */
481 // Info->LocalKey gets set in About_get
482 if ((FD = About_get(Info, Data1, Data2)) == -1)
483 a_Chain_fcb(OpAbort, Info, NULL, NULL);
484 break;
485 case OpAbort:
486 a_Chain_bcb(OpAbort, Info, NULL, NULL);
487 g_free(Info->LocalKey);
488 g_free(Info);
489 break;
490 }
491 } else { /* FWD */
492 switch (Op) {
493 case OpSend:
494 /* This means the sending framework was set OK */
495 FD = ((SplashInfo_t *)Info->LocalKey)->FD_Read;
496 a_Chain_fcb(OpSend, Info, &FD, NULL);
497 break;
498 case OpEnd:
499 /* Everything sent! */
500 a_Chain_del_link(Info, BCK);
501 g_free(Info->LocalKey);
502 a_Chain_fcb(OpEnd, Info, NULL, NULL);
503 break;
504 case OpAbort:
505 g_free(Info->LocalKey);
506 a_Chain_fcb(OpAbort, Info, NULL, NULL);
507 break;
508 }
509 }
510 }
511 }
512
0 /*
1 * File: dpi.c
2 *
3 * Copyright (C) 2002, 2003 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * (Prototype code)
13 *
14 * Dillo plugins (small programs that interact with dillo)
15 * This should be able to handle:
16 * bookmarks, cookies, FTP, downloads, preferences, https and
17 * a lot of any-to-html filters.
18 */
19
20
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <errno.h> /* for errno */
29
30 #include <stdio.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36
37 #include "../msg.h"
38 #include "IO.h"
39 #include "Url.h"
40 #include "../misc.h"
41 #include "../../dpip/dpip.h"
42
43 /* #define DEBUG_LEVEL 2 */
44 #define DEBUG_LEVEL 4
45 #include "../debug.h"
46
47 /* This one is tricky, some sources state it should include the byte
48 * for the terminating NULL, and others say it shouldn't. */
49 # define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
50 + strlen ((ptr)->sun_path))
51
52 /* Solaris may not have this one... */
53 #ifndef AF_LOCAL
54 #define AF_LOCAL AF_UNIX
55 #endif
56
57
58 typedef struct {
59 gint FreeBuf;
60 gint InTag, InData;
61 gint PipeActive;
62 gint Send2EOF;
63
64 gint DataTotalSize;
65 gint DataRecvSize;
66
67 void *Buf;
68 gint BufIdx;
69 gint BufSize;
70
71 gint TokIdx;
72 gint TokSize;
73 gint TokIsTag;
74
75 ChainLink *InfoRecv;
76 } conn_data_t;
77
78
79 /*
80 * Local data
81 */
82
83 /* list for dpi connections waiting for a FD;
84 * it holds pointers to Info structures (i.e. CCC Nodes). */
85 static GSList *PendingNodes = NULL;
86
87 /*
88 * Forward references
89 */
90
91
92 /*
93 * Close a FD handling EINTR
94 */
95 static void Dpi_close_fd(gint fd)
96 {
97 gint st;
98
99 do
100 st = close(fd);
101 while (st < 0 && errno == EINTR);
102 }
103
104 /*
105 * Create a new connection data structure
106 */
107 static conn_data_t *Dpi_conn_data_new(ChainLink *Info)
108 {
109 conn_data_t *conn = g_new0(conn_data_t, 1);
110
111 conn->Buf = NULL;
112 conn->InfoRecv = Info;
113 return conn;
114 }
115
116 /*
117 * Free a connection data structure
118 */
119 static void Dpi_conn_data_free(conn_data_t *conn)
120 {
121 if (conn->FreeBuf)
122 g_free(conn->Buf);
123 g_free(conn);
124 }
125
126 /*
127 * Append the new buffer in 'io' to Buf in 'conn'
128 */
129 static void Dpi_append_io_buf(conn_data_t *conn, IOData_t *io)
130 {
131 if (io->Status > 0) {
132 conn->Buf = g_realloc(conn->Buf, conn->BufSize + (gulong)io->Status);
133 memcpy((gchar*)conn->Buf + conn->BufSize, io->Buf, (size_t)io->Status);
134 conn->BufSize += io->Status;
135 conn->FreeBuf = 0;
136 }
137 }
138
139 /*
140 * Split the data stream into tokens.
141 * Here, a token is either:
142 * a) a dpi tag
143 * b) a raw data chunk
144 *
145 * Return Value: 0 upon a new token, -1 on not enough data.
146 *
147 * TODO: define an API and move this function into libDpip.a.
148 */
149 static gint Dpi_get_token(conn_data_t *conn)
150 {
151 gint i, resp = -1;
152 gchar *buf = conn->Buf;
153
154 if (conn->FreeBuf || conn->BufIdx == conn->BufSize) {
155 g_free(conn->Buf);
156 conn->Buf = NULL;
157 conn->BufIdx = conn->BufSize = 0;
158 conn->FreeBuf = 0;
159 return resp;
160 }
161
162 if (conn->Send2EOF) {
163 conn->TokIdx = conn->BufIdx;
164 conn->TokSize = conn->BufSize - conn->BufIdx;
165 conn->BufIdx = conn->BufSize;
166 return 0;
167 }
168
169 if (!conn->InTag && !conn->InData) {
170 /* search for start of tag */
171 /*
172 gchar *pbuf=NULL;
173 MSG("conn->BufIdx = %d; conn->BufSize = %d\n", conn->BufIdx,conn->BufSize);
174 pbuf = g_strndup(buf, conn->BufSize - conn->BufIdx);
175 MSG("buf: [%s]\n", pbuf);
176 g_free(pbuf);
177 */
178 while (conn->BufIdx < conn->BufSize && buf[conn->BufIdx] != '<')
179 ++conn->BufIdx;
180 if (conn->BufIdx < conn->BufSize) {
181 /* found */
182 conn->InTag = 1;
183 conn->TokIdx = conn->BufIdx;
184 } else {
185 MSG("ERROR: [Dpi_get_token] Can't find token start\n");
186 conn->FreeBuf = 1;
187 return Dpi_get_token(conn);
188 }
189 }
190
191 if (conn->InTag) {
192 /* search for end of tag (EOT=" '>") */
193 for (i = conn->BufIdx; i < conn->BufSize; ++i)
194 if (buf[i] == '>' && i >= 2 && buf[i-1] == '\'' && buf[i-2] == ' ')
195 break;
196 conn->BufIdx = i;
197
198 if (conn->BufIdx < conn->BufSize) {
199 /* found EOT */
200 conn->TokIsTag = 1;
201 conn->TokSize = conn->BufIdx - conn->TokIdx + 1;
202 ++conn->BufIdx;
203 conn->InTag = 0;
204 resp = 0;
205 }
206 }
207
208 if (conn->InData) {
209 conn->TokIsTag = 0;
210 if (conn->DataRecvSize + conn->BufSize - conn->BufIdx <
211 conn-> DataTotalSize) {
212 conn->TokSize += conn->BufSize - conn->BufIdx;
213 conn->DataRecvSize += conn->BufSize - conn->BufIdx;
214 conn->FreeBuf = 1;
215 resp = 0;
216 } else {
217 /* srch end of data */
218 MSG("ERROR: [Dpi_get_token] *** NULL code here ***\n");
219 while (conn->BufIdx < conn->BufSize)
220 ++conn->BufIdx;
221 resp = -1;
222 }
223 }
224
225 return resp;
226 }
227
228 /*
229 * Parse a dpi tag and take the appropriate actions
230 */
231 static void Dpi_parse_token(conn_data_t *conn)
232 {
233 gchar *tag, *cmd, *msg, *urlstr;
234 DataBuf *dbuf;
235
236 if (conn->Send2EOF) {
237 /* we're receiving data chunks from a HTML page */
238 dbuf = a_Chain_dbuf_new(conn->Buf + conn->TokIdx, conn->TokSize, 0);
239 a_Chain_fcb(OpSend, conn->InfoRecv, dbuf, "send_page_2eof");
240 g_free(dbuf);
241 return;
242 }
243
244 tag = g_strndup(conn->Buf + conn->TokIdx, (guint)conn->TokSize);
245 MSG("Dpi_parse_token: {%s}\n", tag);
246
247 cmd = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "cmd");
248 if (strcmp(cmd, "send_status_message") == 0) {
249 msg = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "msg");
250 a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
251 g_free(msg);
252
253 } else if (strcmp(cmd, "chat") == 0) {
254 msg = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "msg");
255 a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
256 g_free(msg);
257
258 } else if (strcmp(cmd, "dialog") == 0) {
259 /* For now will send the dpip tag... */
260 a_Chain_fcb(OpSend, conn->InfoRecv, tag, cmd);
261
262 } else if (strcmp(cmd, "start_send_page") == 0) {
263 urlstr = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "url");
264 a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
265 g_free(urlstr);
266 /* todo: a_Dpip_get_attr(conn->Buf + conn->TokIdx,
267 * conn->TokSize, "send_mode") */
268 conn->Send2EOF = 1;
269
270 } else if (strcmp(cmd, "reload_request") == 0) {
271 urlstr = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "url");
272 a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
273 g_free(urlstr);
274 }
275 g_free(cmd);
276
277 g_free(tag);
278 }
279
280 /*
281 * Compare function for searching a CCC Node with a 'web' pointer.
282 */
283 static gint Dpi_node_cmp(gconstpointer node, gconstpointer key)
284 {
285 return a_Url_cmp(key, ((ChainLink *)node)->LocalKey);
286 }
287
288
289 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
290
291 /*
292 * Get a new data buffer (within an 'io'), save it into local data,
293 * split in tokens and parse the contents.
294 */
295 static void Dpi_process_io(int Op, void *Data1, conn_data_t *conn)
296 {
297 IOData_t *io = Data1;
298
299 /* Very useful for debugging: show the data stream as received. */
300 /* fwrite(io->Buf, io->Status, 1, stdout); */
301
302 if (Op == IORead) {
303 Dpi_append_io_buf(conn, io);
304 while (Dpi_get_token(conn) != -1) {
305 Dpi_parse_token(conn);
306 }
307
308 } else if (Op == IOClose) {
309 MSG("Dpi: [Dpi_process_io] IOClose\n");
310 }
311 }
312
313 /*
314 * Start dpid.
315 * Return: 0 starting now, 1 Error.
316 */
317 static gint Dpi_start_dpid(void)
318 {
319 pid_t pid;
320 gint st_pipe[2], n, ret = 1;
321 gchar buf[16];
322
323 /* create a pipe to track our child's status */
324 if (pipe(st_pipe))
325 return 1;
326
327 pid = fork();
328 if (pid == 0) {
329 /* This is the child process. Execute the command. */
330 gchar *path1 = a_Misc_prepend_user_home(".dillo/dpid");
331 Dpi_close_fd(st_pipe[0]);
332 if (execl(path1, "dpid", NULL) == -1) {
333 g_free(path1);
334 if (execlp("dpid", "dpid", NULL) == -1) {
335 DEBUG_MSG(4, "Dpi_start_dpid (child): %s\n", g_strerror(errno));
336 do
337 n = write(st_pipe[1], "ERROR", 5);
338 while (n == -1 && errno == EINTR);
339 Dpi_close_fd(st_pipe[1]);
340 _exit (EXIT_FAILURE);
341 }
342 }
343 } else if (pid < 0) {
344 /* The fork failed. Report failure. */
345 DEBUG_MSG(4, "Dpi_start_dpid: %s\n", g_strerror(errno));
346 /* close the unused pipe */
347 Dpi_close_fd(st_pipe[0]);
348 Dpi_close_fd(st_pipe[1]);
349
350 } else {
351 /* This is the parent process, check our child status... */
352 Dpi_close_fd(st_pipe[1]);
353 do
354 n = read(st_pipe[0], buf, 16);
355 while (n == -1 && errno == EINTR);
356 DEBUG_MSG(2, "Dpi_start_dpid: n = %d\n", n);
357 if (n != 5)
358 ret = 0;
359 else
360 DEBUG_MSG(4, "Dpi_start_dpid: %s\n", g_strerror(errno));
361 }
362
363 return ret;
364 }
365
366 /*
367 * Make a connection test for a UDS.
368 * Return: 0 OK, 1 Not working.
369 */
370 static gint Dpi_check_uds(gchar *uds_name)
371 {
372 struct sockaddr_un pun;
373 gint SockFD, ret = 1;
374
375 if (access(uds_name, W_OK) == 0) {
376 /* socket connection test */
377 memset(&pun, 0, sizeof(struct sockaddr_un));
378 pun.sun_family = AF_LOCAL;
379 strncpy(pun.sun_path, uds_name, sizeof (pun.sun_path));
380
381 if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1 ||
382 connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
383 DEBUG_MSG(4, "Dpi_check_uds: %s %s\n", g_strerror(errno), uds_name);
384 } else {
385 Dpi_close_fd(SockFD);
386 ret = 0;
387 }
388 }
389 return ret;
390 }
391
392 /*
393 * Return the directory where the UDS are in,
394 * NULL if it can't be found.
395 */
396 static gchar *Dpi_get_dpid_uds_dir(void)
397 {
398 FILE *in;
399 gchar *saved_name_filename; /* :) */
400 gchar dpid_uds_dir[256], *p;
401
402 saved_name_filename =
403 g_strconcat(g_get_home_dir(), "/", ".dillo/dpi_socket_dir", NULL);
404 in = fopen(saved_name_filename, "r");
405 g_free(saved_name_filename);
406
407 if (in != NULL) {
408 fgets(dpid_uds_dir, 256, in);
409 fclose(in);
410 if ((p = strchr(dpid_uds_dir, '\n')))
411 *p = 0;
412 if (access(dpid_uds_dir, F_OK) == 0)
413 p = g_strdup(dpid_uds_dir);
414 _MSG("Dpi_get_dpid_uds_dir:: %s\n", p);
415 return p;
416 }
417
418 _MSG("Dpi_get_dpid_uds_dir: %s \n", g_strerror(errno));
419 return NULL;
420 }
421
422 /*
423 * Return the dpid's UDS name, NULL on failure.
424 */
425 static gchar *Dpi_get_dpid_uds_name(void)
426 {
427 gchar *dpid_uds_dir, *dpid_uds_name = NULL;
428
429 if ((dpid_uds_dir = Dpi_get_dpid_uds_dir()) != NULL)
430 dpid_uds_name= g_strconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
431
432 g_free(dpid_uds_dir);
433 return dpid_uds_name;
434 }
435
436 /*
437 * Confirm that the dpid is running. If not, start it.
438 * Return: 0 running OK, 1 starting (EAGAIN), 2 Error.
439 */
440 static gint Dpi_check_dpid(void)
441 {
442 static gint starting = 0;
443 gchar *dpid_uds_name;
444 gint check_st = 1, ret = 2;
445
446 if ((dpid_uds_name = Dpi_get_dpid_uds_name()))
447 check_st = Dpi_check_uds(dpid_uds_name);
448
449 _MSG("Dpi_check_dpid: dpid_uds_name=%s, check_st=%d\n",
450 dpid_uds_name, check_st);
451
452 if (check_st == 0) {
453 /* connection test with dpi server passed */
454 starting = 0;
455 ret = 0;
456 } else if (!dpid_uds_name || check_st) {
457 if (!starting) {
458 /* start dpid */
459 if (Dpi_start_dpid() == 0) {
460 starting = 1;
461 ret = 1;
462 }
463 } else if (++starting < 25) {
464 ret = 1;
465 } else {
466 /* we waited too much, report an error... */
467 starting = 0;
468 }
469 }
470
471 g_free(dpid_uds_name);
472 DEBUG_MSG(2, "Dpi_check_dpid:: %s\n",
473 (ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
474 return ret;
475 }
476
477 /*
478 * Return the UDS name of a dpi server.
479 * (A query is sent to dpid and then its answer parsed)
480 * note: as the available servers and/or the dpi socket directory can
481 * change at any time, we'll ask each time. If someday we find
482 * that connecting each time significantly degrades performance,
483 * an optimized approach can be tried.
484 */
485 static gchar *Dpi_get_server_uds_name(const gchar *server_name)
486 {
487 gchar *dpid_uds_dir, *dpid_uds_name = NULL,
488 *server_uds_name = NULL;
489 gint st;
490
491 g_return_val_if_fail (server_name != NULL, NULL);
492 DEBUG_MSG(2, "Dpi_get_server_uds_name:: server_name = [%s]\n", server_name);
493
494 dpid_uds_dir = Dpi_get_dpid_uds_dir();
495 if (dpid_uds_dir) {
496 struct sockaddr_un dpid;
497 gint sock, req_sz, rdlen;
498 gchar buf[128], *cmd, *request, *rply;
499 size_t buflen;
500
501 /* Get the server's uds name from dpid */
502 sock = socket(AF_LOCAL, SOCK_STREAM, 0);
503 dpid.sun_family = AF_LOCAL;
504 dpid_uds_name = g_strconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
505 _MSG("dpid_uds_name = [%s]\n", dpid_uds_name);
506 strncpy(dpid.sun_path, dpid_uds_name, sizeof(dpid.sun_path));
507
508 if (connect(sock, (struct sockaddr *) &dpid, sizeof(dpid)) == -1)
509 perror("connect");
510 /* ask dpid to check the server plugin and send its UDS name back */
511 request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
512 DEBUG_MSG(2, "[%s]\n", request);
513 do
514 st = write(sock, request, strlen(request));
515 while (st < 0 && errno == EINTR);
516 if (st < 0 && errno != EINTR)
517 perror("writing request");
518 g_free(request);
519 shutdown(sock, 1); /* signals no more writes to dpid */
520
521 /* Get the reply */
522 rply = NULL;
523 buf[0] = '\0';
524 buflen = sizeof(buf)/sizeof(buf[0]);
525 for (req_sz = 0; (rdlen = read(sock, buf, buflen)) != 0;
526 req_sz += rdlen) {
527 if (rdlen == -1 && errno == EINTR)
528 continue;
529 if (rdlen == -1) {
530 perror(" ** Dpi_get_server_uds_name **");
531 break;
532 }
533 rply = g_realloc(rply, (guint)(req_sz + rdlen + 1));
534 if (req_sz == 0)
535 rply[0] = '\0';
536 strncat(rply, buf, (size_t)rdlen);
537 }
538 Dpi_close_fd(sock);
539 DEBUG_MSG(2, "rply = [%s]\n", rply);
540
541 /* Parse reply */
542 if (rdlen == 0 && rply) {
543 cmd = a_Dpip_get_attr(rply, (gint)strlen(rply), "cmd");
544 if (strcmp(cmd, "send_data") == 0)
545 server_uds_name = a_Dpip_get_attr(rply, (gint)strlen(rply), "msg");
546 g_free(cmd);
547 g_free(rply);
548 }
549 }
550 g_free(dpid_uds_dir);
551 g_free(dpid_uds_name);
552 DEBUG_MSG(2, "Dpi_get_server_uds_name:: %s\n", server_uds_name);
553 return server_uds_name;
554 }
555
556
557 /*
558 * Connect a socket to a dpi server and return the socket's FD.
559 * We have to ask 'dpid' (dpi daemon) for the UDS of the target dpi server.
560 * Once we have it, then the proper file descriptor is returned (-1 on error).
561 */
562 static gint Dpi_connect_socket(const gchar *server_name, gint retry)
563 {
564 char *server_uds_name;
565 struct sockaddr_un pun;
566 gint SockFD, err;
567
568 /* Query dpid for the UDS name for this server */
569 server_uds_name = Dpi_get_server_uds_name(server_name);
570 DEBUG_MSG(2, "server_uds_name = [%s]\n", server_uds_name);
571
572 if (access(server_uds_name, F_OK) != 0) {
573 MSG("server socket was NOT found\n");
574 return -1;
575 }
576
577 /* connect with this server's socket */
578 memset(&pun, 0, sizeof(struct sockaddr_un));
579 pun.sun_family = AF_LOCAL;
580 strncpy(pun.sun_path, server_uds_name, sizeof (pun.sun_path));
581 g_free(server_uds_name);
582
583 if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
584 perror("[dpi::socket]");
585 else if (connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
586 err = errno;
587 SockFD = -1;
588 MSG("[dpi::connect] errno:%d %s\n", errno, g_strerror(errno));
589 if (retry) {
590 switch (err) {
591 case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
592 /* the server may crash and its socket name survive */
593 unlink(pun.sun_path);
594 SockFD = Dpi_connect_socket(server_name, FALSE);
595 break;
596 }
597 }
598 }
599
600 return SockFD;
601 }
602
603
604 /*
605 * CCC function for the Dpi module
606 */
607 void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
608 void *Data1, void *Data2)
609 {
610 GSList *list;
611 gint SockFD = -1, st;
612
613 a_Chain_debug_msg("a_Dpi_ccc", Op, Branch, Dir);
614
615 if (Branch == 1) {
616 if (Dir == BCK) {
617 /* Cache request, return the FD. */
618 /* (Data1 = Url; Data2 = Web) */
619 switch (Op) {
620 case OpStart:
621 /* We'll know the FD later, enqueue this connection.
622 * (The Url is used as an identifier for the connection) */
623 Info->LocalKey = a_Url_dup(Data1);
624 PendingNodes = g_slist_append(PendingNodes, (gpointer)Info);
625 break;
626 }
627 } else { /* FWD */
628 switch (Op) {
629 case OpEnd:
630 /* End this requesting branch */
631 a_Url_free(Info->LocalKey);
632 a_Chain_fcb(OpEnd, Info, NULL, NULL);
633 break;
634 case OpAbort:
635 list = g_slist_find_custom(
636 PendingNodes, Info->LocalKey, Dpi_node_cmp);
637 if (list) {
638 /* The connection is not pending anymore */
639 PendingNodes = g_slist_remove(PendingNodes, list->data);
640 }
641 a_Url_free(Info->LocalKey);
642 a_Chain_fcb(OpAbort, Info, NULL, NULL);
643 break;
644 }
645 }
646
647 } else if (Branch == 2) {
648 if (Dir == BCK) {
649 /* Send commands to dpi-server */
650 switch (Op) {
651 case OpStart:
652 if ((st = Dpi_check_dpid()) == 0) {
653 SockFD = Dpi_connect_socket(Data1, TRUE);
654 if (SockFD != -1) {
655 gint *fd = g_new(gint, 1);
656 *fd = SockFD;
657 Info->LocalKey = fd;
658 a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 3, 2);
659 a_Chain_bcb(OpStart, Info, Info->LocalKey, NULL);
660 /* tell the capi to start the receiving branch */
661 a_Chain_fcb(OpSend, Info, Info->LocalKey, "SockFD");
662 }
663 }
664
665 if (st == 0 && SockFD != -1)
666 a_Chain_fcb(OpSend, Info, NULL, (void*)"DpidOK");
667 else if (st == 1)
668 a_Chain_fcb(OpSend, Info, NULL, (void*)"DpidEAGAIN");
669 else {
670 DEBUG_MSG(4, "dpi.c: ERROR, can't start dpi daemon\n");
671 a_Dpi_ccc(OpAbort, 2, FWD, Info, "ERR_dpid", NULL);
672 }
673 break;
674 case OpSend:
675 a_Chain_bcb(OpSend, Info, Data1, NULL);
676 break;
677 case OpEnd:
678 a_Chain_bcb(OpEnd, Info, NULL, NULL);
679 g_free(Info->LocalKey);
680 g_free(Info);
681 break;
682 case OpAbort:
683 MSG("a_Dpi_ccc: OpAbort[2B], Not implemented\n");
684 g_free(Info->LocalKey);
685 g_free(Info);
686 break;
687 }
688 } else { /* FWD */
689 /* Send commands to dpi-server (status) */
690 switch (Op) {
691 case OpEnd:
692 a_Chain_del_link(Info, BCK);
693 g_free(Info);
694 break;
695 case OpSend:
696 case OpAbort:
697 a_Chain_fcb(OpAbort, Info, Data1, NULL);
698 g_free(Info);
699 break;
700 }
701 }
702
703 } else if (Branch == 3) {
704 if (Dir == FWD) {
705 /* Receiving from server */
706 switch (Op) {
707 case OpSend:
708 Dpi_process_io(IORead, Data1, Info->LocalKey);
709 break;
710 case OpEnd:
711 a_Chain_del_link(Info, BCK);
712 Dpi_process_io(IOClose, Data1, Info->LocalKey);
713 Dpi_conn_data_free(Info->LocalKey);
714 a_Chain_fcb(OpEnd, Info, NULL, NULL);
715 break;
716 case OpAbort:
717 MSG(" Not implemented\n");
718 break;
719 }
720 } else { /* BCK */
721 switch (Op) {
722 case OpStart:
723 {
724 IOData_t *io2;
725 Info->LocalKey = Dpi_conn_data_new(Info);
726 io2 = a_IO_new(IORead, *(int*)Data1); /* SockFD */
727 a_IO_set_buf(io2, NULL, IOBufLen);
728 a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 3);
729 a_Chain_bcb(OpStart, Info, io2, NULL);
730 break;
731 }
732 case OpSend:
733 /* now that we have the FD, resume the connection.
734 * (Data1 = FD, Data2 = Url) */
735 list = g_slist_find_custom(PendingNodes, Data2, Dpi_node_cmp);
736 if (list) {
737 ChainLink *P_Info = list->data;
738 /* Tell the cache to start the receiving CCC */
739 a_Chain_fcb(OpSend, P_Info, Data1, NULL);
740 /* The connection is not pending anymore */
741 PendingNodes = g_slist_remove(PendingNodes, list->data);
742 /* End this requesting CCC */
743 a_Dpi_ccc(OpEnd, 1, FWD, P_Info, NULL, NULL);
744 }
745 break;
746 case OpStop:
747 /* Stop transfer, abort the pending node.
748 * (Data1 = Url, Data2 = NULL) */
749 list = g_slist_find_custom(PendingNodes, Data1, Dpi_node_cmp);
750 if (list) {
751 ChainLink *P_Info = list->data;
752 /* Abort this requesting CCC */
753 a_Dpi_ccc(OpAbort, 1, FWD, P_Info, Data1, NULL);
754 }
755 break;
756 case OpAbort:
757 MSG(" Not implemented\n");
758 break;
759 }
760 }
761
762 } else if (Branch == 4) {
763 /* Unused */
764 g_assert_not_reached();
765 }
766 }
767
768 /*! Send DpiBye to dpid
769 * Note: currently disabled. Maybe it'd be better to have a
770 * dpid_idle_timeout variable in the config file.
771 */
772 void a_Dpi_bye_dpid()
773 {
774 char *DpiBye_cmd;
775 struct sockaddr_un sa;
776 size_t sun_path_len, addr_len;
777 char *srs_name;
778 int new_socket;
779
780 srs_name = Dpi_get_dpid_uds_name();
781 sun_path_len = sizeof(sa.sun_path);
782 addr_len = sizeof(sa);
783
784 sa.sun_family = AF_LOCAL;
785
786 if ((new_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
787 DEBUG_MSG(4, "a_Dpi_bye_dpid: %s\n", g_strerror(errno));
788 }
789 strncpy(sa.sun_path, srs_name, sizeof (sa.sun_path));
790 if (connect(new_socket, (struct sockaddr *) &sa, addr_len) == -1) {
791 DEBUG_MSG(4, "a_Dpi_bye_dpid: %s\n", g_strerror(errno));
792 fprintf(stderr, "%s\n", sa.sun_path);
793 }
794 DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
795 (void) write(new_socket, DpiBye_cmd, strlen(DpiBye_cmd));
796 g_free(DpiBye_cmd);
797 Dpi_close_fd(new_socket);
798 }
799
800
801 /*
802 * Send a command to a dpi server, and block until the answer is got.
803 * Return value: the dpip tag answer as an string, NULL on error.
804 */
805 char *a_Dpi_send_blocking_cmd(const gchar *server_name, const gchar *cmd)
806 {
807 gint i, cst, SockFD;
808 ssize_t st;
809 gchar buf[16384], *retval = NULL;
810
811 /* test the dpid, and wait a bit for it to start if necessary */
812 for (i = 0; (cst = Dpi_check_dpid()) == 1 && i < 2; ++i)
813 sleep(1);
814
815 if (cst != 0)
816 return retval;
817
818 SockFD = Dpi_connect_socket(server_name, TRUE);
819 if (SockFD != -1) {
820 /* todo: handle the case of (st < strlen(cmd)) */
821 do
822 st = write(SockFD, cmd, strlen(cmd));
823 while (st == -1 && errno == EINTR);
824
825 /* todo: if the answer is too long... */
826 do
827 st = read(SockFD, buf, 16384);
828 while (st < 0 && errno == EINTR);
829
830 if (st == -1)
831 perror("[a_Dpi_send_blocking_cmd]");
832 else if (st > 0)
833 retval = g_strndup(buf, (guint)st);
834
835 Dpi_close_fd(SockFD);
836
837 } else {
838 perror("[a_Dpi_send_blocking_cmd]");
839 }
840
841 return retval;
842 }
843
0 /*
1 * File: http.c
2 *
3 * Copyright (C) 2000, 2001 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * HTTP connect functions
13 */
14
15
16 #include <config.h>
17
18 #include <unistd.h>
19 #include <errno.h> /* for errno */
20 #include <string.h> /* for strstr */
21 #include <stdlib.h>
22 #include <signal.h>
23 #include <fcntl.h>
24 #include <sys/wait.h>
25 #include <sys/socket.h> /* for lots of socket stuff */
26 #include <netinet/in.h> /* for ntohl and stuff */
27 #include <arpa/inet.h> /* for inet_ntop */
28
29 #include "IO.h"
30 #include "Url.h"
31 #include "../klist.h"
32 #include "../dns.h"
33 #include "../cache.h"
34 #include "../web.h"
35 #include "../interface.h"
36 #include "../cookies.h"
37 #include "../prefs.h"
38 #include "../misc.h"
39
40 /* Used to send a message to the bw's status bar */
41 #define BW_MSG(web, root, fmt...) \
42 G_STMT_START { \
43 if (a_Web_valid((web)) && (!(root) || (web)->flags & WEB_RootUrl)) \
44 a_Interface_msg((web)->bw, fmt); \
45 } G_STMT_END
46
47 #define DEBUG_LEVEL 5
48 #include "../debug.h"
49
50
51
52 /* 'Url' and 'web' are just references (no need to deallocate them here). */
53 typedef struct {
54 gint SockFD;
55 const DilloUrl *Url; /* reference to original URL */
56 guint port; /* need a separate port in order to support PROXY */
57 gboolean use_proxy; /* indicates whether to use proxy or not */
58 DilloWeb *web; /* reference to client's web structure */
59 GSList *addr_list; /* Holds the DNS answer */
60 GSList *addr_list_iter; /* Points to address currently being used */
61 GIOChannel *GioCh; /* GIOChannel to monitor the connecting process */
62 gint Err; /* Holds the errno of the connect() call */
63 ChainLink *Info; /* Used for CCC asynchronous operations */
64 } SocketData_t;
65
66
67 /*
68 * Local data
69 */
70 static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
71 * SocketData_t structures. */
72
73 static DilloUrl *HTTP_Proxy = NULL;
74 static gchar *HTTP_Proxy_Auth_base64 = NULL;
75
76 /*
77 * Initialize proxy vars.
78 */
79 gint a_Http_init(void)
80 {
81 gchar *env_proxy = getenv("http_proxy");
82
83 if (env_proxy && strlen(env_proxy))
84 HTTP_Proxy = a_Url_new(env_proxy, NULL, 0, 0, 0);
85 if (!HTTP_Proxy && prefs.http_proxy)
86 HTTP_Proxy = a_Url_dup(prefs.http_proxy);
87
88 /* This allows for storing the proxy password in "user:passwd" format
89 * in dillorc, but as this constitutes a security problem, it was disabled.
90 *
91 if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
92 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
93 */
94 return 0;
95 }
96
97 /*
98 * Tell whether the proxy auth is already set (user:password)
99 * Return: 1 Yes, 0 No
100 */
101 gint a_Http_proxy_auth(void)
102 {
103 return (HTTP_Proxy_Auth_base64 ? 1 : 0);
104 }
105
106 /*
107 * Activate entered proxy password for HTTP.
108 */
109 void a_Http_set_proxy_passwd(gchar *str)
110 {
111 gchar *http_proxyauth = g_strconcat(prefs.http_proxyuser, ":", str, NULL);
112 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(http_proxyauth);
113 g_free(http_proxyauth);
114 }
115
116 /*
117 * Forward declarations
118 */
119 static void Http_send_query(ChainLink *Info, SocketData_t *S);
120 static int Http_connect_socket(ChainLink *Info);
121
122 /*
123 * Create and init a new SocketData_t struct, insert into ValidSocks,
124 * and return a primary key for it.
125 */
126 static gint Http_sock_new(void)
127 {
128 SocketData_t *S = g_new0(SocketData_t, 1);
129 return a_Klist_insert(&ValidSocks, (gpointer)S);
130 }
131
132 /*
133 * Free SocketData_t struct
134 */
135 static void Http_socket_free(gint SKey)
136 {
137 SocketData_t *S;
138
139 if ((S = a_Klist_get_data(ValidSocks, SKey))) {
140 a_Klist_remove(ValidSocks, SKey);
141 if (S->GioCh)
142 g_io_channel_unref(S->GioCh);
143 g_free(S);
144 }
145 }
146
147 /*
148 * Make the http query string
149 */
150 gchar *a_Http_make_query_str(const DilloUrl *url, gboolean use_proxy)
151 {
152 gchar *str, *ptr, *cookies;
153 GString *s_port = g_string_new(""),
154 *query = g_string_new(""),
155 *full_path = g_string_new(""),
156 *proxy_auth = g_string_new("");
157
158 /* Sending the default port in the query may cause a 302-answer. --Jcid */
159 if (URL_PORT(url) && URL_PORT(url) != DILLO_URL_HTTP_PORT)
160 g_string_sprintfa(s_port, ":%d", URL_PORT(url));
161
162 if (use_proxy) {
163 g_string_sprintfa(full_path, "%s%s",
164 URL_STR(url),
165 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
166 if ((ptr = strrchr(full_path->str, '#')))
167 g_string_truncate(full_path, ptr - full_path->str);
168 if (HTTP_Proxy_Auth_base64)
169 g_string_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
170 HTTP_Proxy_Auth_base64);
171 } else {
172 g_string_sprintfa(full_path, "%s%s%s%s",
173 URL_PATH(url),
174 URL_QUERY_(url) ? "?" : "",
175 URL_QUERY(url),
176 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
177 }
178
179 cookies = a_Cookies_get(url);
180 if ( URL_FLAGS(url) & URL_Post ){
181 g_string_sprintfa(
182 query,
183 "POST %s HTTP/1.0\r\n"
184 "Host: %s%s\r\n"
185 "%s"
186 "User-Agent: Dillo/%s\r\n"
187 "Cookie2: $Version=\"1\"\r\n"
188 "%s"
189 "Content-type: application/x-www-form-urlencoded\r\n"
190 "Content-length: %ld\r\n"
191 "\r\n"
192 "%s",
193 full_path->str, URL_HOST(url), s_port->str,
194 proxy_auth->str, VERSION, cookies,
195 (glong)strlen(URL_DATA(url)),
196 URL_DATA(url));
197
198 } else {
199 g_string_sprintfa(
200 query,
201 "GET %s HTTP/1.0\r\n"
202 "%s"
203 "Host: %s%s\r\n"
204 "%s"
205 "User-Agent: Dillo/%s\r\n"
206 "Cookie2: $Version=\"1\"\r\n"
207 "%s"
208 "\r\n",
209 full_path->str,
210 (URL_FLAGS(url) & URL_E2EReload) ?
211 "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
212 URL_HOST(url), s_port->str,
213 proxy_auth->str,
214 VERSION,
215 cookies);
216 }
217 g_free(cookies);
218
219 str = query->str;
220 g_string_free(query, FALSE);
221 g_string_free(s_port, TRUE);
222 g_string_free(full_path, TRUE);
223 g_string_free(proxy_auth, TRUE);
224 DEBUG_MSG(4, "Query:\n%s", str);
225 return str;
226 }
227
228 /*
229 * This function is called after the socket has been successfuly connected,
230 * or upon an error condition on the connecting process.
231 * Task: use the socket to send the HTTP-query and expect its answer
232 */
233 static gboolean
234 Http_use_socket(GIOChannel *src, GIOCondition cond, gpointer data)
235 {
236 ChainLink *Info;
237 SocketData_t *S;
238 gint SKey = GPOINTER_TO_INT(data);
239
240 DEBUG_MSG(3, "Http_use_socket: %s [errno %d] [GIOcond %d]\n",
241 g_strerror(errno), errno, cond);
242
243 /* This check is required because glib may asynchronously
244 * call this function with data that's no longer used --Jcid */
245 if ( !(S = a_Klist_get_data(ValidSocks, SKey)) )
246 return FALSE;
247
248 Info = S->Info;
249 if ( cond & G_IO_HUP ) {
250 DEBUG_MSG(3, "--Connection broken\n");
251 /* get rid of S->GioCh */
252 g_io_channel_close(S->GioCh);
253 g_io_channel_unref(S->GioCh);
254 S->GioCh = NULL;
255 S->addr_list_iter = S->addr_list_iter->next;
256 if (!S->addr_list_iter || Http_connect_socket(Info) < 0) {
257 BW_MSG(S->web, 0, "ERROR: unable to connect to remote host");
258 Http_socket_free(SKey);
259 a_Chain_fcb(OpAbort, Info, NULL, NULL);
260 }
261 } else if ( S->Err ) {
262 BW_MSG(S->web, 0, "ERROR: %s", g_strerror(S->Err));
263 DEBUG_MSG(3, "Http_use_socket ERROR: %s\n", g_strerror(S->Err));
264 a_Chain_fcb(OpAbort, Info, NULL, NULL);
265 g_io_channel_close(S->GioCh);
266 Http_socket_free(SKey);
267 } else if ( cond & G_IO_OUT ) {
268 DEBUG_MSG(3, "--Connection established\n");
269 g_io_channel_unref(S->GioCh);
270 S->GioCh = NULL;
271 Http_send_query(Info, S);
272 }
273 return FALSE;
274 }
275
276 /*
277 * This function gets called after the DNS succeeds solving a hostname.
278 * Task: Finish socket setup and start connecting the socket.
279 * Return value: 0 on success; -1 on error.
280 */
281 static int Http_connect_socket(ChainLink *Info)
282 {
283 gint status;
284 guint watch_id;
285 #ifdef ENABLE_IPV6
286 struct sockaddr_in6 name;
287 #else
288 struct sockaddr_in name;
289 #endif
290 SocketData_t *S;
291 DilloHost *dh;
292 socklen_t socket_len = 0;
293
294 S = a_Klist_get_data(ValidSocks, GPOINTER_TO_INT(Info->LocalKey));
295
296 dh = S->addr_list_iter->data;
297
298 if ( (S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0 ) {
299 S->Err = errno;
300 DEBUG_MSG(5, "Http_connect_socket ERROR: %s\n", g_strerror(errno));
301 return -1;
302 }
303 /* set NONBLOCKING and close on exec. */
304 fcntl(S->SockFD, F_SETFL,
305 O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
306 fcntl(S->SockFD, F_SETFD,
307 FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
308
309 /* Some OSes require this... */
310 memset(&name, 0, sizeof(name));
311 /* Set remaining parms. */
312 switch (dh->af) {
313 case AF_INET:
314 {
315 struct sockaddr_in *sin = (struct sockaddr_in *)&name;
316 socket_len = sizeof(struct sockaddr_in);
317 sin->sin_family = dh->af;
318 sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
319 memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
320 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
321 DEBUG_MSG(5, "Connecting to %s\n", inet_ntoa(sin->sin_addr));
322 break;
323 }
324 #ifdef ENABLE_IPV6
325 case AF_INET6:
326 {
327 char buf[128];
328 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
329 socket_len = sizeof(struct sockaddr_in6);
330 sin6->sin6_family = dh->af;
331 sin6->sin6_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
332 memcpy(&sin6->sin6_addr, dh->data, dh->alen);
333 inet_ntop(dh->af, dh->data, buf, sizeof(buf));
334 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
335 DEBUG_MSG(5, "Connecting to %s\n", buf);
336 break;
337 }
338 #endif
339 }
340
341 if (!S->GioCh)
342 S->GioCh = g_io_channel_unix_new(S->SockFD);
343 watch_id = g_io_add_watch(S->GioCh, G_IO_ERR | G_IO_HUP | G_IO_OUT,
344 Http_use_socket, Info->LocalKey);
345 status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
346 if ( status == -1 && errno != EINPROGRESS ) {
347 DEBUG_MSG(5, "Http_connect_socket ERROR: %s\n", g_strerror(errno));
348 g_io_channel_close(S->GioCh);
349 g_source_remove(watch_id);
350 S->Err = errno;
351 return -1;
352 }
353
354 BW_MSG(S->web, 1, "Contacting host...");
355
356 return 0; /* Success */
357 }
358
359
360 /*
361 * Create and submit the HTTP query to the IO engine
362 */
363 static void Http_send_query(ChainLink *Info, SocketData_t *S)
364 {
365 IOData_t *io;
366 gchar *query;
367 void *link;
368
369 /* Create the query */
370 query = a_Http_make_query_str(S->Url, S->use_proxy);
371
372 /* send query */
373 BW_MSG(S->web, 1, "Sending query to %s...", URL_HOST_(S->Url));
374 io = a_IO_new(IOWrite, S->SockFD);
375 a_IO_set_buf(io, query, strlen(query));
376 io->Flags |= IOFlag_FreeIOBuf;
377 link = a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 2);
378 a_Chain_bcb(OpStart, Info, io, NULL);
379 a_Chain_bcb(OpSend, Info, io, NULL);
380
381 /* Tell the cache to start the receiving CCC for the answer */
382 a_Chain_fcb(OpSend, Info, &S->SockFD, NULL);
383 }
384
385 /*
386 * Test proxy settings and check the no_proxy domains list
387 * Return value: whether to use proxy or not.
388 */
389 static gint Http_must_use_proxy(const DilloUrl *url)
390 {
391 gint i;
392 gchar **NoProxyVec = prefs.no_proxy_vec;
393
394 if (HTTP_Proxy) {
395 if (NoProxyVec)
396 for (i = 0; NoProxyVec[i]; ++i)
397 if (strstr(URL_AUTHORITY(url), NoProxyVec[i]))
398 return 0;
399 return 1;
400 }
401 return 0;
402 }
403
404 /*
405 * Asynchronously create a new http connection for 'Url'
406 * We'll set some socket parameters; the rest will be set later
407 * when the IP is known.
408 * ( Data1 = Requested Url; Data2 = Web structure )
409 * Return value: 0 on success, -1 otherwise
410 */
411 static gint Http_get(ChainLink *Info, void *Data1, void *Data2)
412 {
413 void *link;
414 const DilloUrl *Url = Data1;
415 SocketData_t *S;
416 gchar *hostname;
417
418 S = a_Klist_get_data(ValidSocks, GPOINTER_TO_INT(Info->LocalKey));
419
420 /* Reference Info data */
421 S->Info = Info;
422 /* Reference Web data */
423 S->web = Data2;
424
425 /* Proxy support */
426 if (Http_must_use_proxy(Url)) {
427 hostname = g_strdup(URL_HOST(HTTP_Proxy));
428 S->port = URL_PORT(HTTP_Proxy);
429 S->use_proxy = TRUE;
430 } else {
431 hostname = g_strdup(URL_HOST(Url));
432 S->port = URL_PORT(Url);
433 S->use_proxy = FALSE;
434 }
435
436 /* Set more socket parameters */
437 S->Url = Url;
438
439 /* Let the user know what we'll do */
440 BW_MSG(S->web, 1, "DNS solving %s", URL_HOST_(S->Url));
441
442 /* Let the DNS engine solve the hostname, and when done,
443 * we'll try to connect the socket */
444 link = a_Chain_link_new(Info, a_Http_ccc, BCK, a_Dns_ccc, 1, 1);
445 a_Chain_bcb(OpStart, Info, hostname, NULL);
446
447 g_free(hostname);
448 return 0;
449 }
450
451 /*
452 * CCC function for the HTTP module
453 */
454 void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
455 void *Data1, void *Data2)
456 {
457 gint SKey = GPOINTER_TO_INT(Info->LocalKey);
458 SocketData_t *S = a_Klist_get_data(ValidSocks, SKey);
459
460 a_Chain_debug_msg("a_Http_ccc", Op, Branch, Dir);
461
462 if ( Branch == 1 ) {
463 if (Dir == BCK) {
464 /* HTTP query branch */
465 switch (Op) {
466 case OpStart:
467 SKey = Http_sock_new();
468 Info->LocalKey = GINT_TO_POINTER(SKey);
469 if (Http_get(Info, Data1, Data2) < 0) {
470 S = a_Klist_get_data(ValidSocks, SKey);
471 BW_MSG(S->web, 1, "ERROR: %s", g_strerror(S->Err));
472 Http_socket_free(SKey);
473 a_Chain_fcb(OpAbort, Info, NULL, NULL);
474 }
475 break;
476 case OpAbort:
477 /* something bad happened... */
478 DEBUG_MSG(2, "Http: OpAbort [1B]\n");
479 Http_socket_free(SKey);
480 a_Chain_bcb(OpAbort, Info, NULL, NULL);
481 g_free(Info);
482 break;
483 }
484 } else { /* FWD */
485 /* DNS answer branch */
486 switch (Op) {
487 case OpSend:
488 /* Successful DNS answer; save the IP */
489 if (S) {
490 S->addr_list = (GSList *)Data1;
491 S->addr_list_iter = S->addr_list;
492 }
493 break;
494 case OpEnd:
495 if (S) {
496 /* Unlink DNS_Info */
497 a_Chain_del_link(Info, BCK);
498 /* start connecting the socket */
499 if (Http_connect_socket(Info) < 0) {
500 BW_MSG(S->web, 1, "ERROR: %s", g_strerror(S->Err));
501 Http_socket_free(SKey);
502 a_Chain_fcb(OpAbort, Info, NULL, NULL);
503 }
504 }
505 break;
506 case OpAbort:
507 /* DNS wasn't able to resolve the hostname */
508 if (S) {
509 /* Unlink DNS_Info */
510 a_Chain_del_link(Info, BCK);
511 BW_MSG(S->web, 0, "ERROR: Dns can't solve %s",
512 (S->use_proxy) ? URL_HOST_(HTTP_Proxy) : URL_HOST_(S->Url));
513 Http_socket_free(SKey);
514 /* send abort message to higher-level functions */
515 a_Chain_fcb(OpAbort, Info, NULL, NULL);
516 }
517 break;
518 }
519 }
520
521 } else if ( Branch == 2 ) {
522 if (Dir == FWD) {
523 /* IO send-query status branch */
524 switch (Op) {
525 case OpStart:
526 /* LocalKey was set by branch 1 */
527 break;
528 case OpSend:
529 /* Not used */
530 break;
531 case OpEnd:
532 /* finished sending the HTTP query */
533 if (S) {
534 a_Chain_del_link(Info, BCK);
535 a_Chain_fcb(OpEnd, Info, &S->SockFD, (void *)S->Url);
536 BW_MSG(S->web, 1, "Query sent, waiting for reply...");
537 Http_socket_free(SKey);
538 }
539 break;
540 case OpAbort:
541 /* something bad happened... */
542 /* unlink IO_Info */
543 if (S) {
544 a_Chain_del_link(Info, BCK);
545 Http_socket_free(SKey);
546 a_Chain_fcb(OpAbort, Info, NULL, NULL);
547 }
548 break;
549 }
550 }
551 }
552 }
553
554
555
556 /*
557 * Deallocate memory used by http module
558 * (Call this one at exit time)
559 */
560 void a_Http_freeall(void)
561 {
562 a_Klist_free(&ValidSocks);
563 a_Url_free(HTTP_Proxy);
564 g_free(HTTP_Proxy_Auth_base64);
565 }
0 /*
1 * File: mime.c
2 *
3 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "mime.h"
12 #include "../msg.h"
13 #include "../list.h"
14
15
16 typedef struct {
17 const char *Name; /* MIME type name */
18 Viewer_t Data; /* Pointer to a function */
19 } MimeItem_t;
20
21
22 /*
23 * Local data
24 */
25 static gint MimeMinItemsSize = 0, MimeMinItemsMax = 8;
26 static MimeItem_t *MimeMinItems = NULL;
27
28 static gint MimeMajItemsSize = 0, MimeMajItemsMax = 8;
29 static MimeItem_t *MimeMajItems = NULL;
30
31
32 /*
33 * Add a specific MIME type (as "image/png") to our viewer list
34 * 'Key' is the content-type string that identifies the MIME type
35 * 'Method' is the function that handles it
36 */
37 static gint Mime_add_minor_type(const char *Key, Viewer_t Method)
38 {
39 a_List_add(MimeMinItems, MimeMinItemsSize, MimeMinItemsMax);
40 MimeMinItems[MimeMinItemsSize].Name = Key;
41 MimeMinItems[MimeMinItemsSize].Data = Method;
42 MimeMinItemsSize++;
43 return 0;
44 }
45
46 /*
47 * Add a major MIME type (as "text") to our viewer list
48 * 'Key' is the content-type string that identifies the MIME type
49 * 'Method' is the function that handles it
50 */
51 static gint Mime_add_major_type(const char *Key, Viewer_t Method)
52 {
53 a_List_add(MimeMajItems, MimeMajItemsSize, MimeMajItemsMax);
54 MimeMajItems[MimeMajItemsSize].Name = Key;
55 MimeMajItems[MimeMajItemsSize].Data = Method;
56 MimeMajItemsSize++;
57 return 0;
58 }
59
60 /*
61 * Search the list of specific MIME viewers, for a Method that matches 'Key'
62 * 'Key' is the content-type string that identifies the MIME type
63 */
64 static Viewer_t Mime_minor_type_fetch(const char *Key, guint Size)
65 {
66 gint i;
67
68 if (Size) {
69 for ( i = 0; i < MimeMinItemsSize; ++i )
70 if ( g_strncasecmp(Key, MimeMinItems[i].Name, Size) == 0 )
71 return MimeMinItems[i].Data;
72 }
73 return NULL;
74 }
75
76 /*
77 * Search the list of major MIME viewers, for a Method that matches 'Key'
78 * 'Key' is the content-type string that identifies the MIME type
79 */
80 static Viewer_t Mime_major_type_fetch(const char *Key, guint Size)
81 {
82 gint i;
83
84 if (Size) {
85 for ( i = 0; i < MimeMajItemsSize; ++i )
86 if ( g_strncasecmp(Key, MimeMajItems[i].Name, Size) == 0 )
87 return MimeMajItems[i].Data;
88 }
89 return NULL;
90 }
91
92
93 /*
94 * Initializes Mime module and, sets the supported Mime types.
95 */
96 void a_Mime_init()
97 {
98 #ifdef ENABLE_GIF
99 Mime_add_minor_type("image/gif", a_Gif_image);
100 #endif
101 #ifdef ENABLE_JPEG
102 Mime_add_minor_type("image/jpeg", a_Jpeg_image);
103 Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
104 Mime_add_minor_type("image/jpg", a_Jpeg_image);
105 #endif
106 #ifdef ENABLE_PNG
107 Mime_add_minor_type("image/png", a_Png_image);
108 Mime_add_minor_type("image/x-png", a_Png_image); /* deprecated */
109 #endif
110 Mime_add_minor_type("text/html", a_Html_text);
111
112 /* Add a major type to handle all the text stuff */
113 Mime_add_major_type("text", a_Plain_text);
114 }
115
116
117 /*
118 * Call the handler for the MIME type to set Call and Data as appropriate
119 *
120 * Return Value:
121 * On success: a new Dw (and Call and Data properly set).
122 * On failure: NULL (and Call and Data untouched).
123 */
124 DwWidget *a_Mime_set_viewer(const char *content_type, void *Ptr,
125 CA_Callback_t *Call, void **Data)
126 {
127
128 Viewer_t viewer;
129 guint MinSize, MajSize, i;
130 const char *str = content_type;
131
132 MajSize = 0;
133 for (i = 0; str[i] && str[i] != ' ' && str[i] != ';'; ++i) {
134 if (str[i] == '/' && !MajSize)
135 MajSize = i;
136 }
137 MinSize = i;
138
139 /* Try minor type */
140 viewer = Mime_minor_type_fetch(content_type, MinSize);
141 if (viewer)
142 return viewer(content_type, Ptr, Call, Data);
143
144 /* Try major type */
145 viewer = Mime_major_type_fetch(content_type, MajSize);
146 if (viewer)
147 return viewer(content_type, Ptr, Call, Data);
148
149 /* Type not handled */
150 return NULL;
151 }
0 /*
1 MIME type handlers
2 Randall Maas
3 1999
4 */
5 #ifndef __MIME_H__
6 #define __MIME_H__
7
8 #include <config.h>
9 #include <stddef.h>
10 #include <gtk/gtk.h>
11 #include "../dw_widget.h"
12 #include "../cache.h"
13
14 typedef DwWidget* (*Viewer_t) (const char*, void*, CA_Callback_t*, void**);
15
16 /*
17 * Function prototypes defined elsewhere
18 */
19 DwWidget* a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
20 void **Data);
21 DwWidget* a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
22 void **Data);
23 #ifdef ENABLE_JPEG
24 DwWidget* a_Jpeg_image(const char *Type,void *web, CA_Callback_t *Call,
25 void **Data);
26 #endif
27 #ifdef ENABLE_PNG
28 DwWidget* a_Png_image (const char *Type,void *web, CA_Callback_t *Call,
29 void **Data);
30 #endif
31 #ifdef ENABLE_GIF
32 DwWidget* a_Gif_image (const char *Type,void *web, CA_Callback_t *Call,
33 void **Data);
34 #endif
35
36 /*
37 * Functions defined inside Mime module
38 */
39 void a_Mime_init(void);
40 DwWidget* a_Mime_set_viewer(const char *content_type, void *Ptr,
41 CA_Callback_t *Call, void **Data);
42
43 #endif /* __MIME_H__ */
0 /*
1 * File: proto.c
2 *
3 * Copyright (C) 2003 Jorge Arellano Cid <jcid@dillo.org>,
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /* This module may be programmed to manage dpi-programs. */
12
0 AM_CPPFLAGS=-DDILLORC_SYS='"$(sysconfdir)/dillorc"' @LIBJPEG_CPPFLAGS@
1 AM_CFLAGS = @GTK_CFLAGS@ @LIBPNG_CFLAGS@
2
3 SUBDIRS = IO
4
5 bin_PROGRAMS = dillo
6
7 dillo_LDADD = ../dpip/libDpip.a IO/libDio.a @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @GTK_LIBS@ @LIBZ_LIBS@ @LIBPTHREAD_LIBS@
8 dillo_LDFLAGS = @LIBJPEG_LDFLAGS@ @LIBPTHREAD_LDFLAGS@
9
10 dillo_SOURCES = \
11 msg.h \
12 acconfig.h \
13 chain.h \
14 chain.c \
15 commands.c \
16 commands.h \
17 cache.c \
18 cache.h \
19 capi.c \
20 capi.h \
21 debug.h \
22 dw.h \
23 dw.c \
24 dw_aligned_page.c \
25 dw_aligned_page.h \
26 dw_bullet.c \
27 dw_bullet.h \
28 dw_button.c \
29 dw_button.h \
30 dw_container.c \
31 dw_container.h \
32 dw_embed_gtk.c \
33 dw_embed_gtk.h \
34 dw_ext_iterator.h \
35 dw_ext_iterator.c \
36 dw_gtk_scrolled_frame.c \
37 dw_gtk_scrolled_frame.h \
38 dw_gtk_scrolled_window.c \
39 dw_gtk_scrolled_window.h \
40 dw_gtk_statuslabel.c \
41 dw_gtk_statuslabel.h \
42 dw_gtk_viewport.c \
43 dw_gtk_viewport.h \
44 dw_hruler.c \
45 dw_hruler.h \
46 dw_image.c \
47 dw_image.h \
48 dw_list_item.c \
49 dw_list_item.h \
50 dw_marshal.c \
51 dw_marshal.h \
52 dw_page.c \
53 dw_page.h \
54 dw_style.c \
55 dw_style.h \
56 dw_table.c \
57 dw_table.h \
58 dw_table_cell.c \
59 dw_table_cell.h \
60 dw_tooltip.c \
61 dw_tooltip.h \
62 dw_widget.c \
63 dw_widget.h \
64 findtext.c \
65 findtext.h \
66 selection.c \
67 selection.h \
68 web.c \
69 web.h \
70 progressbar.c \
71 progressbar.h \
72 dillo.c \
73 dillo.h \
74 bookmark.c \
75 bookmark.h \
76 browser.h \
77 dicache.c \
78 dicache.h \
79 dns.c \
80 dns.h \
81 gtk_ext_button.c \
82 gtk_ext_button.h \
83 gtk_ext_menu.c \
84 gtk_ext_menu.h \
85 gtk_ext_menu_item.c \
86 gtk_ext_menu_item.h \
87 gtk_menu_title.c \
88 gtk_menu_title.h \
89 gif.c \
90 jpeg.c \
91 png.c \
92 html.c \
93 html.h \
94 image.c \
95 image.h \
96 misc.c \
97 misc.h \
98 interface.h \
99 interface.c \
100 history.c \
101 history.h \
102 nav.c \
103 nav.h \
104 plain.c \
105 menu.c \
106 menu.h \
107 prefs.c \
108 prefs.h \
109 colors.c \
110 colors.h \
111 bitvec.c \
112 bitvec.h \
113 klist.c \
114 klist.h \
115 strbuf.c \
116 strbuf.h \
117 url.c \
118 url.h \
119 cookies.c \
120 cookies.h \
121 list.h \
122 binaryconst.h \
123 pixmaps.h \
124 dpiapi.c \
125 dpiapi.h
126
127 EXTRA_DIST = chg srch
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(dillo_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 bin_PROGRAMS = dillo$(EXEEXT)
42 subdir = src
43 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
44 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
45 am__aclocal_m4_deps = $(top_srcdir)/configure.in
46 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
47 $(ACLOCAL_M4)
48 mkinstalldirs = $(install_sh) -d
49 CONFIG_HEADER = $(top_builddir)/config.h
50 CONFIG_CLEAN_FILES =
51 am__installdirs = "$(DESTDIR)$(bindir)"
52 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
53 PROGRAMS = $(bin_PROGRAMS)
54 am_dillo_OBJECTS = chain.$(OBJEXT) commands.$(OBJEXT) cache.$(OBJEXT) \
55 capi.$(OBJEXT) dw.$(OBJEXT) dw_aligned_page.$(OBJEXT) \
56 dw_bullet.$(OBJEXT) dw_button.$(OBJEXT) dw_container.$(OBJEXT) \
57 dw_embed_gtk.$(OBJEXT) dw_ext_iterator.$(OBJEXT) \
58 dw_gtk_scrolled_frame.$(OBJEXT) \
59 dw_gtk_scrolled_window.$(OBJEXT) dw_gtk_statuslabel.$(OBJEXT) \
60 dw_gtk_viewport.$(OBJEXT) dw_hruler.$(OBJEXT) \
61 dw_image.$(OBJEXT) dw_list_item.$(OBJEXT) dw_marshal.$(OBJEXT) \
62 dw_page.$(OBJEXT) dw_style.$(OBJEXT) dw_table.$(OBJEXT) \
63 dw_table_cell.$(OBJEXT) dw_tooltip.$(OBJEXT) \
64 dw_widget.$(OBJEXT) findtext.$(OBJEXT) selection.$(OBJEXT) \
65 web.$(OBJEXT) progressbar.$(OBJEXT) dillo.$(OBJEXT) \
66 bookmark.$(OBJEXT) dicache.$(OBJEXT) dns.$(OBJEXT) \
67 gtk_ext_button.$(OBJEXT) gtk_ext_menu.$(OBJEXT) \
68 gtk_ext_menu_item.$(OBJEXT) gtk_menu_title.$(OBJEXT) \
69 gif.$(OBJEXT) jpeg.$(OBJEXT) png.$(OBJEXT) html.$(OBJEXT) \
70 image.$(OBJEXT) misc.$(OBJEXT) interface.$(OBJEXT) \
71 history.$(OBJEXT) nav.$(OBJEXT) plain.$(OBJEXT) menu.$(OBJEXT) \
72 prefs.$(OBJEXT) colors.$(OBJEXT) bitvec.$(OBJEXT) \
73 klist.$(OBJEXT) strbuf.$(OBJEXT) url.$(OBJEXT) \
74 cookies.$(OBJEXT) dpiapi.$(OBJEXT)
75 dillo_OBJECTS = $(am_dillo_OBJECTS)
76 dillo_DEPENDENCIES = ../dpip/libDpip.a IO/libDio.a
77 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
78 depcomp = $(SHELL) $(top_srcdir)/depcomp
79 am__depfiles_maybe = depfiles
80 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
81 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
82 CCLD = $(CC)
83 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
84 SOURCES = $(dillo_SOURCES)
85 DIST_SOURCES = $(dillo_SOURCES)
86 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
87 html-recursive info-recursive install-data-recursive \
88 install-exec-recursive install-info-recursive \
89 install-recursive installcheck-recursive installdirs-recursive \
90 pdf-recursive ps-recursive uninstall-info-recursive \
91 uninstall-recursive
92 ETAGS = etags
93 CTAGS = ctags
94 DIST_SUBDIRS = $(SUBDIRS)
95 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
96 ACLOCAL = @ACLOCAL@
97 AMDEP_FALSE = @AMDEP_FALSE@
98 AMDEP_TRUE = @AMDEP_TRUE@
99 AMTAR = @AMTAR@
100 AUTOCONF = @AUTOCONF@
101 AUTOHEADER = @AUTOHEADER@
102 AUTOMAKE = @AUTOMAKE@
103 AWK = @AWK@
104 CC = @CC@
105 CCDEPMODE = @CCDEPMODE@
106 CFLAGS = @CFLAGS@
107 CPP = @CPP@
108 CPPFLAGS = @CPPFLAGS@
109 CXX = @CXX@
110 CXXDEPMODE = @CXXDEPMODE@
111 CXXFLAGS = @CXXFLAGS@
112 CYGPATH_W = @CYGPATH_W@
113 DEFS = @DEFS@
114 DEPDIR = @DEPDIR@
115 DLGUI_FALSE = @DLGUI_FALSE@
116 DLGUI_TRUE = @DLGUI_TRUE@
117 ECHO_C = @ECHO_C@
118 ECHO_N = @ECHO_N@
119 ECHO_T = @ECHO_T@
120 EGREP = @EGREP@
121 EXEEXT = @EXEEXT@
122 GLIB_CFLAGS = @GLIB_CFLAGS@
123 GLIB_CONFIG = @GLIB_CONFIG@
124 GLIB_LIBS = @GLIB_LIBS@
125 GTK_CFLAGS = @GTK_CFLAGS@
126 GTK_CONFIG = @GTK_CONFIG@
127 GTK_LIBS = @GTK_LIBS@
128 INSTALL_DATA = @INSTALL_DATA@
129 INSTALL_PROGRAM = @INSTALL_PROGRAM@
130 INSTALL_SCRIPT = @INSTALL_SCRIPT@
131 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
132 LDFLAGS = @LDFLAGS@
133 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
134 LIBFLTK_LIBS = @LIBFLTK_LIBS@
135 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
136 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
137 LIBJPEG_LIBS = @LIBJPEG_LIBS@
138 LIBOBJS = @LIBOBJS@
139 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
140 LIBPNG_LIBS = @LIBPNG_LIBS@
141 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
142 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
143 LIBS = @LIBS@
144 LIBSSL_LIBS = @LIBSSL_LIBS@
145 LIBZ_LIBS = @LIBZ_LIBS@
146 LTLIBOBJS = @LTLIBOBJS@
147 MAKEINFO = @MAKEINFO@
148 OBJEXT = @OBJEXT@
149 PACKAGE = @PACKAGE@
150 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
151 PACKAGE_NAME = @PACKAGE_NAME@
152 PACKAGE_STRING = @PACKAGE_STRING@
153 PACKAGE_TARNAME = @PACKAGE_TARNAME@
154 PACKAGE_VERSION = @PACKAGE_VERSION@
155 PATH_SEPARATOR = @PATH_SEPARATOR@
156 RANLIB = @RANLIB@
157 SET_MAKE = @SET_MAKE@
158 SHELL = @SHELL@
159 STRIP = @STRIP@
160 VERSION = @VERSION@
161 ac_ct_CC = @ac_ct_CC@
162 ac_ct_CXX = @ac_ct_CXX@
163 ac_ct_RANLIB = @ac_ct_RANLIB@
164 ac_ct_STRIP = @ac_ct_STRIP@
165 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
166 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
167 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
168 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
169 am__include = @am__include@
170 am__leading_dot = @am__leading_dot@
171 am__quote = @am__quote@
172 am__tar = @am__tar@
173 am__untar = @am__untar@
174 bindir = @bindir@
175 build = @build@
176 build_alias = @build_alias@
177 build_cpu = @build_cpu@
178 build_os = @build_os@
179 build_vendor = @build_vendor@
180 datadir = @datadir@
181 exec_prefix = @exec_prefix@
182 host = @host@
183 host_alias = @host_alias@
184 host_cpu = @host_cpu@
185 host_os = @host_os@
186 host_vendor = @host_vendor@
187 includedir = @includedir@
188 infodir = @infodir@
189 install_sh = @install_sh@
190 libdir = @libdir@
191 libexecdir = @libexecdir@
192 localstatedir = @localstatedir@
193 mandir = @mandir@
194 mkdir_p = @mkdir_p@
195 oldincludedir = @oldincludedir@
196 prefix = @prefix@
197 program_transform_name = @program_transform_name@
198 sbindir = @sbindir@
199 sharedstatedir = @sharedstatedir@
200 sysconfdir = @sysconfdir@
201 target = @target@
202 target_alias = @target_alias@
203 target_cpu = @target_cpu@
204 target_os = @target_os@
205 target_vendor = @target_vendor@
206 AM_CPPFLAGS = -DDILLORC_SYS='"$(sysconfdir)/dillorc"' @LIBJPEG_CPPFLAGS@
207 AM_CFLAGS = @GTK_CFLAGS@ @LIBPNG_CFLAGS@
208 SUBDIRS = IO
209 dillo_LDADD = ../dpip/libDpip.a IO/libDio.a @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @GTK_LIBS@ @LIBZ_LIBS@ @LIBPTHREAD_LIBS@
210 dillo_LDFLAGS = @LIBJPEG_LDFLAGS@ @LIBPTHREAD_LDFLAGS@
211 dillo_SOURCES = \
212 msg.h \
213 acconfig.h \
214 chain.h \
215 chain.c \
216 commands.c \
217 commands.h \
218 cache.c \
219 cache.h \
220 capi.c \
221 capi.h \
222 debug.h \
223 dw.h \
224 dw.c \
225 dw_aligned_page.c \
226 dw_aligned_page.h \
227 dw_bullet.c \
228 dw_bullet.h \
229 dw_button.c \
230 dw_button.h \
231 dw_container.c \
232 dw_container.h \
233 dw_embed_gtk.c \
234 dw_embed_gtk.h \
235 dw_ext_iterator.h \
236 dw_ext_iterator.c \
237 dw_gtk_scrolled_frame.c \
238 dw_gtk_scrolled_frame.h \
239 dw_gtk_scrolled_window.c \
240 dw_gtk_scrolled_window.h \
241 dw_gtk_statuslabel.c \
242 dw_gtk_statuslabel.h \
243 dw_gtk_viewport.c \
244 dw_gtk_viewport.h \
245 dw_hruler.c \
246 dw_hruler.h \
247 dw_image.c \
248 dw_image.h \
249 dw_list_item.c \
250 dw_list_item.h \
251 dw_marshal.c \
252 dw_marshal.h \
253 dw_page.c \
254 dw_page.h \
255 dw_style.c \
256 dw_style.h \
257 dw_table.c \
258 dw_table.h \
259 dw_table_cell.c \
260 dw_table_cell.h \
261 dw_tooltip.c \
262 dw_tooltip.h \
263 dw_widget.c \
264 dw_widget.h \
265 findtext.c \
266 findtext.h \
267 selection.c \
268 selection.h \
269 web.c \
270 web.h \
271 progressbar.c \
272 progressbar.h \
273 dillo.c \
274 dillo.h \
275 bookmark.c \
276 bookmark.h \
277 browser.h \
278 dicache.c \
279 dicache.h \
280 dns.c \
281 dns.h \
282 gtk_ext_button.c \
283 gtk_ext_button.h \
284 gtk_ext_menu.c \
285 gtk_ext_menu.h \
286 gtk_ext_menu_item.c \
287 gtk_ext_menu_item.h \
288 gtk_menu_title.c \
289 gtk_menu_title.h \
290 gif.c \
291 jpeg.c \
292 png.c \
293 html.c \
294 html.h \
295 image.c \
296 image.h \
297 misc.c \
298 misc.h \
299 interface.h \
300 interface.c \
301 history.c \
302 history.h \
303 nav.c \
304 nav.h \
305 plain.c \
306 menu.c \
307 menu.h \
308 prefs.c \
309 prefs.h \
310 colors.c \
311 colors.h \
312 bitvec.c \
313 bitvec.h \
314 klist.c \
315 klist.h \
316 strbuf.c \
317 strbuf.h \
318 url.c \
319 url.h \
320 cookies.c \
321 cookies.h \
322 list.h \
323 binaryconst.h \
324 pixmaps.h \
325 dpiapi.c \
326 dpiapi.h
327
328 EXTRA_DIST = chg srch
329 all: all-recursive
330
331 .SUFFIXES:
332 .SUFFIXES: .c .o .obj
333 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
334 @for dep in $?; do \
335 case '$(am__configure_deps)' in \
336 *$$dep*) \
337 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
338 && exit 0; \
339 exit 1;; \
340 esac; \
341 done; \
342 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
343 cd $(top_srcdir) && \
344 $(AUTOMAKE) --gnu src/Makefile
345 .PRECIOUS: Makefile
346 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
347 @case '$?' in \
348 *config.status*) \
349 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
350 *) \
351 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
352 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
353 esac;
354
355 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
356 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
357
358 $(top_srcdir)/configure: $(am__configure_deps)
359 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
360 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
361 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
362 install-binPROGRAMS: $(bin_PROGRAMS)
363 @$(NORMAL_INSTALL)
364 test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
365 @list='$(bin_PROGRAMS)'; for p in $$list; do \
366 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
367 if test -f $$p \
368 ; then \
369 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
370 echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
371 $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
372 else :; fi; \
373 done
374
375 uninstall-binPROGRAMS:
376 @$(NORMAL_UNINSTALL)
377 @list='$(bin_PROGRAMS)'; for p in $$list; do \
378 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
379 echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
380 rm -f "$(DESTDIR)$(bindir)/$$f"; \
381 done
382
383 clean-binPROGRAMS:
384 -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
385 dillo$(EXEEXT): $(dillo_OBJECTS) $(dillo_DEPENDENCIES)
386 @rm -f dillo$(EXEEXT)
387 $(LINK) $(dillo_LDFLAGS) $(dillo_OBJECTS) $(dillo_LDADD) $(LIBS)
388
389 mostlyclean-compile:
390 -rm -f *.$(OBJEXT)
391
392 distclean-compile:
393 -rm -f *.tab.c
394
395 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitvec.Po@am__quote@
396 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bookmark.Po@am__quote@
397 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Po@am__quote@
398 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/capi.Po@am__quote@
399 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chain.Po@am__quote@
400 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colors.Po@am__quote@
401 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commands.Po@am__quote@
402 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookies.Po@am__quote@
403 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dicache.Po@am__quote@
404 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dillo.Po@am__quote@
405 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@
406 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpiapi.Po@am__quote@
407 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw.Po@am__quote@
408 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_aligned_page.Po@am__quote@
409 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_bullet.Po@am__quote@
410 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_button.Po@am__quote@
411 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_container.Po@am__quote@
412 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_embed_gtk.Po@am__quote@
413 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_ext_iterator.Po@am__quote@
414 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_scrolled_frame.Po@am__quote@
415 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_scrolled_window.Po@am__quote@
416 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_statuslabel.Po@am__quote@
417 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_viewport.Po@am__quote@
418 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_hruler.Po@am__quote@
419 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_image.Po@am__quote@
420 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_list_item.Po@am__quote@
421 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_marshal.Po@am__quote@
422 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_page.Po@am__quote@
423 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_style.Po@am__quote@
424 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_table.Po@am__quote@
425 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_table_cell.Po@am__quote@
426 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_tooltip.Po@am__quote@
427 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_widget.Po@am__quote@
428 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/findtext.Po@am__quote@
429 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gif.Po@am__quote@
430 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_ext_button.Po@am__quote@
431 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_ext_menu.Po@am__quote@
432 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_ext_menu_item.Po@am__quote@
433 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_menu_title.Po@am__quote@
434 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/history.Po@am__quote@
435 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Po@am__quote@
436 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image.Po@am__quote@
437 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interface.Po@am__quote@
438 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg.Po@am__quote@
439 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/klist.Po@am__quote@
440 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu.Po@am__quote@
441 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
442 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nav.Po@am__quote@
443 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plain.Po@am__quote@
444 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/png.Po@am__quote@
445 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs.Po@am__quote@
446 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progressbar.Po@am__quote@
447 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selection.Po@am__quote@
448 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strbuf.Po@am__quote@
449 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
450 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web.Po@am__quote@
451
452 .c.o:
453 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
454 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
455 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
456 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
457 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
458
459 .c.obj:
460 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
461 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
462 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
463 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
464 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
465 uninstall-info-am:
466
467 # This directory's subdirectories are mostly independent; you can cd
468 # into them and run `make' without going through this Makefile.
469 # To change the values of `make' variables: instead of editing Makefiles,
470 # (1) if the variable is set in `config.status', edit `config.status'
471 # (which will cause the Makefiles to be regenerated when you run `make');
472 # (2) otherwise, pass the desired values on the `make' command line.
473 $(RECURSIVE_TARGETS):
474 @failcom='exit 1'; \
475 for f in x $$MAKEFLAGS; do \
476 case $$f in \
477 *=* | --[!k]*);; \
478 *k*) failcom='fail=yes';; \
479 esac; \
480 done; \
481 dot_seen=no; \
482 target=`echo $@ | sed s/-recursive//`; \
483 list='$(SUBDIRS)'; for subdir in $$list; do \
484 echo "Making $$target in $$subdir"; \
485 if test "$$subdir" = "."; then \
486 dot_seen=yes; \
487 local_target="$$target-am"; \
488 else \
489 local_target="$$target"; \
490 fi; \
491 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
492 || eval $$failcom; \
493 done; \
494 if test "$$dot_seen" = "no"; then \
495 $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
496 fi; test -z "$$fail"
497
498 mostlyclean-recursive clean-recursive distclean-recursive \
499 maintainer-clean-recursive:
500 @failcom='exit 1'; \
501 for f in x $$MAKEFLAGS; do \
502 case $$f in \
503 *=* | --[!k]*);; \
504 *k*) failcom='fail=yes';; \
505 esac; \
506 done; \
507 dot_seen=no; \
508 case "$@" in \
509 distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
510 *) list='$(SUBDIRS)' ;; \
511 esac; \
512 rev=''; for subdir in $$list; do \
513 if test "$$subdir" = "."; then :; else \
514 rev="$$subdir $$rev"; \
515 fi; \
516 done; \
517 rev="$$rev ."; \
518 target=`echo $@ | sed s/-recursive//`; \
519 for subdir in $$rev; do \
520 echo "Making $$target in $$subdir"; \
521 if test "$$subdir" = "."; then \
522 local_target="$$target-am"; \
523 else \
524 local_target="$$target"; \
525 fi; \
526 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
527 || eval $$failcom; \
528 done && test -z "$$fail"
529 tags-recursive:
530 list='$(SUBDIRS)'; for subdir in $$list; do \
531 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
532 done
533 ctags-recursive:
534 list='$(SUBDIRS)'; for subdir in $$list; do \
535 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
536 done
537
538 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
539 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
540 unique=`for i in $$list; do \
541 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
542 done | \
543 $(AWK) ' { files[$$0] = 1; } \
544 END { for (i in files) print i; }'`; \
545 mkid -fID $$unique
546 tags: TAGS
547
548 TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
549 $(TAGS_FILES) $(LISP)
550 tags=; \
551 here=`pwd`; \
552 if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
553 include_option=--etags-include; \
554 empty_fix=.; \
555 else \
556 include_option=--include; \
557 empty_fix=; \
558 fi; \
559 list='$(SUBDIRS)'; for subdir in $$list; do \
560 if test "$$subdir" = .; then :; else \
561 test ! -f $$subdir/TAGS || \
562 tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
563 fi; \
564 done; \
565 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
566 unique=`for i in $$list; do \
567 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
568 done | \
569 $(AWK) ' { files[$$0] = 1; } \
570 END { for (i in files) print i; }'`; \
571 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
572 test -n "$$unique" || unique=$$empty_fix; \
573 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
574 $$tags $$unique; \
575 fi
576 ctags: CTAGS
577 CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
578 $(TAGS_FILES) $(LISP)
579 tags=; \
580 here=`pwd`; \
581 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
582 unique=`for i in $$list; do \
583 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
584 done | \
585 $(AWK) ' { files[$$0] = 1; } \
586 END { for (i in files) print i; }'`; \
587 test -z "$(CTAGS_ARGS)$$tags$$unique" \
588 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
589 $$tags $$unique
590
591 GTAGS:
592 here=`$(am__cd) $(top_builddir) && pwd` \
593 && cd $(top_srcdir) \
594 && gtags -i $(GTAGS_ARGS) $$here
595
596 distclean-tags:
597 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
598
599 distdir: $(DISTFILES)
600 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
601 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
602 list='$(DISTFILES)'; for file in $$list; do \
603 case $$file in \
604 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
605 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
606 esac; \
607 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
608 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
609 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
610 dir="/$$dir"; \
611 $(mkdir_p) "$(distdir)$$dir"; \
612 else \
613 dir=''; \
614 fi; \
615 if test -d $$d/$$file; then \
616 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
617 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
618 fi; \
619 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
620 else \
621 test -f $(distdir)/$$file \
622 || cp -p $$d/$$file $(distdir)/$$file \
623 || exit 1; \
624 fi; \
625 done
626 list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
627 if test "$$subdir" = .; then :; else \
628 test -d "$(distdir)/$$subdir" \
629 || $(mkdir_p) "$(distdir)/$$subdir" \
630 || exit 1; \
631 distdir=`$(am__cd) $(distdir) && pwd`; \
632 top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
633 (cd $$subdir && \
634 $(MAKE) $(AM_MAKEFLAGS) \
635 top_distdir="$$top_distdir" \
636 distdir="$$distdir/$$subdir" \
637 distdir) \
638 || exit 1; \
639 fi; \
640 done
641 check-am: all-am
642 check: check-recursive
643 all-am: Makefile $(PROGRAMS)
644 installdirs: installdirs-recursive
645 installdirs-am:
646 for dir in "$(DESTDIR)$(bindir)"; do \
647 test -z "$$dir" || $(mkdir_p) "$$dir"; \
648 done
649 install: install-recursive
650 install-exec: install-exec-recursive
651 install-data: install-data-recursive
652 uninstall: uninstall-recursive
653
654 install-am: all-am
655 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
656
657 installcheck: installcheck-recursive
658 install-strip:
659 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
660 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
661 `test -z '$(STRIP)' || \
662 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
663 mostlyclean-generic:
664
665 clean-generic:
666
667 distclean-generic:
668 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
669
670 maintainer-clean-generic:
671 @echo "This command is intended for maintainers to use"
672 @echo "it deletes files that may require special tools to rebuild."
673 clean: clean-recursive
674
675 clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
676
677 distclean: distclean-recursive
678 -rm -rf ./$(DEPDIR)
679 -rm -f Makefile
680 distclean-am: clean-am distclean-compile distclean-generic \
681 distclean-tags
682
683 dvi: dvi-recursive
684
685 dvi-am:
686
687 html: html-recursive
688
689 info: info-recursive
690
691 info-am:
692
693 install-data-am:
694
695 install-exec-am: install-binPROGRAMS
696
697 install-info: install-info-recursive
698
699 install-man:
700
701 installcheck-am:
702
703 maintainer-clean: maintainer-clean-recursive
704 -rm -rf ./$(DEPDIR)
705 -rm -f Makefile
706 maintainer-clean-am: distclean-am maintainer-clean-generic
707
708 mostlyclean: mostlyclean-recursive
709
710 mostlyclean-am: mostlyclean-compile mostlyclean-generic
711
712 pdf: pdf-recursive
713
714 pdf-am:
715
716 ps: ps-recursive
717
718 ps-am:
719
720 uninstall-am: uninstall-binPROGRAMS uninstall-info-am
721
722 uninstall-info: uninstall-info-recursive
723
724 .PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \
725 clean clean-binPROGRAMS clean-generic clean-recursive ctags \
726 ctags-recursive distclean distclean-compile distclean-generic \
727 distclean-recursive distclean-tags distdir dvi dvi-am html \
728 html-am info info-am install install-am install-binPROGRAMS \
729 install-data install-data-am install-exec install-exec-am \
730 install-info install-info-am install-man install-strip \
731 installcheck installcheck-am installdirs installdirs-am \
732 maintainer-clean maintainer-clean-generic \
733 maintainer-clean-recursive mostlyclean mostlyclean-compile \
734 mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \
735 tags tags-recursive uninstall uninstall-am \
736 uninstall-binPROGRAMS uninstall-info-am
737
738 # Tell versions [3.59,3.63) of GNU make to not export all variables.
739 # Otherwise a system limit (for SysV at least) may be exceeded.
740 .NOEXPORT:
0 #undef PACKAGE
1 #undef VERSION
0 #ifndef __BINARYCONST_H__
1 #define __BINARYCONST_H__
2
3 /* Macros for allowing binary constants in C
4 * By Tom Torfs - donated to the public domain */
5
6 #define HEX__(n) 0x##n##LU
7
8 /* 8-bit conversion function */
9 #define B8__(x) ((x&0x0000000FLU)?1:0) \
10 +((x&0x000000F0LU)?2:0) \
11 +((x&0x00000F00LU)?4:0) \
12 +((x&0x0000F000LU)?8:0) \
13 +((x&0x000F0000LU)?16:0) \
14 +((x&0x00F00000LU)?32:0) \
15 +((x&0x0F000000LU)?64:0) \
16 +((x&0xF0000000LU)?128:0)
17
18
19 /*
20 * *** USER MACROS ***
21 */
22
23 /* for upto 8-bit binary constants */
24 #define B8(d) ((unsigned char)B8__(HEX__(d)))
25
26 /* for upto 16-bit binary constants, MSB first */
27 #define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
28
29 /*
30 * Sample usage:
31 * B8(01010101) = 85
32 * B16(10101010,01010101) = 43605
33 */
34
35
36 #endif /* __BINARYCONST_H__ */
37
0 /*
1 * File: bitvec.c
2 *
3 * Copyright 2001 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * A simple ADT for bit arrays
13 */
14
15 #include "bitvec.h"
16
17
18 /*
19 * Create a new bitvec with 'num_bits' size
20 */
21 bitvec_t *a_Bitvec_new(gint num_bits)
22 {
23 bitvec_t *bvec = g_new(bitvec_t, 1);
24
25 bvec->vec = g_new0(guchar, num_bits/BVEC_SIZE + 1);
26 bvec->len = num_bits;
27 return bvec;
28 }
29
30 /*
31 * Free a bitvec
32 */
33 void a_Bitvec_free(bitvec_t *bvec)
34 {
35 if (bvec) {
36 g_free(bvec->vec);
37 g_free(bvec);
38 }
39 }
40
41 /*
42 * Get a bit
43 */
44 gint a_Bitvec_get_bit(bitvec_t *bvec, gint pos)
45 {
46 g_return_val_if_fail (pos < bvec->len, 0);
47 return (bvec->vec[pos/BVEC_SIZE] & 1 << pos % BVEC_SIZE);
48 }
49
50 /*
51 * Set a bit
52 */
53 void a_Bitvec_set_bit(bitvec_t *bvec, gint pos)
54 {
55 g_return_if_fail (pos < bvec->len);
56 bvec->vec[pos/BVEC_SIZE] |= 1 << (pos % BVEC_SIZE);
57 }
0 #ifndef __BITVEC_H__
1 #define __BITVEC_H__
2
3 #include <glib.h>
4
5 #define BVEC_TYPE guchar
6 #define BVEC_SIZE sizeof(BVEC_TYPE)
7
8 typedef struct _bitvec bitvec_t;
9
10 struct _bitvec {
11 BVEC_TYPE *vec;
12 gint len; /* number of bits [1 based] */
13 };
14
15
16 /*
17 * Function prototypes
18 */
19 bitvec_t *a_Bitvec_new(gint bits);
20 void a_Bitvec_free(bitvec_t *bvec);
21 gint a_Bitvec_get_bit(bitvec_t *bvec, gint pos);
22 void a_Bitvec_set_bit(bitvec_t *bvec, gint pos);
23
24 /*
25 #define a_Bitvec_get_bit(bvec,pos) \
26 ((bvec)->vec[(pos)/BVEC_SIZE] & 1 << (pos) % BVEC_SIZE)
27
28 #define a_Bitvec_set_bit(bvec,pos) \
29 ((bvec)->vec[(pos)/BVEC_SIZE] |= 1 << (pos) % BVEC_SIZE)
30 */
31 #define a_Bitvec_clear_bit(bvec,pos) \
32 ((bvec)->vec[(pos)/BVEC_SIZE] &= ~(1 << (pos) % BVEC_SIZE))
33
34
35 #endif /* __BITVEC_H__ */
0 /*
1 * File: bookmark.c
2 *
3 * Copyright 2002 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "msg.h"
17 #include "browser.h"
18 #include "history.h"
19 #include "menu.h"
20 #include "capi.h"
21 #include "nav.h"
22 #include "misc.h"
23 #include "bookmark.h" /* for prototypes */
24 #include "../dpip/dpip.h"
25
26
27
28 /*
29 * Initialize the bookmarks module
30 */
31 void a_Bookmarks_init(void)
32 {
33 /* simple isn't it? ;) */
34 }
35
36 /*
37 * Have a short chat with the bookmarks server,
38 * and finally ask it to add a new bookmark.
39 * (this is an example of dpi chat)
40 */
41 void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer)
42 {
43 static char *cmd1 = NULL, *cmd2 = NULL, *cmd3 = NULL, *cmd4 = NULL;
44 static BrowserWindow *bw = NULL;
45
46 if (!cmd1) {
47 cmd1 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Hi server");
48 cmd2 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat",
49 "I want to set a bookmark");
50 cmd3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Sure it is!");
51 }
52
53 _MSG("a_Bookmarks_chat_add\n answer=%s\n", answer ? answer : "(null)");
54
55 if (Bw)
56 bw = Bw;
57 if (!cmd4 && Cmd)
58 cmd4 = g_strdup(Cmd);
59
60 if (!answer) {
61 a_Capi_dpi_send_cmd(NULL, bw, cmd1, "bookmarks", 1);
62
63 } else {
64 /* we have an answer */
65 if (answer) {
66 if (*answer == 'H') {
67 /* "Hi browser" */
68 a_Capi_dpi_send_cmd(NULL, bw, cmd2, "bookmarks", 0);
69 } else if (*answer == 'I') {
70 /* "Is it worth?" */
71 a_Capi_dpi_send_cmd(NULL, bw, cmd3, "bookmarks", 0);
72 } else if (*answer == 'O') {
73 /* "OK, send it!" */
74 a_Capi_dpi_send_cmd(NULL, bw, cmd4, "bookmarks", 0);
75 g_free(cmd4);
76 cmd4 = NULL;
77 }
78 }
79 }
80 }
81
82 /*
83 * Add the new bookmark through the bookmarks server
84 */
85 void a_Bookmarks_add(GtkWidget *widget, gpointer client_data)
86 {
87 BrowserWindow *bw = (BrowserWindow *)client_data;
88 DilloUrl *url;
89 const gchar *title;
90 gchar *cmd;
91
92 url = a_Menu_popup_get_url(bw);
93 g_return_if_fail(url != NULL);
94
95 /* if the page has no title, we'll use the url string */
96 title = a_History_get_title_by_url(url, 1);
97
98 cmd = a_Dpip_build_cmd("cmd=%s url=%s title=%s",
99 "add_bookmark", URL_STR(url), title);
100 a_Bookmarks_chat_add(bw, cmd, NULL);
101 g_free(cmd);
102 }
103
104 /*
105 * Request the server to show the bookmarks
106 */
107 void a_Bookmarks_show(BrowserWindow *bw)
108 {
109 DilloUrl *url;
110
111 url = a_Url_new("dpi:/bm/", NULL, 0, 0, 0);
112 a_Nav_push(bw, url);
113 a_Url_free(url);
114 }
115
0 #ifndef __BOOKMARK_H__
1 #define __BOOKMARK_H__
2
3 #include "browser.h"
4
5
6 void a_Bookmarks_init(void);
7 void a_Bookmarks_add(GtkWidget *widget, gpointer client_data);
8 void a_Bookmarks_show(BrowserWindow *bw);
9
10 /* todo: this is for testing purposes */
11 void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer);
12
13 #endif /* __BOOKMARK_H__ */
0 #ifndef __BROWSER_H__
1 #define __BROWSER_H__
2
3 #include <sys/types.h>
4 #include <gdk/gdk.h>
5 #include <gtk/gtk.h>
6
7 #include "url.h" /* for DilloUrl */
8
9
10 typedef struct _BrowserWindow BrowserWindow;
11 typedef struct _DilloMenuPopup DilloMenuPopup;
12
13 typedef struct {
14 DilloUrl *Url; /* URL-key for this cache connection */
15 gint Flags; /* {WEB_RootUrl, WEB_Image, WEB_Download} */
16 } BwUrls;
17
18 /* The popup menus so that we can call them. */
19 struct _DilloMenuPopup
20 {
21 GtkWidget *over_page;
22 GtkWidget *over_link;
23 GtkWidget *over_image;
24 GtkWidget *over_back;
25 GtkWidget *over_forw;
26 GtkWidget *over_bug;
27 DilloUrl *url;
28 DilloUrl *url2;
29 GtkWidget *ol_oi_submenu;
30 };
31
32 /* browser_window contains all widgets to create a single window */
33 struct _BrowserWindow
34 {
35 /* Control-Panel handleboxes --used for hiding */
36 GSList *PanelHandles;
37 /* Flag: TRUE when control-panel is hidden */
38 gboolean fullwindow;
39
40 /* widgets for the main window */
41 GtkWidget *main_window;
42 GtkWidget *back_button;
43 GtkWidget *forw_button;
44 GtkWidget *home_button;
45 GtkWidget *reload_button;
46 GtkWidget *save_button;
47 GtkWidget *stop_button;
48 GtkWidget *bookmarks_button;
49 GtkWidget *menubar;
50 GtkWidget *clear_url_button;
51 GtkWidget *location;
52 GtkWidget *search_button;
53 GtkWidget *progress_box;
54 GtkWidget *status_box;
55 GtkWidget *status;
56 GtkWidget *status_bug_meter;
57 gint status_is_link;
58 GtkWidget *imgprogress;
59 GtkWidget *progress;
60
61 /* the keyboard accelerator table */
62 GtkAccelGroup *accel_group;
63
64 /* Popup menu for this BrowserWindow */
65 DilloMenuPopup menu_popup;
66
67 /* The "Headings" and "Anchors" menus */
68 GtkWidget *pagemarks_menuitem;
69 GtkWidget *pagemarks_menu;
70 GtkWidget *pagemarks_last;
71
72 /* "View page Bugs" menuitem so we can set its sensitivity */
73 GtkWidget *viewbugs_menuitem;
74
75 /* This is the main document widget. (HTML rendering or whatever) */
76 GtkWidget *docwin;
77
78 /* Current cursor type */
79 GdkCursorType CursorType;
80
81 /* A list of active cache clients in the window (The primary Key) */
82 gint *RootClients;
83 gint NumRootClients;
84 gint MaxRootClients;
85
86 /* Image Keys for all active connections in the window */
87 gint *ImageClients;
88 gint NumImageClients;
89 gint MaxImageClients;
90 /* Number of different images in the page */
91 gint NumImages;
92 /* Number of different images already loaded */
93 gint NumImagesGot;
94
95 /* List of all Urls requested by this page (and its types) */
96 BwUrls *PageUrls;
97 gint NumPageUrls;
98 gint MaxPageUrls;
99
100 /* widgets for dialog boxes off main window */
101 GtkWidget *open_dialog_window;
102 GtkWidget *open_dialog_entry;
103 GtkWidget *openfile_dialog_window;
104 GtkWidget *quit_dialog_window;
105 GtkWidget *save_dialog_window;
106 GtkWidget *save_link_dialog_window;
107 GtkWidget *findtext_dialog_window;
108 GtkWidget *findtext_dialog_check;
109 GtkWidget *findtext_dialog_entry;
110 GtkWidget *search_dialog_window;
111 GtkWidget *search_dialog_entry;
112 GtkWidget *proxy_passwd_dialog_window;
113 GtkWidget *proxy_passwd_dialog_entry;
114 GtkWidget *question_dialog_window;
115 gpointer question_dialog_data;
116 gpointer question_dialog_answer;
117 GtkWidget *viewsource_window;
118 GtkWidget *pagebugs_window;
119 GtkWidget *full_screen_off_button;
120
121 /* Dillo navigation stack (holds indexes to history list) */
122 gint *nav_stack;
123 gint nav_stack_size; /* [1 based] */
124 gint nav_stack_size_max;
125 /* 'nav_stack_ptr' refers to what's being displayed */
126 gint nav_stack_ptr; /* [0 based] */
127 /* When the user clicks a link, the URL isn't pushed directly to history;
128 * nav_expect_url holds it until the first answer-bytes are got. Only then
129 * it is sent to history and referenced in 'nav_stack[++nav_stack_ptr]' */
130 DilloUrl *nav_expect_url;
131 /* 'nav_expecting' is true if the last URL is being loaded for
132 * the first time and has not gotten the dw yet. */
133 gboolean nav_expecting;
134
135 /* Counter for the number of hops on a redirection. Used to stop
136 * redirection loops (accounts for WEB_RootUrl only) */
137 gint redirect_level;
138
139 /* The id for the idle function that sets button sensitivity. */
140 guint sens_idle_id;
141 };
142
143
144
145 #endif /* __BROWSER_H__ */
146
0 /*
1 * File: cache.c
2 *
3 * Copyright 2000, 2001, 2002, 2003, 2004 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Dillo's cache module
13 */
14
15 #include <ctype.h> /* for tolower */
16 #include <sys/types.h>
17
18 #include <sys/stat.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #include "msg.h"
25 #include "list.h"
26 #include "IO/Url.h"
27 #include "IO/IO.h"
28 #include "web.h"
29 #include "dicache.h"
30 #include "interface.h"
31 #include "nav.h"
32 #include "cookies.h"
33 #include "misc.h"
34
35 #define NULLKey 0
36
37 #define DEBUG_LEVEL 5
38 #include "debug.h"
39
40 /*
41 * Local data types
42 */
43
44 typedef struct {
45 const DilloUrl *Url; /* Cached Url. Url is used as a primary Key */
46 char *TypeDet; /* MIME type string (detected from data) */
47 char *TypeHdr; /* MIME type string as from the HTTP Header */
48 GString *Header; /* HTTP header */
49 const DilloUrl *Location; /* New URI for redirects */
50 void *Data; /* Pointer to raw data */
51 size_t ValidSize, /* Actually size of valid range */
52 TotalSize, /* Goal size of the whole data (0 if unknown) */
53 BuffSize; /* Buffer Size for unknown length transfers */
54 guint Flags; /* Look Flag Defines in cache.h */
55 IOData_t *io; /* Pointer to IO data */
56 ChainLink *CCCQuery; /* CCC link for querying branch */
57 ChainLink *CCCAnswer; /* CCC link for answering branch */
58 } CacheData_t;
59
60
61 /*
62 * Local data
63 */
64 /* A hash for cached data. Holds pointers to CacheData_t structs */
65 static GHashTable *CacheHash = NULL;
66
67 /* A list for cache clients.
68 * Although implemented as a list, we'll call it ClientQueue --Jcid */
69 static GSList *ClientQueue = NULL;
70
71 /* A list for delayed clients (it holds weak pointers to cache entries,
72 * which are used to make deferred calls to Cache_process_queue) */
73 static GSList *DelayedQueue = NULL;
74 static guint DelayedQueueIdleId = 0;
75
76
77 /*
78 * Forward declarations
79 */
80 static void Cache_process_queue(CacheData_t *entry);
81 static void Cache_delayed_process_queue(CacheData_t *entry);
82 static void Cache_stop_client(gint Key, gint force);
83
84 /*
85 * Determine if two hash entries are equal (used by GHashTable)
86 */
87 static gint Cache_hash_entry_equal(gconstpointer v1, gconstpointer v2)
88 {
89 return (!a_Url_cmp((DilloUrl *)v1, (DilloUrl *)v2));
90 }
91
92 /*
93 * Determine the hash value given the key (used by GHashTable)
94 */
95 static guint Cache_url_hash(gconstpointer key)
96 {
97 const char *p = URL_STR((DilloUrl *)key);
98 guint h = *p;
99
100 if (h)
101 for (p += 1; *p != '\0' && *p != '#'; p++)
102 h = (h << 5) - h + *p;
103
104 return h;
105 }
106
107 /*
108 * Initialize dicache data
109 */
110 void a_Cache_init(void)
111 {
112 CacheHash = g_hash_table_new(Cache_url_hash, Cache_hash_entry_equal);
113 }
114
115 /* Client operations ------------------------------------------------------ */
116
117 /*
118 * Make a unique primary-key for cache clients
119 */
120 static gint Cache_client_make_key(void)
121 {
122 static gint ClientKey = 0; /* Provide a primary key for each client */
123
124 if ( ++ClientKey < 0 ) ClientKey = 1;
125 return ClientKey;
126 }
127
128 /*
129 * Add a client to ClientQueue.
130 * - Every client-camp is just a reference (except 'Web').
131 * - Return a unique number for identifying the client.
132 */
133 static gint Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web,
134 CA_Callback_t Callback, void *CbData)
135 {
136 gint ClientKey;
137 CacheClient_t *NewClient;
138
139 NewClient = g_new(CacheClient_t, 1);
140 ClientKey = Cache_client_make_key();
141 NewClient->Key = ClientKey;
142 NewClient->Url = Url;
143 NewClient->Buf = NULL;
144 NewClient->Callback = Callback;
145 NewClient->CbData = CbData;
146 NewClient->Web = Web;
147
148 ClientQueue = g_slist_append(ClientQueue, NewClient);
149
150 return ClientKey;
151 }
152
153 /*
154 * Compare function for searching a Client by its key
155 */
156 static gint Cache_client_key_cmp(gconstpointer client, gconstpointer key)
157 {
158 return ( GPOINTER_TO_INT(key) != ((CacheClient_t *)client)->Key );
159 }
160
161 /*
162 * Compare function for searching a Client by its URL
163 */
164 static gint Cache_client_url_cmp(gconstpointer client, gconstpointer url)
165 {
166 return a_Url_cmp((DilloUrl *)url, ((CacheClient_t *)client)->Url);
167 }
168
169 /*
170 * Compare function for searching a Client by hostname
171 */
172 static gint Cache_client_host_cmp(gconstpointer client, gconstpointer hostname)
173 {
174 return g_strcasecmp(URL_HOST(((CacheClient_t *)client)->Url),
175 (gchar *)hostname );
176 }
177
178 /*
179 * Remove a client from the queue
180 */
181 static void Cache_client_dequeue(CacheClient_t *Client, gint Key)
182 {
183 GSList *List;
184
185 if (!Client &&
186 (List = g_slist_find_custom(ClientQueue, GINT_TO_POINTER(Key),
187 Cache_client_key_cmp)))
188 Client = List->data;
189
190 if ( Client ) {
191 ClientQueue = g_slist_remove(ClientQueue, Client);
192 a_Web_free(Client->Web);
193 g_free(Client);
194 }
195 }
196
197
198 /* Entry operations ------------------------------------------------------- */
199
200 /*
201 * Set safe values for a new cache entry
202 */
203 static void Cache_entry_init(CacheData_t *NewEntry, const DilloUrl *Url)
204 {
205 NewEntry->Url = a_Url_dup(Url);
206 NewEntry->TypeDet = NULL;
207 NewEntry->TypeHdr = NULL;
208 NewEntry->Header = g_string_new("");
209 NewEntry->Location = NULL;
210 NewEntry->Data = NULL;
211 NewEntry->ValidSize = 0;
212 NewEntry->TotalSize = 0;
213 NewEntry->BuffSize = 4096;
214 NewEntry->Flags = 0;
215 NewEntry->io = NULL;
216 NewEntry->CCCQuery = a_Chain_new();
217 NewEntry->CCCAnswer = NULL;
218 }
219
220 /*
221 * Get the data structure for a cached URL (using 'Url' as the search key)
222 * If 'Url' isn't cached, return NULL
223 */
224 static CacheData_t *Cache_entry_search(const DilloUrl *Url)
225 {
226 return g_hash_table_lookup(CacheHash, Url);
227 }
228
229 /*
230 * Allocate and set a new entry in the cache list
231 */
232 static CacheData_t *Cache_entry_add(const DilloUrl *Url)
233 {
234 CacheData_t *new_entry = g_new(CacheData_t, 1);
235
236 if (Cache_entry_search(Url))
237 DEBUG_MSG(5, "WARNING: Cache_entry_add, leaking an entry.\n");
238
239 Cache_entry_init(new_entry, Url); /* Set safe values */
240 g_hash_table_insert(CacheHash, (gpointer)new_entry->Url, new_entry);
241 return new_entry;
242 }
243
244 /*
245 * Free the components of a CacheData_t struct.
246 */
247 static void Cache_entry_free(CacheData_t *entry)
248 {
249 a_Url_free((DilloUrl *)entry->Url);
250 g_free(entry->TypeDet);
251 g_free(entry->TypeHdr);
252 g_string_free(entry->Header, TRUE);
253 a_Url_free((DilloUrl *)entry->Location);
254 g_free(entry->Data);
255 g_free(entry);
256 /* CCCQuery and CCCAnswer are just references */
257 }
258
259 /*
260 * Remove an entry from the CacheList (no CCC-function is called)
261 */
262 static gint Cache_entry_remove_raw(CacheData_t *entry, DilloUrl *url)
263 {
264 if (!entry && !(entry = Cache_entry_search(url)))
265 return 0;
266
267 /* There MUST NOT be any clients */
268 g_return_val_if_fail(
269 !g_slist_find_custom(
270 ClientQueue, (gpointer)entry->Url, Cache_client_url_cmp), 0);
271
272 /* remove from DelayedQueue */
273 DelayedQueue = g_slist_remove(DelayedQueue, entry);
274
275 /* remove from dicache */
276 a_Dicache_invalidate_entry(entry->Url);
277
278 /* remove from cache */
279 g_hash_table_remove(CacheHash, entry->Url);
280 Cache_entry_free(entry);
281 return 1;
282 }
283
284 /*
285 * Remove an entry, using the CCC if necessary.
286 * (entry SHOULD NOT have clients)
287 */
288 static void Cache_entry_remove(CacheData_t *entry, DilloUrl *url)
289 {
290 ChainLink *InfoQuery, *InfoAnswer;
291
292 if (!entry && !(entry = Cache_entry_search(url)))
293 return;
294
295 InfoQuery = entry->CCCQuery;
296 InfoAnswer = entry->CCCAnswer;
297
298 if (InfoQuery) {
299 DEBUG_MSG(2, "## Aborting CCCQuery\n");
300 a_Cache_ccc(OpAbort, 1, BCK, InfoQuery, NULL, NULL);
301 } else if (InfoAnswer) {
302 DEBUG_MSG(2, "## Aborting CCCAnswer\n");
303 a_Cache_ccc(OpAbort, 2, BCK, InfoAnswer, NULL, NULL);
304 } else {
305 DEBUG_MSG(2, "## Aborting raw2\n");
306 Cache_entry_remove_raw(entry, NULL);
307 }
308 }
309
310
311 /* Misc. operations ------------------------------------------------------- */
312
313 /*
314 * Given an entry (url), remove all its clients (by url or host).
315 */
316 static void Cache_stop_clients(DilloUrl *url, gint ByHost)
317 {
318 guint i;
319 CacheClient_t *Client;
320
321 for (i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i) {
322 if ( (ByHost && Cache_client_host_cmp(Client, URL_HOST(url)) == 0) ||
323 (!ByHost && Cache_client_url_cmp(Client, url) == 0) ) {
324 Cache_stop_client(Client->Key, 0);
325 --i;
326 }
327 }
328 }
329
330 /*
331 * Prepare a reload by stopping clients and removing the entry
332 * Return value: 1 if on success, 0 otherwise
333 */
334 static gint Cache_prepare_reload(DilloUrl *url)
335 {
336 CacheClient_t *Client;
337 DilloWeb *ClientWeb;
338 guint i;
339
340 for (i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i){
341 if (Cache_client_url_cmp(Client, url) == 0 &&
342 (ClientWeb = Client->Web) && !(ClientWeb->flags & WEB_Download))
343 Cache_stop_client(Client->Key, 0);
344 }
345
346 if (!g_slist_find_custom(ClientQueue, url, Cache_client_url_cmp)) {
347 /* There're no clients for this entry */
348 DEBUG_MSG(2, "## No more clients for this entry\n");
349 Cache_entry_remove(NULL, url);
350 return 1;
351 } else {
352 MSG("Cache_prepare_reload: ERROR, entry still has clients\n");
353 }
354
355 return 0;
356 }
357
358 /*
359 * Try finding the url in the cache. If it hits, send the cache contents
360 * from there. If it misses, set up a new connection.
361 * Return value: A primary key for identifying the client.
362 */
363 static gint Cache_open_url(DilloWeb *Web, CA_Callback_t Call, void *CbData)
364 {
365 void *link;
366 gint ClientKey;
367 ChainFunction_t cccFunct;
368 DilloUrl *Url = Web->url;
369 CacheData_t *entry = Cache_entry_search(Url);
370
371 _MSG("Cache_open_url: %s %sFOUND\n", URL_STR(Url), entry ? "" : "NOT ");
372
373 if ( entry ) {
374 /* URL is cached: feed our client with cached data */
375 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
376 Cache_delayed_process_queue(entry);
377
378 } else {
379 /* URL not cached: create an entry, send our client to the queue,
380 * and open a new connection */
381 entry = Cache_entry_add(Url);
382 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
383 a_Cache_ccc(OpStart, 1, BCK, entry->CCCQuery, NULL, (void *)entry->Url);
384 cccFunct = a_Url_get_ccc_funct(entry->Url);
385 if ( cccFunct ) {
386 link = a_Chain_link_new(entry->CCCQuery,
387 a_Cache_ccc, BCK, cccFunct, 1, 1);
388 a_Chain_bcb(OpStart, entry->CCCQuery, (void *)entry->Url, Web);
389 } else {
390 a_Interface_msg(Web->bw, "ERROR: unsupported protocol");
391 a_Cache_ccc(OpAbort, 1, FWD, entry->CCCQuery, NULL, NULL);
392 ClientKey = 0; /* aborted */
393 }
394 }
395 return ClientKey;
396 }
397
398 /*
399 * Try finding the url in the cache. If it hits, send the cache contents
400 * from there. If it misses, set up a new connection.
401 *
402 * - 'Web' is an auxiliar data structure with misc. parameters.
403 * - 'Call' is the callback that receives the data
404 * - 'CbData' is custom data passed to 'Call'
405 * Note: 'Call' and/or 'CbData' can be NULL, in that case they get set
406 * later by a_Web_dispatch_by_type, based on content/type and 'Web' data.
407 *
408 * Return value: A primary key for identifying the client.
409 */
410 gint a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
411 {
412 gint ClientKey;
413 DICacheEntry *DicEntry;
414 CacheData_t *entry;
415 DilloWeb *Web = web;
416 DilloUrl *Url = Web->url;
417
418 if (URL_FLAGS(Url) & URL_E2EReload) {
419 /* Reload operation */
420 Cache_prepare_reload(Url);
421 }
422
423 if ( Call ) {
424 /* This is a verbatim request */
425 ClientKey = Cache_open_url(Web, Call, CbData);
426
427 } else if ( (DicEntry = a_Dicache_get_entry(Url)) &&
428 (entry = Cache_entry_search(Url)) ) {
429 /* We have it in the Dicache! */
430 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
431 Cache_delayed_process_queue(entry);
432
433 } else {
434 /* It can be anything; let's request it and decide what to do
435 when we get more info... */
436 ClientKey = Cache_open_url(Web, Call, CbData);
437 }
438
439 if (g_slist_find_custom(ClientQueue, GINT_TO_POINTER(ClientKey),
440 Cache_client_key_cmp))
441 return ClientKey;
442 else
443 return 0; /* Aborted */
444 }
445
446 /*
447 * Get the pointer to the URL document, and its size, from the cache entry.
448 * Return: 1 cached, 0 not cached.
449 */
450 gint a_Cache_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize)
451 {
452 CacheData_t *entry;
453
454 while ((entry = Cache_entry_search(Url))) {
455
456 /* Test for a redirection loop */
457 if (entry->Flags & CA_RedirectLoop) {
458 g_warning ("Redirect loop for URL: >%s<\n", URL_STR_(Url));
459 break;
460 }
461 /* Test for a working redirection */
462 if (entry && entry->Flags & CA_Redirect && entry->Location) {
463 Url = entry->Location;
464 } else
465 break;
466 }
467
468 *BufSize = (entry) ? entry->ValidSize : 0;
469 *PBuf = (entry) ? (gchar *) entry->Data : NULL;
470 return (entry ? 1 : 0);
471 }
472
473 /*
474 * Extract a single field from the header, allocating and storing the value
475 * in 'field'. ('fieldname' must not include the trailing ':')
476 * Return a new string with the field-content if found (NULL on error)
477 * (This function expects a '\r' stripped header)
478 */
479 static char *Cache_parse_field(const char *header, const char *fieldname)
480 {
481 char *field;
482 guint i, j;
483
484 for ( i = 0; header[i]; i++ ) {
485 /* Search fieldname */
486 for (j = 0; fieldname[j]; j++)
487 if ( tolower(fieldname[j]) != tolower(header[i + j]))
488 break;
489 if ( fieldname[j] ) {
490 /* skip to next line */
491 for ( i += j; header[i] != '\n'; i++);
492 continue;
493 }
494
495 i += j;
496 while (header[i] == ' ') i++;
497 if (header[i] == ':' ) {
498 /* Field found! */
499 while (header[++i] == ' ');
500 for (j = 0; header[i + j] != '\n'; j++);
501 field = g_strndup(header + i, j);
502 return field;
503 }
504 }
505 return NULL;
506 }
507
508 #ifndef DISABLE_COOKIES
509 /*
510 * Extract multiple fields from the header.
511 */
512 static GList *Cache_parse_multiple_fields(const char *header,
513 const char *fieldname)
514 {
515 guint i, j;
516 GList *fields = NULL;
517 char *field;
518
519 for (i = 0; header[i]; i++) {
520 /* Search fieldname */
521 for (j = 0; fieldname[j]; j++)
522 if (tolower(fieldname[j]) != tolower(header[i + j]))
523 break;
524 if (fieldname[j]) {
525 /* skip to next line */
526 for (i += j; header[i] != '\n'; i++);
527 continue;
528 }
529
530 i += j;
531 for ( ; header[i] == ' '; i++);
532 if (header[i] == ':' ) {
533 /* Field found! */
534 while (header[++i] == ' ');
535 for (j = 0; header[i + j] != '\n'; j++);
536 field = g_strndup(header + i, j);
537 fields = g_list_append(fields, field);
538 }
539 }
540 return fields;
541 }
542 #endif /* !DISABLE_COOKIES */
543
544 /*
545 * Scan, allocate, and set things according to header info.
546 * (This function needs the whole header to work)
547 */
548 static void Cache_parse_header(CacheData_t *entry, IOData_t *io, gint HdrLen)
549 {
550 gchar *header = entry->Header->str;
551 gchar *Length, *Type, *location_str;
552 #ifndef DISABLE_COOKIES
553 GList *Cookies;
554 #endif
555
556 if ( HdrLen < 12 ) {
557 /* Not enough info. */
558
559 } if ( header[9] == '3' && header[10] == '0' ) {
560 /* 30x: URL redirection */
561 entry->Flags |= CA_Redirect;
562 if ( header[11] == '1' )
563 entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
564 else if ( header[11] == '2' )
565 entry->Flags |= CA_TempRedirect; /* 302 Temporal Redirect */
566
567 location_str = Cache_parse_field(header, "Location");
568 entry->Location = a_Url_new(location_str, URL_STR_(entry->Url), 0, 0, 0);
569 g_free(location_str);
570
571 } else if ( strncmp(header + 9, "404", 3) == 0 ) {
572 entry->Flags |= CA_NotFound;
573 }
574
575 entry->ValidSize = io->Status - HdrLen;
576 if ( (Length = Cache_parse_field(header, "Content-Length")) != NULL ) {
577 entry->Flags |= CA_GotLength;
578 entry->TotalSize = strtol(Length, NULL, 10);
579 g_free(Length);
580 if (entry->TotalSize < entry->ValidSize)
581 entry->TotalSize = 0;
582 }
583
584 #ifndef DISABLE_COOKIES
585 /* BUG: If a server feels like mixing Set-Cookie2 and Set-Cookie
586 * responses which aren't identical, then we have a problem. I don't
587 * know if that is a real issue though. */
588 if ( (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie2")) ||
589 (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie")) ) {
590 a_Cookies_set(Cookies, entry->Url);
591 g_list_foreach(Cookies, (GFunc)g_free, NULL);
592 g_list_free(Cookies);
593 }
594 #endif /* !DISABLE_COOKIES */
595
596 if ( entry->TotalSize > 0 && entry->TotalSize >= entry->ValidSize ) {
597 entry->Data = g_malloc(entry->TotalSize);
598 memcpy(entry->Data, (char*)io->Buf+HdrLen, (size_t)io->Status-HdrLen);
599 /* Prepare next read */
600 a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize,
601 entry->TotalSize - entry->ValidSize);
602 } else {
603 /* We don't know the size of the transfer; A lazy server? ;) */
604 entry->Data = g_malloc(entry->ValidSize + entry->BuffSize);
605 memcpy(entry->Data, (char *)io->Buf+HdrLen, entry->ValidSize);
606 /* Prepare next read */
607 a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize,
608 entry->BuffSize);
609 }
610
611 /* Get Content-Type */
612 if ( (Type = Cache_parse_field(header, "Content-Type")) == NULL ) {
613 MSG_HTTP("Server didn't send Content-Type in header.\n");
614 } else {
615 entry->TypeHdr = Type;
616 /* This Content-Type is not trusted. It's checked against real data
617 * in Cache_process_queue(); only then CA_GotContentType becomes true.
618 */
619 }
620 }
621
622 /*
623 * Consume bytes until the whole header is got (up to a "\r\n\r\n" sequence)
624 * (Also strip '\r' chars from header)
625 */
626 static gint Cache_get_header(IOData_t *io, CacheData_t *entry)
627 {
628 gint N, i;
629 GString *hdr = entry->Header;
630 guchar *data = io->Buf;
631
632 /* Header finishes when N = 2 */
633 N = (hdr->len && hdr->str[hdr->len - 1] == '\n');
634 for ( i = 0; i < io->Status && N < 2; ++i ) {
635 if ( data[i] == '\r' || !data[i] )
636 continue;
637 N = (data[i] == '\n') ? N + 1 : 0;
638 g_string_append_c(hdr, data[i]);
639 }
640
641 if ( N == 2 ){
642 /* Got whole header */
643 _MSG("Header [io_len=%d]\n%s", i, hdr->str);
644 entry->Flags |= CA_GotHeader;
645 /* Return number of original-header bytes in this io [1 based] */
646 return i;
647 }
648 return 0;
649 }
650
651 /*
652 * Receive new data, update the reception buffer (for next read), update the
653 * cache, and service the client queue.
654 *
655 * This function gets called whenever the IO has new data.
656 * 'Op' is the operation to perform
657 * 'VPtr' is a (void) pointer to the IO control structure
658 */
659 static void Cache_process_io(int Op, void *VPtr)
660 {
661 gint Status, len;
662 IOData_t *io = VPtr;
663 const DilloUrl *Url = io->ExtData;
664 CacheData_t *entry = Cache_entry_search(Url);
665
666 /* Assert a valid entry (not aborted) */
667 if ( !entry )
668 return;
669
670 /* Keep track of this entry's io */
671 entry->io = io;
672
673 if ( Op == IOClose ) {
674 if (entry->Flags & CA_GotLength && entry->TotalSize != entry->ValidSize){
675 MSG_HTTP("Content-Length does NOT match message body,\n"
676 " at: %s\n", URL_STR_(entry->Url));
677 }
678 entry->Flags |= CA_GotData;
679 entry->Flags &= ~CA_Stopped; /* it may catch up! */
680 entry->TotalSize = entry->ValidSize;
681 entry->io = NULL;
682 entry->CCCAnswer = NULL;
683 Cache_process_queue(entry);
684 return;
685 } else if ( Op == IOAbort ) {
686 /* todo: implement Abort
687 * (eliminate cache entry and anything related) */
688 DEBUG_MSG(5, "Cache_process_io Op = IOAbort; not implemented yet\n");
689 entry->io = NULL;
690 entry->CCCAnswer = NULL;
691 return;
692 }
693
694 if ( !(entry->Flags & CA_GotHeader) ) {
695 /* Haven't got the whole header yet */
696 len = Cache_get_header(io, entry);
697 if ( entry->Flags & CA_GotHeader ) {
698 /* Let's scan, allocate, and set things according to header info */
699 Cache_parse_header(entry, io, len);
700 /* Now that we have it parsed, let's update our clients */
701 Cache_process_queue(entry);
702 }
703 return;
704 }
705
706 Status = io->Status;
707 entry->ValidSize += Status;
708 if ( Status < (gint)io->BufSize ) {
709 /* An incomplete buffer; update buffer & size */
710 a_IO_set_buf(io, (char *)io->Buf + Status, io->BufSize - Status);
711
712 } else if ( Status == (gint)io->BufSize ) {
713 /* A full buffer! */
714 if ( !entry->TotalSize ) {
715 /* We are receiving in small chunks... */
716 entry->Data = g_realloc(entry->Data,entry->ValidSize+entry->BuffSize);
717 a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize,
718 entry->BuffSize);
719 } else {
720 /* We have a preallocated buffer! */
721 a_IO_set_buf(io, (char *)io->Buf + Status, io->BufSize - Status);
722 }
723 }
724 Cache_process_queue(entry);
725 }
726
727 /*
728 * Process redirections (HTTP 30x answers)
729 * (This is a work in progress --not finished yet)
730 */
731 static gint Cache_redirect(CacheData_t *entry, gint Flags, BrowserWindow *bw)
732 {
733 DilloUrl *NewUrl;
734
735 _MSG(" Cache_redirect: redirect_level = %d\n", bw->redirect_level);
736
737 /* if there's a redirect loop, stop now */
738 if (bw->redirect_level >= 5)
739 entry->Flags |= CA_RedirectLoop;
740
741 if (entry->Flags & CA_RedirectLoop) {
742 a_Interface_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
743 bw->redirect_level = 0;
744 return 0;
745 }
746
747 if ((entry->Flags & CA_Redirect && entry->Location) &&
748 (entry->Flags & CA_ForceRedirect || entry->Flags & CA_TempRedirect ||
749 !entry->ValidSize || entry->ValidSize < 1024 )) {
750
751 _MSG(">>>Redirect from: %s\n to %s\n",
752 URL_STR_(entry->Url), URL_STR_(entry->Location));
753 _MSG("%s", entry->Header->str);
754
755 if (Flags & WEB_RootUrl) {
756 /* Redirection of the main page */
757 NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url),
758 0, 0, 0);
759 if (entry->Flags & CA_TempRedirect)
760 a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EReload);
761 a_Nav_push(bw, NewUrl);
762 a_Url_free(NewUrl);
763 } else {
764 /* Sub entity redirection (most probably an image) */
765 if ( !entry->ValidSize ) {
766 DEBUG_MSG(3,">>>Image redirection without entity-content<<<\n");
767 } else {
768 DEBUG_MSG(3, ">>>Image redirection with entity-content<<<\n");
769 }
770 }
771 }
772 return 0;
773 }
774
775 /*
776 * Check whether a URL scheme is downloadable.
777 * Return: 1 enabled, 0 disabled.
778 */
779 int Cache_download_enabled(const DilloUrl *url)
780 {
781 if (!strcasecmp(URL_SCHEME(url), "http") ||
782 !strcasecmp(URL_SCHEME(url), "https") ||
783 !strcasecmp(URL_SCHEME(url), "ftp") )
784 return 1;
785 return 0;
786 }
787
788 /*
789 * Don't process data any further, but let the cache fill the entry.
790 * (Currently used to handle WEB_RootUrl redirects,
791 * and to ignore image redirects --Jcid)
792 */
793 void a_Cache_null_client(int Op, CacheClient_t *Client)
794 {
795 DilloWeb *Web = Client->Web;
796
797 /* make the stop button insensitive when done */
798 if (Op == CA_Close) {
799 if (Web->flags & WEB_RootUrl) {
800 /* Remove this client from our active list */
801 a_Interface_close_client(Web->bw, Client->Key);
802 }
803 }
804
805 /* else ignore */
806
807 return;
808 }
809
810 /*
811 * Update cache clients for a single cache-entry
812 * Tasks:
813 * - Set the client function (if not already set)
814 * - Look if new data is available and pass it to client functions
815 * - Remove clients when done
816 * - Call redirect handler
817 *
818 * todo: Implement CA_Abort Op in client callback
819 */
820 static void Cache_process_queue(CacheData_t *entry)
821 {
822 guint i;
823 gint st;
824 const gchar *Type;
825 CacheClient_t *Client;
826 DilloWeb *ClientWeb;
827 BrowserWindow *Client_bw = NULL;
828 static gboolean Busy = FALSE;
829 gboolean AbortEntry = FALSE;
830 gboolean OfferDownload = FALSE;
831 gboolean TypeMismatch = FALSE;
832
833 if ( Busy )
834 DEBUG_MSG(5, "FATAL!:*** >>>> Cache_process_queue Caught busy!!!\n");
835 if (!(entry->Flags & CA_GotHeader))
836 return;
837 if (!(entry->Flags & CA_GotContentType)) {
838 st = a_Misc_get_content_type_from_data(
839 entry->Data, entry->ValidSize, &Type);
840 if (st == 0 || entry->Flags & CA_GotData) {
841 if (a_Misc_content_type_check(entry->TypeHdr, Type) < 0) {
842 MSG_HTTP("Content-Type '%s' doesn't match the real data.\n",
843 entry->TypeHdr);
844 TypeMismatch = TRUE;
845 }
846 entry->TypeDet = g_strdup(Type);
847 entry->Flags |= CA_GotContentType;
848 } else
849 return; /* i.e., wait for more data */
850 }
851
852 Busy = TRUE;
853 for ( i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i ) {
854 if ( Client->Url == entry->Url ) {
855 ClientWeb = Client->Web; /* It was a (void*) */
856 Client_bw = ClientWeb->bw; /* 'bw' in a local var */
857
858 if (ClientWeb->flags & WEB_RootUrl) {
859 if (!(entry->Flags & CA_MsgErased)) {
860 /* clear the "expecting for reply..." message */
861 a_Interface_msg(Client_bw, "");
862 entry->Flags |= CA_MsgErased;
863 }
864 if (TypeMismatch)
865 a_Interface_msg(Client_bw,"HTTP warning: Content-Type '%s' "
866 "doesn't match the real data.", entry->TypeHdr);
867 if (entry->Flags & CA_Redirect) {
868 if (!Client->Callback) {
869 Client->Callback = a_Cache_null_client;
870 Client_bw->redirect_level++;
871 }
872 } else {
873 Client_bw->redirect_level = 0;
874 }
875 } else {
876 /* For non root URLs, ignore redirections and 404 answers */
877 if (entry->Flags & CA_Redirect || entry->Flags & CA_NotFound)
878 Client->Callback = a_Cache_null_client;
879 }
880
881 /* Set the client function */
882 if (!Client->Callback) {
883 if (TypeMismatch) {
884 AbortEntry = TRUE;
885 Client->Callback = a_Cache_null_client;
886 } else {
887 st = a_Web_dispatch_by_type(
888 entry->TypeHdr ? entry->TypeHdr : entry->TypeDet,
889 ClientWeb, &Client->Callback, &Client->CbData);
890 if (st == -1) {
891 /* MIME type is not viewable */
892 Client->Callback = a_Cache_null_client;
893 if (ClientWeb->flags & WEB_RootUrl) {
894 /* Unhandled MIME type, prepare a download offer... */
895 AbortEntry = TRUE;
896 OfferDownload = TRUE;
897 } else {
898 /* TODO: Resource Type not handled.
899 * Not aborted to avoid multiple connections on the same
900 * resource. A better idea is to abort the connection and
901 * to keep a failed-resource flag in the cache entry. */
902 MSG_HTTP("Unhandled MIME type: <%s>\n",
903 entry->TypeHdr ? entry->TypeHdr:entry->TypeDet);
904 }
905 }
906 }
907 if (AbortEntry) {
908 a_Interface_remove_client(Client_bw, Client->Key);
909 Cache_client_dequeue(Client, NULLKey);
910 --i; /* Keep the index value in the next iteration */
911 continue;
912 }
913 }
914
915 /* Send data to our client */
916 if ( (Client->BufSize = entry->ValidSize) > 0) {
917 Client->Buf = (guchar *)entry->Data;
918 (Client->Callback)(CA_Send, Client);
919 }
920
921 /* Remove client when done */
922 if ( (entry->Flags & CA_GotData) ) {
923 /* Copy flags to a local var */
924 gint flags = ClientWeb->flags;
925 /* We finished sending data, let the client know */
926 if (!Client->Callback)
927 DEBUG_MSG(3, "Client Callback is NULL");
928 else
929 (Client->Callback)(CA_Close, Client);
930 Cache_client_dequeue(Client, NULLKey);
931 --i; /* Keep the index value in the next iteration */
932
933 /* call Cache_redirect() from this 'if' to assert one call only. */
934 if ( entry->Flags & CA_Redirect )
935 Cache_redirect(entry, flags, Client_bw);
936
937 _MSG("Cache_process_queue: NumRootClients=%d sens_idle_id = %d\n",
938 Client_bw->NumRootClients, Client_bw->sens_idle_id);
939 }
940 }
941 } /* for */
942
943 if (AbortEntry) {
944 /* Abort the entry, remove it from cache, and maybe offer download.
945 * (the dialog is made before 'entry' is freed) */
946 if (OfferDownload && Cache_download_enabled(entry->Url))
947 a_Interface_offer_link_download(Client_bw, entry->Url);
948 Cache_entry_remove(entry, NULL);
949 }
950
951 Busy = FALSE;
952 DEBUG_MSG(1, "QueueSize ====> %d\n", g_slist_length(ClientQueue));
953 }
954
955 /*
956 * Callback function for Cache_delayed_process_queue.
957 */
958 static gint Cache_delayed_process_queue_callback(gpointer data)
959 {
960 gpointer entry;
961
962 while ((entry = g_slist_nth_data(DelayedQueue, 0))) {
963 Cache_process_queue( (CacheData_t *)entry );
964 /* note that if Cache_process_queue removes the entry,
965 * the following g_slist_remove has no effect. */
966 DelayedQueue = g_slist_remove(DelayedQueue, entry);
967 }
968 DelayedQueueIdleId = 0;
969 return FALSE;
970 }
971
972 /*
973 * Set a call to Cache_process_queue from the gtk_main cycle.
974 */
975 static void Cache_delayed_process_queue(CacheData_t *entry)
976 {
977 /* there's no need to repeat entries in the queue */
978 if (!g_slist_find(DelayedQueue, entry))
979 DelayedQueue = g_slist_prepend(DelayedQueue, entry);
980
981 if (DelayedQueueIdleId == 0)
982 DelayedQueueIdleId =
983 gtk_idle_add((GtkFunction)Cache_delayed_process_queue_callback, NULL);
984 }
985
986
987 /*
988 * Remove a cache client
989 * todo: beware of downloads
990 */
991 static void Cache_remove_client_raw(CacheClient_t *Client, gint Key)
992 {
993 Cache_client_dequeue(Client, Key);
994 }
995
996 /*
997 * Remove every Interface-client of a single Url.
998 * todo: beware of downloads
999 * (this is transitory code)
1000 */
1001 static void Cache_remove_interface_clients(const DilloUrl *Url)
1002 {
1003 guint i;
1004 DilloWeb *Web;
1005 CacheClient_t *Client;
1006
1007 for ( i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i ) {
1008 if ( Client->Url == Url ) {
1009 Web = Client->Web;
1010 a_Interface_remove_client(Web->bw, Client->Key);
1011 }
1012 }
1013 }
1014
1015 /*
1016 * Remove a client from the client queue
1017 * todo: notify the dicache and upper layers
1018 */
1019 static void Cache_stop_client(gint Key, gint force)
1020 {
1021 CacheClient_t *Client;
1022 CacheData_t *entry;
1023 GSList *List;
1024 DilloUrl *url;
1025
1026 if (!(List = g_slist_find_custom(ClientQueue, GINT_TO_POINTER(Key),
1027 Cache_client_key_cmp))){
1028 _MSG("WARNING: Cache_stop_client, inexistent client\n");
1029 return;
1030 }
1031
1032 Client = List->data;
1033 url = (DilloUrl *)Client->Url;
1034 Cache_remove_client_raw(Client, NULLKey);
1035
1036 if (force &&
1037 !g_slist_find_custom(ClientQueue, url, Cache_client_url_cmp)) {
1038 /* it was the last client of this entry */
1039 if ((entry = Cache_entry_search(url))) {
1040 if (entry->CCCQuery) {
1041 a_Cache_ccc(OpAbort, 1, BCK, entry->CCCQuery, NULL, NULL);
1042 } else if (entry->CCCAnswer) {
1043 a_Cache_ccc(OpStop, 2, BCK, entry->CCCAnswer, NULL, Client);
1044 }
1045 }
1046 }
1047 }
1048
1049 /*
1050 * Remove a client from the client queue
1051 * (It may keep feeding the cache, but nothing else)
1052 */
1053 void a_Cache_stop_client(gint Key)
1054 {
1055 Cache_stop_client(Key, 0);
1056 }
1057
1058
1059 /*
1060 * CCC function for the CACHE module
1061 */
1062 void a_Cache_ccc(int Op, int Branch, int Dir, ChainLink *Info,
1063 void *Data1, void *Data2)
1064 {
1065 CacheData_t *entry;
1066
1067 a_Chain_debug_msg("a_Cache_ccc", Op, Branch, Dir);
1068
1069 if ( Branch == 1 ) {
1070 if (Dir == BCK) {
1071 /* Querying branch */
1072 switch (Op) {
1073 case OpStart:
1074 /* Localkey = entry->Url */
1075 Info->LocalKey = Data2;
1076 break;
1077 case OpStop:
1078 break;
1079 case OpAbort:
1080 Cache_entry_remove_raw(NULL, Info->LocalKey);
1081 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1082 g_free(Info);
1083 break;
1084 }
1085 } else { /* FWD */
1086 switch (Op) {
1087 case OpSend:
1088 /* Start the answer-reading chain */
1089 a_Cache_ccc(OpStart, 2, BCK, a_Chain_new(), Data1, Info->LocalKey);
1090 break;
1091 case OpEnd:
1092 /* unlink HTTP_Info */
1093 a_Chain_del_link(Info, BCK);
1094 /* 'entry->CCCQuery' and 'Info' point to the same place! */
1095 if ((entry = Cache_entry_search(Info->LocalKey)) != NULL)
1096 entry->CCCQuery = NULL;
1097 g_free(Info);
1098 break;
1099 case OpAbort:
1100 /* Unlink HTTP_Info */
1101 a_Chain_del_link(Info, BCK);
1102
1103 /* remove interface client-references of this entry */
1104 Cache_remove_interface_clients(Info->LocalKey);
1105 /* remove clients of this entry */
1106 Cache_stop_clients(Info->LocalKey, 0);
1107 /* remove the entry */
1108 Cache_entry_remove_raw(NULL, Info->LocalKey);
1109
1110 g_free(Info);
1111 break;
1112 }
1113 }
1114
1115 } else if (Branch == 2) {
1116 if (Dir == FWD) {
1117 /* Answering branch */
1118 switch (Op) {
1119 case OpStart:
1120 /* Data2 = entry->Url */
1121 Info->LocalKey = Data2;
1122 if ((entry = Cache_entry_search(Info->LocalKey))) {
1123 entry->CCCAnswer = Info;
1124 } else {
1125 /* The cache-entry was removed */
1126 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1127 g_free(Info);
1128 }
1129 break;
1130 case OpSend:
1131 /* Send data */
1132 if ((entry = Cache_entry_search(Info->LocalKey))) {
1133 Cache_process_io(IORead, Data1);
1134 } else {
1135 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1136 g_free(Info);
1137 }
1138 break;
1139 case OpEnd:
1140 /* Unlink HTTP_Info */
1141 a_Chain_del_link(Info, BCK);
1142 g_free(Info);
1143 Cache_process_io(IOClose, Data1);
1144 break;
1145 case OpAbort:
1146 a_Chain_del_link(Info, BCK);
1147 g_free(Info);
1148 Cache_process_io(IOAbort, Data1);
1149 break;
1150 }
1151 } else { /* BCK */
1152 switch (Op) {
1153 case OpStart:
1154 {
1155 IOData_t *io2;
1156
1157 Info->LocalKey = Data2;
1158 if ((entry = Cache_entry_search(Data2))) /* Is Data2 valid? */
1159 entry->CCCAnswer = Info;
1160
1161 /* Link IO to receive the answer */
1162 io2 = a_IO_new(IORead, *(int*)Data1);
1163 a_IO_set_buf(io2, NULL, IOBufLen);
1164 io2->ExtData = Data2; /* We have it as LocalKey */
1165 a_Chain_link_new(Info, a_Cache_ccc, BCK, a_IO_ccc, 2, 2);
1166 a_Chain_bcb(OpStart, Info, io2, NULL);
1167 break;
1168 }
1169 case OpStop:
1170 MSG(" Not implemented\n");
1171 break;
1172 case OpAbort:
1173 Cache_entry_remove_raw(NULL, Info->LocalKey);
1174 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1175 g_free(Info);
1176 break;
1177 }
1178 }
1179 }
1180 }
1181
1182 static gboolean
1183 Cache_remove_hash_entry(gpointer key, gpointer value, gpointer user_data)
1184 {
1185 Cache_entry_free((CacheData_t *)value);
1186 return TRUE;
1187 }
1188
1189
1190 /*
1191 * Memory deallocator (only called at exit time)
1192 */
1193 void a_Cache_freeall(void)
1194 {
1195 CacheClient_t *Client;
1196
1197 /* free the client queue */
1198 while ( (Client = g_slist_nth_data(ClientQueue, 0)) )
1199 Cache_client_dequeue(Client, NULLKey);
1200
1201 /* free the main cache */
1202 /* Remove every cache entry */
1203 g_hash_table_foreach_remove(CacheHash, (GHRFunc)Cache_remove_hash_entry,
1204 NULL);
1205 /* Remove the cache hash */
1206 g_hash_table_destroy(CacheHash);
1207 }
0 #ifndef __CACHE_H__
1 #define __CACHE_H__
2
3 #include <glib.h>
4 #include "chain.h"
5 #include "url.h"
6
7 /*
8 * Cache Op codes
9 */
10 #define CA_Send (0) /* Normal update */
11 #define CA_Close (1) /* Successful operation close */
12 #define CA_Abort (2) /* Operation abort */
13
14 /*
15 * Flag Defines
16 */
17 #define CA_GotHeader (1) /* True after header is completely got */
18 #define CA_GotContentType (2) /* True after Content-Type is known */
19 #define CA_GotLength (4) /* True if Content-Length is known */
20 #define CA_GotData (8) /* True if we have all Data in cache */
21 #define CA_FreeData (16) /* Free the cache Data on close */
22 #define CA_Redirect (32) /* Data actually points to a redirect */
23 #define CA_ForceRedirect (64) /* Unconditional redirect */
24 #define CA_TempRedirect (128) /* Temporal redirect */
25 #define CA_NotFound (256) /* True if remote server didn't find the URL */
26 #define CA_Stopped (512) /* True if the entry has been stopped */
27 #define CA_MsgErased (1024) /* Used to erase the bw's status bar */
28 #define CA_RedirectLoop (2048) /* Redirect loop */
29
30 /*
31 * Callback type for cache clients
32 */
33 typedef struct _CacheClient CacheClient_t;
34 typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
35
36 /*
37 * Data structure for cache clients.
38 */
39 struct _CacheClient {
40 gint Key; /* Primary Key for this client */
41 const DilloUrl *Url; /* Pointer to a cache entry Url */
42 guchar *Buf; /* Pointer to cache-data */
43 guint BufSize; /* Valid size of cache-data */
44 CA_Callback_t Callback; /* Client function */
45 void *CbData; /* Client function data */
46 void *Web; /* Pointer to the Web structure of our client */
47 };
48
49 /*
50 * Function prototypes
51 */
52 void a_Cache_init(void);
53 gint a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData);
54 gint a_Cache_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize);
55 void a_Cache_freeall(void);
56 void a_Cache_null_client(int Op, CacheClient_t *Client);
57 void a_Cache_stop_client(gint Key);
58
59
60 #endif /* __CACHE_H__ */
61
0 /*
1 * File: capi.c
2 *
3 * Copyright 2002 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Cache API
13 * This is the module that manages the cache and starts the CCC chains
14 * to get the requests served. Kind of a broker.
15 */
16
17 #include <string.h>
18 #include <unistd.h> /* for pipe */
19
20 #include "msg.h"
21 #include "capi.h"
22 #include "IO/Url.h"
23 #include "chain.h"
24 #include "list.h"
25 #include "interface.h"
26 #include "history.h"
27 #include "nav.h"
28 #include "misc.h"
29 #include "dpiapi.h"
30 #include "../dpip/dpip.h"
31
32 /* for testing dpi chat */
33 #include "bookmark.h"
34
35 #define DEBUG_LEVEL 5
36 #include "debug.h"
37
38 typedef struct {
39 DilloWeb *web;
40 DilloUrl *url; /* local copy of web->url. Used when the latter is freed */
41 void *bw;
42 gchar *server;
43 gchar *datastr;
44 gint SockFD;
45 gint Flags;
46 gint DpiPipe[2];
47 ChainLink *InfoSend;
48 ChainLink *InfoRecv;
49 ChainLink *InfoPipe;
50
51 gint Ref;
52 } dpi_conn_t;
53
54 /* Flags for conn */
55 enum {
56 PENDING = 1,
57 TIMEOUT = 2, /* unused */
58 ABORTED = 4
59 };
60
61 /*
62 * Local data
63 */
64 /* Data list for active dpi connections */
65 static dpi_conn_t **DpiConn = NULL;
66 static gint DpiConnSize;
67 static gint DpiConnMax = 4;
68
69 /* ID for the timeout function that waits for dpid to start */
70 static guint dpi_conn_timeout_id = 0;
71
72 /*
73 * Forward declarations
74 */
75 void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
76 void *Data1, void *Data2);
77
78
79 /* ------------------------------------------------------------------------- */
80
81 /*
82 * Create a new connection data structure
83 */
84 static dpi_conn_t *
85 Capi_dpi_conn_new(DilloWeb *web, void *bw, char *server, gchar *datastr)
86 {
87 dpi_conn_t *conn;
88
89 conn = g_new(dpi_conn_t, 1);
90 conn->web = web;
91 conn->url = (web) ? a_Url_dup(web->url) : NULL;
92 conn->bw = bw;
93 conn->server = g_strdup(server);
94 conn->datastr = g_strdup(datastr);
95 conn->SockFD = -1;
96 conn->Flags = PENDING;
97 conn->InfoSend = a_Chain_new();
98 conn->InfoRecv = NULL; /* will be set later */
99 conn->InfoPipe = NULL; /* may be set later */
100 conn->Ref = 0; /* Reference count */
101 return conn;
102 }
103
104 /*
105 * Increment the reference count and add to the list if not present
106 */
107 static void Capi_dpi_conn_ref(dpi_conn_t *conn)
108 {
109 if (++conn->Ref == 1) {
110 /* add the connection data to list */
111 a_List_add(DpiConn, DpiConnSize, DpiConnMax);
112 DpiConn[DpiConnSize] = conn;
113 DpiConnSize++;
114 }
115 }
116
117 /*
118 * Decrement the reference count (and remove from list when zero)
119 */
120 static void Capi_dpi_conn_unref(dpi_conn_t *conn)
121 {
122 gint i, j;
123
124 --conn->Ref;
125 if (conn->Ref == 0) {
126 for (i = 0; i < DpiConnSize; ++i)
127 if (DpiConn[i] == conn) {
128 /* remove conn preserving the list order */
129 for (j = i; j + 1 < DpiConnSize; ++j)
130 DpiConn[j] = DpiConn[j + 1];
131 --DpiConnSize;
132 /* free dynamic memory */
133 a_Url_free(conn->url);
134 g_free(conn->server);
135 g_free(conn->datastr);
136 g_free(conn);
137 break;
138 }
139 }
140 }
141
142 /*
143 * Find connection data by server
144 */
145 static dpi_conn_t *Capi_dpi_conn_find(gchar *server)
146 {
147 gint i;
148
149 for (i = 0; i < DpiConnSize; ++i)
150 if (strcmp(server, DpiConn[i]->server) == 0)
151 return DpiConn[i];
152
153 return NULL;
154 }
155
156 /*
157 * Resume connections that were waiting for dpid to start.
158 */
159 static void Capi_dpi_conn_resume(void)
160 {
161 gint i;
162 DataBuf *dbuf;
163
164 for (i = 0; i < DpiConnSize; ++i)
165 if (DpiConn[i]->Flags & PENDING) {
166 dpi_conn_t *conn = DpiConn[i];
167 dbuf = a_Chain_dbuf_new(conn->datastr,(gint)strlen(conn->datastr), 0);
168 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
169 g_free(dbuf);
170 conn->Flags &= ~PENDING;
171 }
172 }
173
174 /*
175 * Test dpid and resume connections if it already started.
176 */
177 static gint Capi_dpi_conn_timeout(gpointer data)
178 {
179 static gint try = 0;
180 gint i;
181
182 ++try;
183 DEBUG_MSG(5, "Capi_dpi_conn_timeout:: try %d\n", try);
184
185 for (i = 0; i < DpiConnSize; ++i)
186 if (DpiConn[i]->Flags & PENDING) {
187 dpi_conn_t *conn = DpiConn[i];
188 a_Chain_bcb(OpStart, conn->InfoSend, conn->server, NULL);
189 break;
190 }
191
192 for (i = 0; i < DpiConnSize; ++i)
193 if (DpiConn[i]->Flags & PENDING)
194 return TRUE;
195 try = 0;
196 dpi_conn_timeout_id = 0;
197 return FALSE;
198 }
199
200 /* ------------------------------------------------------------------------- */
201
202 /*
203 * Safety test: only allow dpi-urls from dpi-generated pages.
204 */
205 static gint Capi_verify_dpi_url_request(DilloWeb *web)
206 {
207 DilloUrl *referer;
208 gint allow = FALSE;
209
210 /* test POST and GET */
211 if (strchr(URL_STR(web->url), '?') || URL_DATA_(web->url)) {
212 /* safety measure: only allow dpi requests from dpi-generated urls */
213 if (a_Nav_stack_size(web->bw)) {
214 referer = a_History_get_url(NAV_TOP(web->bw));
215 if (g_strncasecmp(URL_STR(referer), "dpi:/", 5) == 0)
216 allow = TRUE;
217 }
218 } else {
219 allow = TRUE;
220 }
221
222 if (!allow) {
223 MSG("Capi_verify_dpi_url_request: Permission Denied!\n");
224 MSG(" URL_STR : %s\n", URL_STR(web->url));
225 if (URL_DATA_(web->url))
226 MSG(" URL_DATA: %s\n", URL_DATA(web->url));
227 }
228 return allow;
229 }
230
231 /*
232 * If the url belongs to a dpi server, return its name.
233 */
234 gint a_Capi_url_uses_dpi(gchar *url_str, gchar **server_ptr)
235 {
236 gchar *p, *server = NULL;
237
238 if (g_strncasecmp(url_str, "dpi:/", 5) == 0) {
239 /* dpi prefix, get this server's name */
240 if ((p = strchr(url_str + 5, '/')) != NULL)
241 server = g_strndup(url_str + 5, (guint)(p - url_str - 5));
242 else
243 server = g_strdup("?");
244
245 if (strcmp(server, "bm") == 0) {
246 g_free(server);
247 server = g_strdup("bookmarks");
248 }
249
250 } else if (g_strncasecmp(url_str, "ftp:/", 5) == 0) {
251 server = g_strdup("ftp");
252
253 } else if (g_strncasecmp(url_str, "https:/", 7) == 0) {
254 server = g_strdup("https");
255 } else if (g_strncasecmp(url_str, "file:", 5) == 0) {
256 server = g_strdup("file");
257 } else if (g_strncasecmp(url_str, "data:", 5) == 0) {
258 server = g_strdup("datauri");
259 }
260
261 return ((*server_ptr = server) ? 1 : 0);
262 }
263
264 /*
265 * Build the dpip command tag, according to URL and server.
266 * todo: make it PROXY-aware (AFAIS, it should be easy)
267 */
268 static gchar *Capi_dpi_build_cmd(DilloUrl *url, gchar *server)
269 {
270 gchar *cmd, *http_query;
271
272 if (strcmp(server, "https") == 0) {
273 /* Let's be kind and make the HTTP query string for the dpi */
274 http_query = a_Http_make_query_str(url, FALSE);
275 cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
276 "open_url", URL_STR(url), http_query);
277 g_free(http_query);
278
279 } else {
280 /* For everyone else, the url_str is enough... */
281 cmd = a_Dpip_build_cmd("cmd=%s url=%s", "open_url", URL_STR(url));
282 }
283 return cmd;
284 }
285
286 /*
287 * Most used function.
288 * todo: clean up the ad-hoc bindings with an API that allows dynamic
289 * addition of new plugins.
290 */
291 gint a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
292 {
293 gint buf_size;
294 char *cmd, *server, *buf;
295 char *url_str = URL_STR(web->url);
296 gint use_cache = 0, safe = 0, reload = 0, ret = 0;
297
298 _MSG(" a_Capi_open_url:: web->Image=%p\n", web->Image);
299
300 if (a_Capi_url_uses_dpi(url_str, &server) && !Call) {
301
302 _MSG(" url_str = %s\n", url_str);
303
304 if (g_strncasecmp(url_str, "dpi:/", 5) == 0) {
305 /* safety check... */
306 safe = Capi_verify_dpi_url_request(web);
307 /* make "dpi:/" prefixed urls always reload. */
308 a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EReload);
309 reload = 1;
310 } else {
311 /* reload test */
312 reload = (!a_Capi_get_buf(web->url, &buf, &buf_size) ||
313 (URL_FLAGS(web->url) & URL_E2EReload));
314 safe = 1;
315 }
316
317 _MSG(" reload=%d URL_E2EReload=%d\n", reload,
318 (URL_FLAGS(web->url) & URL_E2EReload));
319
320 if (safe && reload) {
321 /* Send dpip command */
322 cmd = Capi_dpi_build_cmd(web->url, server);
323 a_Capi_dpi_send_cmd(web, web->bw, cmd, server, 1);
324 g_free(cmd);
325
326 /* test the new dpi-cache connection! */
327 use_cache = 1;
328
329 } else if (safe && !reload) {
330 use_cache = 1;
331 }
332 g_free(server);
333
334 } else {
335 use_cache = 1;
336 }
337
338 if (use_cache)
339 ret = a_Cache_open_url(web, Call, CbData);
340 else
341 a_Web_free(web);
342
343 return ret;
344 }
345
346 /*
347 * Get the cache's buffer for the URL, and its size.
348 * Return: 1 cached, 0 not cached.
349 */
350 gint a_Capi_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize)
351 {
352 return a_Cache_get_buf(Url, PBuf, BufSize);
353 }
354
355 /*
356 * Send a dpi cmd.
357 * (For instance: add_bookmark, open_url, send_preferences, ...)
358 */
359 gint a_Capi_dpi_send_cmd(DilloWeb *web, void *bw, char *cmd, char *server,
360 gint flags)
361 {
362 dpi_conn_t *conn;
363 DataBuf *dbuf;
364
365 if (flags & 1) {
366 /* open a new connection to server */
367
368 /* Create a new connection data struct and add it to the list */
369 conn = Capi_dpi_conn_new(web, bw, server, cmd);
370 /* start the CCC operations */
371 a_Capi_ccc(OpStart, 1, BCK, conn->InfoSend, conn, server);
372
373 } else {
374 /* Re-use an open connection */
375 conn = Capi_dpi_conn_find(server);
376 if (conn) {
377 /* found */
378 dbuf = a_Chain_dbuf_new(cmd, (gint)strlen(cmd), 0);
379 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
380 g_free(dbuf);
381 } else {
382 MSG(" ERROR: [a_Capi_dpi_send_cmd] No open connection found\n");
383 }
384 }
385
386 return 0;
387 }
388
389
390 /*
391 * CCC function for the CAPI module
392 */
393 void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
394 void *Data1, void *Data2)
395 {
396 dpi_conn_t *conn;
397
398 a_Chain_debug_msg("a_Capi_ccc", Op, Branch, Dir);
399
400 if (Branch == 1) {
401 if (Dir == BCK) {
402 /* Command sending branch */
403 switch (Op) {
404 case OpStart:
405 conn = Data1;
406 Capi_dpi_conn_ref(conn);
407 Info->LocalKey = Data1;
408 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 1);
409 a_Chain_bcb(OpStart, Info, Data2, NULL);
410 break;
411 case OpSend:
412 a_Chain_bcb(OpSend, Info, Data1, NULL);
413 break;
414 case OpEnd:
415 a_Chain_bcb(OpEnd, Info, NULL, NULL);
416 Capi_dpi_conn_unref(Info->LocalKey);
417 g_free(Info);
418 break;
419 case OpStop:
420 case OpAbort:
421 MSG(" Not implemented\n");
422 break;
423 }
424 } else { /* FWD */
425 /* Command sending branch (status) */
426 switch (Op) {
427 case OpSend:
428 if (!Data2) {
429 g_warning("Capi.c: Opsend [1F] Data2 = NULL\n");
430 } else if (strcmp(Data2, "SockFD") == 0) {
431 /* start the receiving branch */
432 dpi_conn_t *conn = Info->LocalKey;
433 conn->SockFD = *(int*)Data1;
434 a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(),Info->LocalKey, NULL);
435 } else if (strcmp(Data2, "DpidOK") == 0) {
436 /* send the data inmediatly! */
437 Capi_dpi_conn_resume();
438 } else if (strcmp(Data2, "DpidEAGAIN") == 0) {
439 /* set a timeout function to retry later... */
440 if (dpi_conn_timeout_id == 0)
441 dpi_conn_timeout_id =
442 gtk_timeout_add(250, (GtkFunction) Capi_dpi_conn_timeout,
443 NULL);
444 }
445 break;
446 case OpStop:
447 case OpAbort:
448 conn = Info->LocalKey;
449 if (Data1 && !strcmp(Data1, "ERR_dpid"))
450 a_Interface_msg(conn->bw, "ERROR: can't start dpid daemon!");
451 Capi_dpi_conn_unref(conn);
452 g_free(Info);
453 break;
454 }
455 }
456
457 } else if (Branch == 2) {
458 if (Dir == BCK) {
459 /* Server listening branch (status) */
460 switch (Op) {
461 case OpStart:
462 {
463 dpi_conn_t *conn = Data1;
464 Capi_dpi_conn_ref(conn);
465 Info->LocalKey = Data1;
466 conn->InfoRecv = Info;
467 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 3, 2);
468 a_Chain_bcb(OpStart, Info, &conn->SockFD, "SockFD");
469 break;
470 }
471 case OpStop:
472 case OpAbort:
473 Capi_dpi_conn_unref(Info->LocalKey);
474 MSG(" Not implemented\n");
475 break;
476 }
477 } else { /* FWD */
478 /* Server listening branch */
479 switch (Op) {
480 case OpSend:
481 conn = Info->LocalKey;
482 if (conn->Flags & ABORTED ||
483 (conn->web && !a_Web_valid(conn->web))) {
484 /* there's no client for this transfer!*/
485 _MSG(" ** Capi 2F catched an invalid 'web' structure\n");
486 /* this flag is used just in case the same memory address
487 * is reused for a new 'web' and the test passes */
488 conn->Flags &= ABORTED;
489 /* Make the dpi module stop this transfer */
490 a_Chain_bcb(OpStop, Info, conn->url, NULL);
491
492 } else if (strcmp(Data2, "send_status_message") == 0) {
493 a_Interface_msg(conn->bw, "%s", Data1);
494 } else if (strcmp(Data2, "chat") == 0) {
495 a_Interface_msg(conn->bw, "%s", Data1);
496 a_Bookmarks_chat_add(NULL, NULL, Data1);
497 } else if (strcmp(Data2, "dialog") == 0) {
498 a_Dpiapi_dialog(conn->bw, conn->server, Data1);
499 } else if (strcmp(Data2, "start_send_page") == 0) {
500 /* start the pipe-writing chain */
501 a_Capi_ccc(OpStart, 3, BCK, a_Chain_new(), Info->LocalKey,NULL);
502 /* let the dpi know the reading end of the pipe */
503 a_Chain_bcb(OpSend, Info, &conn->DpiPipe[0], conn->web->url);
504 } else if (strcmp(Data2, "send_page_2eof") == 0) {
505 a_Capi_ccc(OpSend, 3, BCK, conn->InfoPipe, Data1, NULL);
506 } else if (strcmp(Data2, "reload_request") == 0) {
507 a_Nav_reload(conn->bw);
508 }
509 break;
510 case OpEnd:
511 {
512 dpi_conn_t *conn = Info->LocalKey;
513 a_Chain_del_link(Info, BCK);
514 conn->InfoRecv = NULL;
515 if (conn->InfoSend) {
516 /* Propagate OpEnd to the sending branch too */
517 a_Capi_ccc(OpEnd, 1, BCK, conn->InfoSend, NULL, NULL);
518 }
519 if (conn->InfoPipe) {
520 /* Propagate OpEnd to the pipe branch too */
521 a_Capi_ccc(OpEnd, 3, BCK, conn->InfoPipe, NULL, NULL);
522 }
523 Capi_dpi_conn_unref(conn);
524 g_free(Info);
525 break;
526 }
527 case OpStop:
528 case OpAbort:
529 MSG(" Not implemented\n");
530 break;
531 }
532 }
533
534 } else if (Branch == 3) {
535 if (Dir == BCK) {
536 /* Pipe writing branch */
537 switch (Op) {
538 case OpStart:
539 {
540 dpi_conn_t *conn = Data1;
541 Info->LocalKey = Data1;
542 Capi_dpi_conn_ref(conn);
543 conn->InfoPipe = Info;
544 if (pipe(conn->DpiPipe)) {
545 MSG(" Error with pipe\n");
546 return;
547 }
548 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_IO_ccc, 3, 3);
549 a_Chain_bcb(OpStart, Info, &conn->DpiPipe[1], "SockFD");
550 break;
551 }
552 case OpSend:
553 a_Chain_bcb(OpSend, Info, Data1, NULL);
554 break;
555 case OpEnd:
556 a_Chain_bcb(OpEnd, Info, (void*)1, NULL);
557 Capi_dpi_conn_unref(Info->LocalKey);
558 g_free(Info);
559 break;
560 case OpStop:
561 case OpAbort:
562 Capi_dpi_conn_unref(Info->LocalKey);
563 MSG(" Not implemented\n");
564 break;
565 }
566 } else { /* FWD */
567 /* Pipe branch (status) */
568 }
569 }
570 }
0 #ifndef __CAPI_H__
1 #define __CAPI_H__
2
3 #include <glib.h>
4 #include "cache.h"
5 #include "web.h"
6
7 /*
8 * Function prototypes
9 */
10 gint a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData);
11 gint a_Capi_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize);
12 gint a_Capi_dpi_send_cmd(DilloWeb *web, void *bw, char *cmd, char *server,
13 gint flags);
14 gint a_Capi_url_uses_dpi(gchar *url_str, gchar **server_ptr);
15
16
17
18 #endif /* __CAPI_H__ */
19
0 /*
1 * File: chain.c
2 * Concomitant control chain (CCC)
3 * Theory and code by Jorge Arellano Cid
4 *
5 * Copyright 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 #include "chain.h"
14
15 #define VERBOSE 0
16
17 /*
18 * Create and initialize a new chain-link
19 */
20 ChainLink *a_Chain_new(void)
21 {
22 return g_new0(ChainLink, 1);
23 }
24
25 /*
26 * Create a new link from module A to module B.
27 * 'Direction' tells whether to make a forward or backward link.
28 * => The link from 'A' to 'B' has 'Direction' direction.
29 * => The main flow of information names the FWD direction.
30 * => AtoB_branch: branch on which 'B' receives communications from 'A'
31 * => BtoA_branch: branch on which 'A' receives communications from 'B'
32 */
33 ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
34 gint Direction, ChainFunction_t BFunc,
35 gint AtoB_branch, gint BtoA_branch)
36 {
37 ChainLink *NewLink = a_Chain_new();
38 ChainLink *OldLink = AInfo;
39
40 if ( Direction == BCK ) {
41 NewLink->Fcb = AFunc;
42 NewLink->FcbInfo = AInfo;
43 NewLink->FcbBranch = BtoA_branch;
44 OldLink->Bcb = BFunc;
45 OldLink->BcbInfo = NewLink;
46 OldLink->BcbBranch = AtoB_branch;
47
48 } else { /* FWD */
49 NewLink->Bcb = AFunc;
50 NewLink->BcbInfo = AInfo;
51 NewLink->BcbBranch = BtoA_branch;
52 OldLink->Fcb = BFunc;
53 OldLink->FcbInfo = NewLink;
54 OldLink->FcbBranch = AtoB_branch;
55 }
56
57 return NewLink;
58 }
59
60 /*
61 * Destroy a link and update the referer.
62 * 'Direction' tells whether to delete the forward or backward link.
63 */
64 void a_Chain_del_link(ChainLink *Info, gint Direction)
65 {
66 ChainLink *DeadLink;
67
68 if ( Direction == FWD ) {
69 DeadLink = Info->FcbInfo;
70 Info->Fcb = NULL;
71 Info->FcbInfo = NULL;
72 Info->FcbBranch = 0;
73 } else { /* BCK */
74 DeadLink = Info->BcbInfo;
75 Info->Bcb = NULL;
76 Info->BcbInfo = NULL;
77 Info->BcbBranch = 0;
78 }
79 g_free(DeadLink);
80 }
81
82 /*
83 * Issue the forward callback of the 'Info' link
84 */
85 gint a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
86 {
87 if ( Info->Fcb ) {
88 Info->Fcb(Op, Info->FcbBranch, FWD, Info->FcbInfo, Data1, Data2);
89 return 1;
90 }
91 return 0;
92 }
93
94 /*
95 * Issue the backward callback of the 'Info' link
96 */
97 gint a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
98 {
99 if ( Info->Bcb ) {
100 Info->Bcb(Op, Info->BcbBranch, BCK, Info->BcbInfo, Data1, Data2);
101 return 1;
102 }
103 return 0;
104 }
105
106
107 /*
108 * Allocate and initialize a new DataBuf structure
109 */
110 DataBuf *a_Chain_dbuf_new(void *buf, gint size, gint code)
111 {
112 DataBuf *dbuf = g_new(DataBuf, 1);
113 dbuf->Buf = buf;
114 dbuf->Size = size;
115 dbuf->Code = code;
116 return dbuf;
117 }
118
119 /*
120 * Show some debugging info
121 */
122 void a_Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir)
123 {
124 #if VERBOSE
125 const gchar *StrOps[] = {"", "OpStart", "OpSend",
126 "OpStop", "OpEnd", "OpAbort"};
127 g_print("%-*s: %-*s [%d%s]\n",
128 12, FuncStr, 7, StrOps[Op], Branch, (Dir == 1) ? "F" : "B");
129 #endif
130 }
0 #ifndef __CHAIN_H__
1 #define __CHAIN_H__
2
3 /*
4 * Concomitant control chain (CCC)
5 * Theory and code by Jorge Arellano Cid <jcid@dillo.org>
6 */
7
8 #include <glib.h>
9
10 /*
11 * Supported CCC operations
12 */
13 #define OpStart 1
14 #define OpSend 2
15 #define OpStop 3
16 #define OpEnd 4
17 #define OpAbort 5
18
19
20 /*
21 * Linking direction
22 */
23 #define FWD 1
24 #define BCK 2
25
26
27 typedef struct _ChainLink ChainLink;
28 typedef struct _DataBuf DataBuf;
29 typedef void (*ChainFunction_t)(int Op, int Branch, int Dir, ChainLink *Info,
30 void *Data1, void *Data2);
31
32 /* This is the main data structure for CCC nodes */
33 struct _ChainLink {
34 void *LocalKey;
35
36 ChainLink *FcbInfo;
37 ChainFunction_t Fcb;
38 gint FcbBranch;
39
40 ChainLink *BcbInfo;
41 ChainFunction_t Bcb;
42 gint BcbBranch;
43 };
44
45 /* A convenience data structure for passing data chunks between nodes */
46 struct _DataBuf {
47 gchar *Buf;
48 gint Size;
49 gint Code;
50 };
51
52
53
54 /*
55 * Function prototypes
56 */
57 ChainLink *a_Chain_new(void);
58 ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
59 gint Direction, ChainFunction_t BFunc,
60 gint AtoB_branch, gint BtoA_branch);
61 void a_Chain_del_link(ChainLink *Info, gint Direction);
62 gint a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2);
63 gint a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2);
64
65 DataBuf *a_Chain_dbuf_new(void *buf, gint size, gint code);
66 void a_Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir);
67
68 /*
69 * CCC functions of subscribing modules
70 */
71 void a_Cache_ccc(int Op, int Branch, int Dir, ChainLink *Info,
72 void *Data1, void *Data2);
73
74 #endif /* __CHAIN_H__ */
0 #!/bin/sh
1 #
2 # Shell script for name changing source code
3 #
4
5 if [ ! $# = 3 ]; then
6 echo "Usage: chg <source> <old_word> <new_word>"
7 echo " (this script changes <source> directly)"
8 exit 1
9 fi
10
11 if [ ! -r $1 ]; then
12 echo "source file ->$1<- doesn't exist..."
13 exit 1
14 fi
15
16 if [ ! -r $1.BAK ]; then
17 echo "creating backup file: $1.BAK"
18 cp $1 $1.BAK
19 fi
20
21 sed "s/$2/$3/g" $1 > out
22 #sed s/$2/$3/ $1 > out
23 rm $1
24 mv out $1
25 echo "done!"
26
27
0 #include <string.h>
1 #include <stdlib.h>
2 #include <glib.h>
3 #include <ctype.h>
4 #include "colors.h"
5
6 #define DEBUG_LEVEL 5
7 #include "debug.h"
8 #include "msg.h"
9
10 /*
11 * If EXTENDED_COLOR is defined, the extended set of named colors is supported.
12 * These colors're not standard but they're supported in most browsers.
13 * NOTE: The colors MUST be in alphabetical order and lower case because the
14 * code uses a binary search.
15 */
16
17 #define EXTENDED_COLOR
18
19 static const struct key {
20 char *key;
21 gint32 val;
22 } color_keyword [] = {
23 #ifdef EXTENDED_COLOR
24 { "aliceblue", 0xf0f8ff},
25 { "antiquewhite", 0xfaebd7},
26 #endif
27 { "aqua", 0x00ffff},
28 #ifdef EXTENDED_COLOR
29 { "aquamarine", 0x7fffd4},
30 { "azure", 0xf0ffff},
31 { "beige", 0xf5f5dc},
32 { "bisque", 0xffe4c4},
33 #endif
34 { "black", 0x000000},
35 #ifdef EXTENDED_COLOR
36 { "blanchedalmond", 0xffebcd},
37 #endif
38 {"blue", 0x0000ff},
39 #ifdef EXTENDED_COLOR
40 { "blueviolet", 0x8a2be2},
41 { "brown", 0xa52a2a},
42 { "burlywood", 0xdeb887},
43 { "cadetblue", 0x5f9ea0},
44 { "chartreuse", 0x7fff00},
45 { "chocolate", 0xd2691e},
46 { "coral", 0xff7f50},
47 { "cornflowerblue", 0x6495ed},
48 { "cornsilk", 0xfff8dc},
49 { "crimson", 0xdc1436},
50 { "cyan", 0x00ffff},
51 { "darkblue", 0x00008b},
52 { "darkcyan", 0x008b8b},
53 { "darkgoldenrod", 0xb8860b},
54 { "darkgray", 0xa9a9a9},
55 { "darkgreen", 0x006400},
56 { "darkkhaki", 0xbdb76b},
57 { "darkmagenta", 0x8b008b},
58 { "darkolivegreen", 0x556b2f},
59 { "darkorange", 0xff8c00},
60 { "darkorchid", 0x9932cc},
61 { "darkred", 0x8b0000},
62 { "darksalmon", 0xe9967a},
63 { "darkseagreen", 0x8fbc8f},
64 { "darkslateblue", 0x483d8b},
65 { "darkslategray", 0x2f4f4f},
66 { "darkturquoise", 0x00ced1},
67 { "darkviolet", 0x9400d3},
68 { "deeppink", 0xff1493},
69 { "deepskyblue", 0x00bfff},
70 { "dimgray", 0x696969},
71 { "dodgerblue", 0x1e90ff},
72 { "firebrick", 0xb22222},
73 { "floralwhite", 0xfffaf0},
74 { "forestgreen", 0x228b22},
75 #endif
76 { "fuchsia", 0xff00ff},
77 #ifdef EXTENDED_COLOR
78 { "gainsboro", 0xdcdcdc},
79 { "ghostwhite", 0xf8f8ff},
80 { "gold", 0xffd700},
81 { "goldenrod", 0xdaa520},
82 #endif
83 { "gray", 0x808080},
84 { "green", 0x008000},
85 #ifdef EXTENDED_COLOR
86 { "greenyellow", 0xadff2f},
87 { "honeydew", 0xf0fff0},
88 { "hotpink", 0xff69b4},
89 { "indianred", 0xcd5c5c},
90 { "indigo", 0x4b0082},
91 { "ivory", 0xfffff0},
92 { "khaki", 0xf0e68c},
93 { "lavender", 0xe6e6fa},
94 { "lavenderblush", 0xfff0f5},
95 { "lawngreen", 0x7cfc00},
96 { "lemonchiffon", 0xfffacd},
97 { "lightblue", 0xadd8e6},
98 { "lightcoral", 0xf08080},
99 { "lightcyan", 0xe0ffff},
100 { "lightgoldenrodyellow", 0xfafad2},
101 { "lightgreen", 0x90ee90},
102 { "lightgrey", 0xd3d3d3},
103 { "lightpink", 0xffb6c1},
104 { "lightsalmon", 0xffa07a},
105 { "lightseagreen", 0x20b2aa},
106 { "lightskyblue", 0x87cefa},
107 { "lightslategray", 0x778899},
108 { "lightsteelblue", 0xb0c4de},
109 { "lightyellow", 0xffffe0},
110 #endif
111 { "lime", 0x00ff00},
112 #ifdef EXTENDED_COLOR
113 { "limegreen", 0x32cd32},
114 { "linen", 0xfaf0e6},
115 { "magenta", 0xff00ff},
116 #endif
117 { "maroon", 0x800000},
118 #ifdef EXTENDED_COLOR
119 { "mediumaquamarine", 0x66cdaa},
120 { "mediumblue", 0x0000cd},
121 { "mediumorchid", 0xba55d3},
122 { "mediumpurple", 0x9370db},
123 { "mediumseagreen", 0x3cb371},
124 { "mediumslateblue", 0x7b68ee},
125 { "mediumspringgreen", 0x00fa9a},
126 { "mediumturquoise", 0x48d1cc},
127 { "mediumvioletred", 0xc71585},
128 { "midnightblue", 0x191970},
129 { "mintcream", 0xf5fffa},
130 { "mistyrose", 0xffe4e1},
131 { "moccasin", 0xffe4b5},
132 { "navajowhite", 0xffdead},
133 #endif
134 { "navy", 0x000080},
135 #ifdef EXTENDED_COLOR
136 { "oldlace", 0xfdf5e6},
137 #endif
138 { "olive", 0x808000},
139 #ifdef EXTENDED_COLOR
140 { "olivedrab", 0x6b8e23},
141 { "orange", 0xffa500},
142 { "orangered", 0xff4500},
143 { "orchid", 0xda70d6},
144 { "palegoldenrod", 0xeee8aa},
145 { "palegreen", 0x98fb98},
146 { "paleturquoise", 0xafeeee},
147 { "palevioletred", 0xdb7093},
148 { "papayawhip", 0xffefd5},
149 { "peachpuff", 0xffdab9},
150 { "peru", 0xcd853f},
151 { "pink", 0xffc0cb},
152 { "plum", 0xdda0dd},
153 { "powderblue", 0xb0e0e6},
154 #endif
155 { "purple", 0x800080},
156 { "red", 0xff0000},
157 #ifdef EXTENDED_COLOR
158 { "rosybrown", 0xbc8f8f},
159 { "royalblue", 0x4169e1},
160 { "saddlebrown", 0x8b4513},
161 { "salmon", 0xfa8072},
162 { "sandybrown", 0xf4a460},
163 { "seagreen", 0x2e8b57},
164 { "seashell", 0xfff5ee},
165 { "sienna", 0xa0522d},
166 #endif
167 { "silver", 0xc0c0c0},
168 #ifdef EXTENDED_COLOR
169 { "skyblue", 0x87ceeb},
170 { "slateblue", 0x6a5acd},
171 { "slategray", 0x708090},
172 { "snow", 0xfffafa},
173 { "springgreen", 0x00ff7f},
174 { "steelblue", 0x4682b4},
175 { "tan", 0xd2b48c},
176 #endif
177 { "teal", 0x008080},
178 #ifdef EXTENDED_COLOR
179 { "thistle", 0xd8bfd8},
180 { "tomato", 0xff6347},
181 { "turquoise", 0x40e0d0},
182 { "violet", 0xee82ee},
183 { "wheat", 0xf5deb3},
184 #endif
185 { "white", 0xffffff},
186 #ifdef EXTENDED_COLOR
187 { "whitesmoke", 0xf5f5f5},
188 #endif
189 { "yellow", 0xffff00},
190 #ifdef EXTENDED_COLOR
191 { "yellowgreen", 0x9acd32},
192 #endif
193 };
194
195 #define NCOLORS (sizeof(color_keyword) / sizeof(struct key))
196
197 /*
198 * Parse a color in hex (RRGGBB)
199 *
200 * Return Value:
201 * parsed color if successful (err = 0),
202 * default_color on error (err = 1).
203 */
204 static gint32 Color_parse_hex (const char *s, gint32 default_color, gint *err)
205 {
206 gint32 ret_color;
207 gchar *tail;
208
209 *err = 1;
210 ret_color = strtol(s, &tail, 16);
211 if ( tail - s == 6 )
212 *err = 0;
213 else
214 ret_color = default_color;
215
216 return ret_color;
217 }
218
219 /*
220 * Parse the color info from a subtag.
221 * If subtag string begins with # or with 0x simply return the color number
222 * otherwise search one color from the set of named colors.
223 *
224 * Return Value:
225 * Parsed color if successful,
226 * default_color (+ error code) on error.
227 */
228 gint32 a_Color_parse (const char *subtag, gint32 default_color, gint *err)
229 {
230 const char *cp;
231 gint32 ret_color;
232 gint ret, low, mid, high, st = 1;
233
234 /* skip leading spaces */
235 for (cp = subtag; isspace(*cp); cp++);
236
237 ret_color = default_color;
238 if (*cp == '#') {
239 ret_color = Color_parse_hex(cp + 1, default_color, &st);
240
241 } else if (*cp == '0' && (cp[1] == 'x' || cp[1] == 'X') ) {
242 ret_color = Color_parse_hex(cp + 2, default_color, &st);
243 st = 2;
244
245 } else {
246 /* Binary search */
247 low = 0;
248 high = NCOLORS - 1;
249 while (low <= high) {
250 mid = (low + high) / 2;
251 if ((ret = g_strcasecmp(cp, color_keyword[mid].key)) < 0)
252 high = mid - 1;
253 else if (ret > 0)
254 low = mid + 1;
255 else {
256 ret_color = color_keyword[mid].val;
257 st = 0;
258 break;
259 }
260 }
261
262 if (low > high) {
263 /* try for RRGGBB lacking the leading '#' */
264 ret_color = Color_parse_hex(cp, default_color, &st);
265 st = 1;
266 }
267 }
268
269 DEBUG_MSG(3, "subtag: %s\n", subtag);
270 DEBUG_MSG(3, "color : %X\n", ret_color);
271
272 *err = st;
273 return ret_color;
274 }
275
276 #if 0
277 /*
278 * Return a "distance" measure (between [0, 10])
279 */
280 static int Color_distance(long c1, long c2)
281 {
282 return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) +
283 labs(((c1 & 0x00ff00) - (c2 & 0x00ff00)) >> 8) +
284 labs(((c1 & 0xff0000) - (c2 & 0xff0000)) >> 16)) / 75;
285 }
286 #endif
287
288 /*
289 * Return: [0-3]
290 */
291 static int Color_distance2(long c1, long c2)
292 {
293 return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) >= 0x000060) +
294 (labs((c1 & 0x00ff00) - (c2 & 0x00ff00)) >= 0x006000) +
295 (labs((c1 & 0xff0000) - (c2 & 0xff0000)) >= 0x600000);
296 }
297
298 /*
299 * Return: [0-3] (requires less contrast than distance2)
300 */
301 static int Color_distance3(long c1, long c2)
302 {
303 return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) >= 0x000040) +
304 (labs((c1 & 0x00ff00) - (c2 & 0x00ff00)) >= 0x004000) +
305 (labs((c1 & 0xff0000) - (c2 & 0xff0000)) >= 0x400000);
306 }
307
308 /*
309 * Return a suitable "visited link" color
310 * Return value:
311 * if candidate has good contrast with C_txt, C_lnk and C_bg -> candidate
312 * else another color (from the internal list)
313 */
314 gint32 a_Color_vc(gint32 candidate, gint32 C_txt, gint32 C_lnk, gint32 C_bg)
315 {
316 /* candidate purple darkcyan darkmagenta olive */
317 static gint32 v[] = {0x000000, 0x800080, 0x008b8b, 0x8b008b, 0x808000,
318 /* darkred coral black */
319 0x8b0000, 0xff7f50, 0x000000};
320 gint v_size = sizeof(v) / sizeof(v[0]);
321 gint i, max_i, score, max_score, d_bg, d_txt, d_lnk;
322
323
324 /* set candidate in the list */
325 v[0] = candidate;
326
327 /* Try to get good overall and individual contrast */
328 max_i = max_score = 0;
329 for (i = 0; i < v_size; ++i) {
330 _MSG("a_Color_vc: [%d]%.6x: %d %d %d\n", i, v[i],
331 Color_distance2(C_txt, v[i]),
332 Color_distance2(C_lnk, v[i]),
333 Color_distance2(C_bg, v[i]));
334
335 /* Tuned with: slashdot.org, paulgraham.com, newsforge.com,
336 * linuxjournal.com
337 */
338 d_txt = Color_distance2(C_txt, v[i]);
339 d_lnk = Color_distance2(C_lnk, v[i]);
340 d_bg = Color_distance2(C_bg, v[i]);
341 score = (d_bg >= 2 ? 4 : 2 * d_bg) +
342 (d_txt + d_lnk >= 2 ? 2 : d_txt + d_lnk) +
343 (Color_distance3(C_lnk, v[i]) >= 1 ? 1 : 0);
344 if (score >= 7) {
345 /* enough distance, use this color */
346 max_i = i;
347 break;
348 } else if (score > max_score) {
349 /* keep track of the best candidate so far */
350 max_score = score;
351 max_i = i;
352 }
353 }
354 return v[max_i];
355 }
0 #ifndef __COLORS_H__
1 #define __COLORS_H__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 gint32 a_Color_parse (const char *subtag, gint32 default_color, gint *err);
8 gint32 a_Color_vc(gint32 candidate, gint32 c1, gint32 c2, gint32 c3);
9
10 #ifdef __cplusplus
11 }
12 #endif /* __cplusplus */
13
14 #endif /* __COLORS_H__ */
0 /*
1 * File: commands.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Sammy Mannaert <nstalkie@tvd.be>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <gtk/gtk.h>
13 #include <stdio.h> /* for sprintf */
14 #include <sys/time.h> /* for gettimeofday (testing gorp only) */
15 #include <unistd.h>
16 #include <string.h> /* for strcat() */
17
18 #include "msg.h"
19 #include "bookmark.h"
20 #include "interface.h"
21 #include "history.h"
22 #include "nav.h"
23 #include "misc.h"
24 #include "commands.h"
25 #include "prefs.h"
26 #include "menu.h"
27 #include "capi.h"
28 #include "selection.h"
29
30 /* FILE MENU */
31
32 /*
33 * Create a new browser window
34 */
35 void a_Commands_new_callback(GtkWidget *widget, gpointer client_data)
36 {
37 BrowserWindow *nbw, *bw = (BrowserWindow *)client_data;
38
39 nbw = a_Interface_browser_window_new(bw->main_window->allocation.width,
40 bw->main_window->allocation.height, 0);
41 /* focus the location bar */
42 gtk_widget_grab_focus(nbw->location);
43 }
44
45 /*
46 * Create and show the "Open file" dialog
47 */
48 void a_Commands_openfile_callback(GtkWidget *widget, gpointer client_data)
49 {
50 BrowserWindow *bw = (BrowserWindow *) client_data;
51
52 a_Interface_openfile_dialog(bw);
53 }
54
55 /*
56 * Create and show the "Open Url" dialog window
57 */
58 void a_Commands_openurl_callback(GtkWidget *widget, gpointer client_data)
59 {
60 BrowserWindow *bw = (BrowserWindow *) client_data;
61 a_Interface_open_dialog(widget, bw);
62 }
63
64 /*
65 * ?
66 */
67 void a_Commands_prefs_callback(GtkWidget *widget, gpointer client_data)
68 {
69 }
70
71 /*
72 * Close browser window, and exit dillo if it's the last one.
73 */
74 void a_Commands_close_callback(GtkWidget *widget, gpointer client_data)
75 {
76 BrowserWindow *bw = (BrowserWindow *)client_data;
77 gtk_widget_destroy(bw->main_window);
78 }
79
80 /*
81 * Free memory and quit dillo
82 */
83 void a_Commands_exit_callback(GtkWidget *widget, gpointer client_data)
84 {
85 a_Interface_quit_all();
86 }
87
88
89 /* PAGE MENU */
90
91 /*
92 * Show current page's source code.
93 */
94 void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data)
95 {
96 BrowserWindow *bw = (BrowserWindow *)client_data;
97 gchar *buf;
98 gint buf_size;
99
100 /* Get page source data */
101 a_Capi_get_buf(a_History_get_url(NAV_TOP(bw)), &buf, &buf_size);
102 /* Show it */
103 a_Interface_text_window (&bw->viewsource_window,
104 "View Source", "view_source",
105 buf, buf_size,
106 530, 500);
107 }
108
109 /*
110 * Show the detected HTML errors in current page.
111 */
112 void a_Commands_view_page_bugs_callback (GtkWidget *widget,
113 gpointer client_data)
114 {
115 DilloHtmlLB *html_lb = client_data;
116
117 a_Interface_text_window (&html_lb->bw->pagebugs_window,
118 "Detected HTML errors", "page_bugs",
119 html_lb->page_bugs->str,
120 html_lb->page_bugs->len,
121 530, 500);
122 }
123
124 /*
125 * ?
126 */
127 void a_Commands_selectall_callback(GtkWidget *widget, gpointer client_data)
128 {
129 }
130
131 /*
132 * Create and show the "Find Text" dialog window
133 */
134 void a_Commands_findtext_callback(GtkWidget *widget, gpointer client_data)
135 {
136 BrowserWindow *bw = (BrowserWindow *) client_data;
137
138 a_Interface_findtext_dialog( bw );
139 }
140
141 /*
142 * Print the page!
143 * ('cat page.html | html2ps | lpr -Pcool' Why bother? I think providing
144 * such an option in a configurable way should cut it --Jcid)
145 */
146 void a_Commands_print_callback(GtkWidget *widget, gpointer client_data)
147 {
148 }
149
150
151 /* TOOLBAR MENU */
152
153 /*
154 * Abort all active connections for this page
155 * (Downloads MUST keep flowing)
156 */
157 void a_Commands_stop_callback(GtkWidget *widget, gpointer client_data)
158 {
159 BrowserWindow *bw = client_data;
160 a_Nav_cancel_expect(bw);
161 a_Interface_stop(bw);
162 a_Interface_set_button_sens(bw);
163 a_Interface_msg(bw, "Stopped");
164 }
165
166 /*
167 * Back to previous page
168 */
169 void a_Commands_back_callback(GtkWidget *widget, gpointer client_data)
170 {
171 BrowserWindow *bw = (BrowserWindow *) client_data;
172
173 a_Nav_back(bw);
174 }
175
176 /*
177 * Creates the menu for the forward navigation button.
178 */
179 GtkMenu *a_Commands_forw_button_menu_creator_callback(GtkExtButton *button,
180 gpointer client_data)
181 {
182 BrowserWindow *bw = client_data;
183
184 if (bw->menu_popup.over_forw)
185 gtk_widget_destroy(bw->menu_popup.over_forw);
186 bw->menu_popup.over_forw = a_Menu_popup_history_new(bw, +1);
187 return GTK_MENU(bw->menu_popup.over_forw);
188 }
189
190 /*
191 * Creates the menu for the backward navigation button.
192 */
193 GtkMenu *a_Commands_back_button_menu_creator_callback(GtkExtButton *button,
194 gpointer client_data)
195 {
196 BrowserWindow *bw = client_data;
197
198 if (bw->menu_popup.over_back)
199 gtk_widget_destroy(bw->menu_popup.over_back);
200 bw->menu_popup.over_back = a_Menu_popup_history_new(bw, -1);
201 return GTK_MENU(bw->menu_popup.over_back);
202 }
203
204 /*
205 * Handler for menu entries in the history menus. This one is called, when
206 * the link should be opened in the same window.
207 */
208 void a_Commands_history_callback_same_bw(GtkWidget *widget,
209 gpointer client_data)
210 {
211 BrowserWindow *bw = client_data;
212
213 a_Nav_jump_callback(widget, bw, 0);
214 }
215
216 /*
217 * Handler for menu entries in the history menus. This one is called, when
218 * the link should be opened in a new window.
219 */
220 void a_Commands_history_callback_new_bw(GtkWidget *widget,
221 gpointer client_data)
222 {
223 BrowserWindow *bw = client_data;
224
225 a_Nav_jump_callback(widget, bw, 1);
226 }
227
228 /*
229 * Go to the next page in the history buffer
230 */
231 void a_Commands_forw_callback(GtkWidget *widget, gpointer client_data)
232 {
233 BrowserWindow *bw = (BrowserWindow *) client_data;
234
235 a_Nav_forw(bw);
236 }
237
238 /*
239 * Start the reload process
240 */
241 void a_Commands_reload_callback(GtkWidget *widget, gpointer client_data)
242 {
243 BrowserWindow *bw = (BrowserWindow *) client_data;
244
245 a_Nav_reload(bw);
246 }
247
248 /*
249 * Go home!
250 */
251 void a_Commands_home_callback(GtkWidget *widget, gpointer client_data)
252 {
253 BrowserWindow *bw = (BrowserWindow *) client_data;
254
255 a_Nav_home(bw);
256 }
257
258 /*
259 * Bring up the save page dialog
260 */
261 void a_Commands_save_callback(GtkWidget *widget, gpointer client_data)
262 {
263 BrowserWindow *bw = (BrowserWindow *) client_data;
264
265 a_Interface_save_dialog(widget, bw);
266 }
267
268 /*
269 * Bring up the save link dialog
270 */
271 void a_Commands_save_link_callback(GtkWidget *widget, gpointer client_data)
272 {
273 BrowserWindow *bw = (BrowserWindow *) client_data;
274
275 a_Interface_save_link_dialog(widget, bw);
276 }
277
278
279 /* BOOKMARKS MENU */
280
281 /*
282 * Add a bookmark to the current bookmark widget.
283 */
284 void a_Commands_addbm_callback(GtkWidget *widget, gpointer client_data)
285 {
286 a_Bookmarks_add(widget, client_data);
287 }
288
289 /*
290 * Show the bookmarks-file as rendered html
291 */
292 void a_Commands_viewbm_callback(GtkWidget *widget, gpointer client_data)
293 {
294 BrowserWindow *bw = (BrowserWindow *) client_data;
295
296 a_Bookmarks_show(bw);
297 }
298
299
300 /* HELP MENU */
301
302 /*
303 * This one was intended as a link to help-info on the web site, but
304 * currently points to the home page --Jcid
305 */
306 void a_Commands_helphome_callback(GtkWidget *widget, gpointer client_data)
307 {
308 BrowserWindow *bw = (BrowserWindow *) client_data;
309 DilloUrl *url = a_Url_new(DILLO_HOME, NULL, 0, 0, 0);
310
311 a_Nav_push(bw, url);
312 a_Url_free(url);
313 }
314
315
316 /* RIGHT BUTTON POP-UP MENU */
317
318 /*
319 * Open link in browser-window
320 */
321 void a_Commands_open_link_callback(GtkWidget *widget, gpointer client_data)
322 {
323 BrowserWindow *bw = (BrowserWindow *)client_data;
324
325 a_Nav_push(bw, a_Menu_popup_get_url(bw));
326 }
327
328 /*
329 * Open link in another browser-window
330 */
331 void a_Commands_open_link_nw_callback(GtkWidget *widget, gpointer client_data)
332 {
333 BrowserWindow *bw = (BrowserWindow *)client_data;
334 gint width, height;
335 BrowserWindow *newbw;
336
337 gdk_window_get_size (bw->main_window->window, &width, &height);
338 newbw = a_Interface_browser_window_new(width, height, 0);
339 a_Nav_push(newbw, a_Menu_popup_get_url(bw));
340 }
341
342 /*
343 * Called when the user wants the popup's URL for pasting.
344 */
345 void a_Commands_select_popup_url_callback(GtkWidget *widget, gpointer data)
346 {
347 BrowserWindow *bw = (BrowserWindow *)data;
348 a_Selection_set_selection(widget, URL_STR(a_Menu_popup_get_url(bw)));
349 }
350
351
352 /* BUG METER POP-UP MENU */
353
354 /*
355 * Helper function for validation.
356 */
357 static void Commands_ob_validate_page(BrowserWindow *bw, const gchar *fmt)
358 {
359 GString *gstr = g_string_sized_new(128);
360 gchar *o_url, *e_url, *n_url;
361 DilloUrl *url;
362
363 /* prepare the validation request URI */
364 o_url = URL_STR(a_History_get_url(NAV_TOP(bw)));
365 e_url = a_Misc_escape_chars(o_url, ":/?");
366 n_url = e_url ? e_url : o_url;
367 g_string_sprintf(gstr, fmt, n_url);
368 if (n_url != o_url)
369 g_free(n_url);
370 url = a_Url_new(gstr->str, NULL, 0, 0, 0);
371 g_string_free(gstr, TRUE);
372
373 a_Nav_push(bw, url);
374 a_Url_free(url);
375 }
376
377 /*
378 * Validate current page with the W3C's validator.
379 */
380 void a_Commands_ob_w3c_callback(GtkWidget *widget, gpointer client_data)
381 {
382 BrowserWindow *bw = (BrowserWindow *)client_data;
383 const gchar *w3c_fmt="http://validator.w3.org/check?uri=%s";
384
385 Commands_ob_validate_page(bw, w3c_fmt);
386 }
387
388 /*
389 * Validate current page with the WDG's validator.
390 */
391 void a_Commands_ob_wdg_callback(GtkWidget *widget, gpointer client_data)
392 {
393 BrowserWindow *bw = (BrowserWindow *)client_data;
394 const gchar *wdg_fmt=
395 "http://www.htmlhelp.org/cgi-bin/validate.cgi?url=%s&warnings=yes";
396
397 Commands_ob_validate_page(bw, wdg_fmt);
398 }
399
400 /*
401 * Request the info page for bug meter.
402 */
403 void a_Commands_ob_info_callback(GtkWidget *widget, gpointer client_data)
404 {
405 BrowserWindow *bw = (BrowserWindow *)client_data;
406 const gchar *info_str = "http://www.dillo.org/help/bug_meter.html";
407 DilloUrl *url = a_Url_new(info_str, NULL, 0, 0, 0);
408
409 a_Nav_push(bw, url);
410 a_Url_free(url);
411 }
412
0 #ifndef __COMMANDS_H__
1 #define __COMMANDS_H__
2
3 #include "gtk_ext_button.h"
4
5 void a_Commands_new_callback(GtkWidget * widget, gpointer client_data);
6 void a_Commands_openfile_callback (GtkWidget *widget, gpointer client_data);
7 void a_Commands_openurl_callback (GtkWidget *widget, gpointer client_data);
8 void a_Commands_prefs_callback(GtkWidget * widget, gpointer client_data);
9 void a_Commands_close_callback(GtkWidget * widget, gpointer client_data);
10 void a_Commands_exit_callback (GtkWidget *widget, gpointer client_data);
11
12 void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data);
13 void a_Commands_view_page_bugs_callback (GtkWidget *button,
14 gpointer client_data);
15 void a_Commands_selectall_callback (GtkWidget *widget, gpointer client_data);
16 void a_Commands_findtext_callback (GtkWidget *widget, gpointer client_data);
17 void a_Commands_print_callback (GtkWidget *widget, gpointer client_data);
18
19 GtkMenu *a_Commands_forw_button_menu_creator_callback(GtkExtButton *button,
20 gpointer client_data);
21 GtkMenu *a_Commands_back_button_menu_creator_callback(GtkExtButton *button,
22 gpointer client_data);
23 void a_Commands_history_callback_same_bw(GtkWidget *widget,
24 gpointer client_data);
25 void a_Commands_history_callback_new_bw(GtkWidget *widget,
26 gpointer client_data);
27
28 void a_Commands_back_callback (GtkWidget *widget, gpointer client_data);
29 void a_Commands_forw_callback (GtkWidget *widget, gpointer client_data);
30 void a_Commands_reload_callback (GtkWidget *widget, gpointer client_data);
31 void a_Commands_stop_callback (GtkWidget *widget, gpointer client_data);
32 void a_Commands_home_callback (GtkWidget *widget, gpointer client_data);
33 void a_Commands_save_callback (GtkWidget *widget, gpointer client_data);
34 void a_Commands_save_link_callback (GtkWidget *widget, gpointer client_data);
35
36 void a_Commands_addbm_callback (GtkWidget *widget, gpointer client_data);
37 void a_Commands_viewbm_callback (GtkWidget *widget, gpointer client_data);
38
39 void a_Commands_helphome_callback (GtkWidget *widget, gpointer client_data);
40
41 void a_Commands_open_link_callback(GtkWidget *widget, gpointer client_data);
42 void a_Commands_open_link_nw_callback(GtkWidget *widget, gpointer client_data);
43 void a_Commands_select_popup_url_callback(GtkWidget *widget, gpointer data);
44
45 void a_Commands_ob_w3c_callback(GtkWidget *widget, gpointer client_data);
46 void a_Commands_ob_wdg_callback(GtkWidget *widget, gpointer client_data);
47 void a_Commands_ob_info_callback(GtkWidget *widget, gpointer client_data);
48
49 #endif /* __COMMANDS_H__ */
0 /*
1 * File: cookies.c
2 *
3 * Copyright 2001 Lars Clausen <lrclause@cs.uiuc.edu>
4 * Jörgen Viksell <jorgen.viksell@telia.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /* Handling of cookies takes place here.
13 * This implementation aims to follow RFC 2965:
14 * http://www.cis.ohio-state.edu/cs/Services/rfc/rfc-text/rfc2965.txt
15 */
16
17 #define DEBUG_LEVEL 10
18 #include "debug.h"
19
20
21 #ifdef DISABLE_COOKIES
22
23 /*
24 * Initialize the cookies module
25 */
26 void a_Cookies_init(void)
27 {
28 DEBUG_MSG(10, "Cookies: absolutely disabled at compilation time.\n");
29 }
30
31 #else
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/file.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <time.h> /* for time() and time_t */
41 #include <ctype.h>
42
43 #include "msg.h"
44 #include "IO/Url.h"
45 #include "misc.h"
46 #include "list.h"
47 #include "cookies.h"
48 #include "capi.h"
49 #include "dpiapi.h"
50 #include "../dpip/dpip.h"
51
52
53 /* The maximum length of a line in the cookie file */
54 #define LINE_MAXLEN 4096
55
56 typedef enum {
57 COOKIE_ACCEPT,
58 COOKIE_ACCEPT_SESSION,
59 COOKIE_DENY
60 } CookieControlAction;
61
62 typedef struct {
63 CookieControlAction action;
64 char *domain;
65 } CookieControl;
66
67 /* Variables for access control */
68 static CookieControl *ccontrol = NULL;
69 static int num_ccontrol = 0;
70 static int num_ccontrol_max = 1;
71 static CookieControlAction default_action = COOKIE_DENY;
72
73 static gboolean disabled;
74
75 static FILE *Cookies_fopen(const char *file, gchar *init_str);
76 static CookieControlAction Cookies_control_check(const DilloUrl *url);
77 static CookieControlAction Cookies_control_check_domain(const char *domain);
78 static int Cookie_control_init(void);
79
80 /*
81 * Return a file pointer. If the file doesn't exist, try to create it,
82 * with the optional 'init_str' as its content.
83 */
84 static FILE *Cookies_fopen(const char *filename, gchar *init_str)
85 {
86 FILE *F_in;
87 int fd;
88
89 if ((F_in = fopen(filename, "r")) == NULL) {
90 /* Create the file */
91 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
92 if (fd != -1) {
93 if (init_str)
94 write(fd, init_str, strlen(init_str));
95 close(fd);
96
97 DEBUG_MSG(10, "Cookies: Created file: %s\n", filename);
98 F_in = Cookies_fopen(filename, NULL);
99 } else {
100 DEBUG_MSG(10, "Cookies: Could not create file: %s!\n", filename);
101 }
102 }
103
104 /* set close on exec */
105 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
106
107 return F_in;
108 }
109
110 /*
111 * Initialize the cookies module
112 * (The 'disabled' variable is writable only within a_Cookies_init)
113 */
114 void a_Cookies_init(void)
115 {
116 /* Default setting */
117 disabled = TRUE;
118
119 /* Read and parse the cookie control file (cookiesrc) */
120 if (Cookie_control_init() != 0) {
121 DEBUG_MSG(10, "Disabling cookies.\n");
122 return;
123 }
124
125 DEBUG_MSG(10, "Enabling cookies as from cookiesrc...\n");
126 disabled = FALSE;
127 }
128
129 /*
130 * Flush cookies to disk and free all the memory allocated.
131 */
132 void a_Cookies_freeall()
133 {
134 }
135
136 /*
137 * Set the value corresponding to the cookie string
138 */
139 void a_Cookies_set(GList *cookie_strings, const DilloUrl *set_url)
140 {
141 CookieControlAction action;
142 char *cmd, *path, numstr[16];
143
144 if (disabled)
145 return;
146
147 action = Cookies_control_check(set_url);
148 if (action == COOKIE_DENY) {
149 DEBUG_MSG(5, "Cookies: denied SET for %s\n", URL_HOST_(set_url));
150 return;
151 }
152
153 while (cookie_strings != NULL ) {
154 char *cookie_string = cookie_strings->data;
155 path = (char *) URL_PATH_(set_url);
156
157 g_snprintf(numstr, 16, "%d", URL_PORT(set_url));
158 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s port=%s",
159 "set_cookie", cookie_string, URL_HOST_(set_url),
160 path ? path : "/", numstr);
161
162 DEBUG_MSG(5, "Cookies.c: a_Cookies_set \n\t \"%s\" \n",cmd );
163 a_Capi_dpi_send_cmd(NULL, NULL, cmd, "cookies", 1);
164 g_free(cmd);
165
166 cookie_strings = g_list_next(cookie_strings);
167 }
168
169 }
170
171 /*
172 * Return a string that contains all relevant cookies as headers.
173 */
174 char *a_Cookies_get(const DilloUrl *request_url)
175 {
176 char *cmd, *path, *dpip_tag, *cookie, numstr[16];
177 CookieControlAction action;
178
179 cookie = g_strdup("");
180
181 if (disabled)
182 return cookie;
183
184 action = Cookies_control_check(request_url);
185 if (action == COOKIE_DENY) {
186 DEBUG_MSG(5, "Cookies: denied GET for %s\n", URL_HOST_(request_url));
187 return cookie;
188 }
189 path = (char *) URL_PATH_(request_url);
190
191 g_snprintf(numstr, 16, "%d", URL_PORT(request_url));
192 cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s port=%s",
193 "get_cookie", URL_SCHEME(request_url),
194 URL_HOST(request_url), path ? path : "/", numstr);
195
196 /* Get the answer from cookies.dpi */
197 dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
198 g_free(cmd);
199
200 if (dpip_tag != NULL) {
201 cookie = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cookie");
202 g_free(dpip_tag);
203 }
204 return cookie;
205 }
206
207 /* -------------------------------------------------------------
208 * Access control routines
209 * ------------------------------------------------------------- */
210
211
212 /*
213 * Get the cookie control rules (from cookiesrc).
214 * Return value:
215 * 0 = Parsed OK, with cookies enabled
216 * 1 = Parsed OK, with cookies disabled
217 * 2 = Can't open the control file
218 */
219 static int Cookie_control_init(void)
220 {
221 CookieControl cc;
222 FILE *stream;
223 char *filename;
224 char line[LINE_MAXLEN];
225 char domain[LINE_MAXLEN];
226 char rule[LINE_MAXLEN];
227 int i, j;
228 gboolean enabled = FALSE;
229
230 /* Get a file pointer */
231 filename = a_Misc_prepend_user_home(".dillo/cookiesrc");
232 stream = Cookies_fopen(filename, "DEFAULT DENY\n");
233 g_free(filename);
234
235 if (!stream)
236 return 2;
237
238 /* Get all lines in the file */
239 while (!feof(stream)) {
240 line[0] = '\0';
241 fgets(line, LINE_MAXLEN, stream);
242
243 /* Remove leading and trailing whitespaces */
244 g_strstrip(line);
245
246 if (line[0] != '\0' && line[0] != '#') {
247 i = 0;
248 j = 0;
249
250 /* Get the domain */
251 while (!isspace(line[i]))
252 domain[j++] = line[i++];
253 domain[j] = '\0';
254
255 /* Skip past whitespaces */
256 i++;
257 while (isspace(line[i]))
258 i++;
259
260 /* Get the rule */
261 j = 0;
262 while (line[i] != '\0' && !isspace(line[i]))
263 rule[j++] = line[i++];
264 rule[j] = '\0';
265
266 if (g_strcasecmp(rule, "ACCEPT") == 0)
267 cc.action = COOKIE_ACCEPT;
268 else if (g_strcasecmp(rule, "ACCEPT_SESSION") == 0)
269 cc.action = COOKIE_ACCEPT_SESSION;
270 else if (g_strcasecmp(rule, "DENY") == 0)
271 cc.action = COOKIE_DENY;
272 else {
273 MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n",
274 rule, domain);
275 continue;
276 }
277
278 cc.domain = g_strdup(domain);
279 if (g_strcasecmp(cc.domain, "DEFAULT") == 0) {
280 /* Set the default action */
281 default_action = cc.action;
282 g_free(cc.domain);
283 } else {
284 a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
285 ccontrol[num_ccontrol++] = cc;
286 }
287
288 if (cc.action != COOKIE_DENY)
289 enabled = TRUE;
290 }
291 }
292
293 fclose(stream);
294
295 return (enabled ? 0 : 1);
296 }
297
298 /*
299 * Check the rules for an appropriate action for this domain
300 */
301 static CookieControlAction Cookies_control_check_domain(const char *domain)
302 {
303 int i, diff;
304
305 for (i = 0; i < num_ccontrol; i++) {
306 if (ccontrol[i].domain[0] == '.') {
307 diff = strlen(domain) - strlen(ccontrol[i].domain);
308 if (diff >= 0) {
309 if (g_strcasecmp(domain + diff, ccontrol[i].domain) != 0)
310 continue;
311 } else {
312 continue;
313 }
314 } else {
315 if (g_strcasecmp(domain, ccontrol[i].domain) != 0)
316 continue;
317 }
318
319 /* If we got here we have a match */
320 return( ccontrol[i].action );
321 }
322
323 return default_action;
324 }
325
326 /*
327 * Same as the above except it takes an URL
328 */
329 static CookieControlAction Cookies_control_check(const DilloUrl *url)
330 {
331 return Cookies_control_check_domain(URL_HOST(url));
332 }
333
334 #endif /* !DISABLE_COOKIES */
0 #ifndef __COOKIES_H__
1 #define __COOKIES_H__
2
3 #ifdef DISABLE_COOKIES
4 # define a_Cookies_get(url) g_strdup("")
5 # define a_Cookies_freeall() ;
6 void a_Cookies_init( void );
7 #else
8 char *a_Cookies_get(const DilloUrl *request_url);
9 void a_Cookies_set(GList *cookie_string, const DilloUrl *set_url);
10 void a_Cookies_init( void );
11 void a_Cookies_freeall( void );
12 #endif
13
14 #endif /* !__COOKIES_H__ */
0 #ifndef __DEBUG_H__
1 #define __DEBUG_H__
2
3 /*
4 * Simple debug messages. Add:
5 *
6 * #define DEBUG_LEVEL <n>
7 * #include "debug.h"
8 *
9 * to the file you are working on, or let DEBUG_LEVEL undefined to
10 * disable all messages. A higher level denotes a greater importance
11 * of the message.
12 */
13
14 #include <glib.h>
15
16
17 # ifdef DEBUG_LEVEL
18 # define DEBUG_MSG(level, fmt...) \
19 G_STMT_START { \
20 if (DEBUG_LEVEL && (level) >= DEBUG_LEVEL) \
21 g_print(fmt); \
22 } G_STMT_END
23 # else
24 # define DEBUG_MSG(level, fmt...)
25 # endif /* DEBUG_LEVEL */
26
27
28
29 /*
30 * Following is experimental, and will be explained soon.
31 */
32
33 #ifdef DBG_RTFL
34
35 #include <unistd.h>
36 #include <stdio.h>
37
38 #define DBG_MSG(obj, aspect, prio, msg) \
39 G_STMT_START { \
40 printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:%s\n", \
41 __FILE__, __LINE__, getpid(), obj, aspect, prio, msg); \
42 fflush (stdout); \
43 } G_STMT_END
44
45 #define DBG_MSGF(obj, aspect, prio, fmt, args...) \
46 G_STMT_START { \
47 printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:" fmt "\n", \
48 __FILE__, __LINE__, getpid(), obj, aspect, prio, args); \
49 fflush (stdout); \
50 } G_STMT_END
51
52 #define DBG_MSG_START(obj) \
53 G_STMT_START { \
54 printf ("[rtfl]%s:%d:%d:msg-start:%p\n", \
55 __FILE__, __LINE__, getpid(), obj); \
56 fflush (stdout); \
57 } G_STMT_END
58
59 #define DBG_MSG_END(obj) \
60 G_STMT_START { \
61 printf ("[rtfl]%s:%d:%d:msg-end:%p\n", \
62 __FILE__, __LINE__, getpid(), obj); \
63 fflush (stdout); \
64 } G_STMT_END
65
66 #define DBG_OBJ_CREATE(obj, klass) \
67 G_STMT_START { \
68 printf ("[rtfl]%s:%d:%d:obj-create:%p:%s\n", \
69 __FILE__, __LINE__, getpid(), obj, klass); \
70 fflush (stdout); \
71 } G_STMT_END
72
73 #define DBG_OBJ_ASSOC(child, parent) \
74 G_STMT_START { \
75 printf ("[rtfl]%s:%d:%d:obj-assoc:%p:%p\n", \
76 __FILE__, __LINE__, getpid(), child, parent); \
77 fflush (stdout); \
78 } G_STMT_END
79
80 #define DBG_OBJ_SET_NUM(obj, var, val) \
81 G_STMT_START { \
82 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%d\n", \
83 __FILE__, __LINE__, getpid(), obj, var, val); \
84 fflush (stdout); \
85 } G_STMT_END
86
87 #define DBG_OBJ_SET_STR(obj, var, val) \
88 G_STMT_START { \
89 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%s\n", \
90 __FILE__, __LINE__, getpid(), obj, var, val); \
91 fflush (stdout); \
92 } G_STMT_END
93
94 #define DBG_OBJ_SET_PTR(obj, var, val) \
95 G_STMT_START { \
96 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%p\n", \
97 __FILE__, __LINE__, getpid(), obj, var, val); \
98 fflush (stdout); \
99 } G_STMT_END
100
101 #define DBG_OBJ_ARRSET_NUM(obj, var, ind, val) \
102 G_STMT_START { \
103 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%d\n", \
104 __FILE__, __LINE__, getpid(), obj, ind, val); \
105 fflush (stdout); \
106 } G_STMT_END
107
108 #define DBG_OBJ_ARRSET_STR(obj, var, ind, val) \
109 G_STMT_START { \
110 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%s\n", \
111 __FILE__, __LINE__, getpid(), obj, ind, val); \
112 fflush (stdout); \
113 } G_STMT_END
114
115 #define DBG_OBJ_ARRSET_PTR(obj, var, ind, val) \
116 G_STMT_START { \
117 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%p\n", \
118 __FILE__, __LINE__, getpid(), obj, ind, val); \
119 fflush (stdout); \
120 } G_STMT_END
121
122 #define DBG_OBJ_COLOR(klass, color) \
123 G_STMT_START { \
124 printf ("[rtfl]%s:%d:%d:obj-color:%s:%s\n", \
125 __FILE__, __LINE__, getpid(), klass, color); \
126 fflush (stdout); \
127 } G_STMT_END
128
129 #else /* DBG_RTFL */
130
131 #define DBG_MSG(obj, aspect, prio, msg)
132 #define DBG_MSGF(obj, aspect, prio, fmt, arg...)
133 #define DBG_MSG_START(obj)
134 #define DBG_MSG_END(obj)
135 #define DBG_OBJ_CREATE(obj, klass)
136 #define DBG_OBJ_ASSOC(child, parent)
137 #define DBG_OBJ_SET_NUM(obj, var, val)
138 #define DBG_OBJ_SET_STR(obj, var, val)
139 #define DBG_OBJ_SET_PTR(obj, var, val)
140 #define DBG_OBJ_ARRSET_NUM(obj, var, ind, val)
141 #define DBG_OBJ_ARRSET_STR(obj, var, ind, val)
142 #define DBG_OBJ_ARRSET_PTR(obj, var, ind, val)
143 #define DBG_OBJ_COLOR(klass, color)
144
145 #endif /* DBG_RTFL */
146
147 #endif /* __DEBUG_H__ */
148
149
0 /*
1 * File: dicache.c
2 *
3 * Copyright 2000 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <glib.h> /* GHashTable functions */
12 #include <gtk/gtk.h>
13 #include <sys/time.h> /* for libc5 compatibility */
14 #include <string.h> /* for memset */
15 #include <stdio.h>
16 #include <stdlib.h>
17
18 #include "image.h"
19 #include "web.h"
20 #include "dicache.h"
21 #include "cache.h"
22 #include "interface.h"
23 #include "prefs.h"
24
25 typedef struct _DICacheHashEntry DICacheHashEntry;
26
27 struct _DICacheHashEntry {
28 gint valid;
29 DilloUrl *url; /* "Key" of this hash entry */
30 DICacheEntry* next; /* pointer to the first dicache entry in this list */
31 };
32
33 static GHashTable *dicache_hash;
34
35 static gint dicache_size_total; /* invariant: dicache_size_total is
36 * the sum of the image sizes of all
37 * cache lines in dicache. */
38
39 /*
40 * Determine if two hash entries are equal (used by GHashTable)
41 */
42 static gint Dicache_hash_entry_equal(gconstpointer v1, gconstpointer v2)
43 {
44 return (!a_Url_cmp((DilloUrl *)v1, (DilloUrl *)v2));
45 }
46
47 /*
48 * Determine the hash value given the key (used by GHashTable)
49 */
50 static guint Dicache_url_hash(gconstpointer key)
51 {
52 return g_str_hash(URL_STR(((DilloUrl *)key)));
53 }
54
55 /*
56 * Initialize dicache data
57 */
58 void a_Dicache_init(void)
59 {
60 dicache_hash = g_hash_table_new(Dicache_url_hash, Dicache_hash_entry_equal);
61 dicache_size_total = 0;
62 }
63
64 /*
65 * Create, and initialize a new, empty, dicache entry
66 */
67 static DICacheEntry *Dicache_entry_new(void)
68 {
69 DICacheEntry *entry = g_new(DICacheEntry, 1);
70
71 entry->width = 0;
72 entry->height = 0;
73 entry->type = DILLO_IMG_TYPE_NOTSET;
74 entry->cmap = NULL;
75 entry->ImageBuffer = NULL;
76 entry->RefCount = 1;
77 entry->TotalSize = 0;
78 entry->Y = 0;
79 entry->BitVec = NULL;
80 entry->State = DIC_Empty;
81 entry->version = 0;
82 entry->next = NULL;
83
84 return entry;
85 }
86
87 /*
88 * Add a new entry to the hash
89 */
90 DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url)
91 {
92 DICacheEntry *entry;
93 DICacheHashEntry *hash_entry;
94
95 entry = Dicache_entry_new();
96
97 if ( (hash_entry = g_hash_table_lookup(dicache_hash, Url))) {
98 /* this URL is already in the hash, add entry at the END of the list */
99 DICacheEntry *ptr = hash_entry->next;
100
101 hash_entry->valid = 1;
102 for ( ; ptr->next; ptr = ptr->next);
103 ptr->next = entry;
104 entry->version = ptr->version+1;
105 entry->url = hash_entry->url;
106
107 } else { /* no hash entry yet, so create one */
108 DICacheHashEntry *hash_entry = g_new(DICacheHashEntry, 1);
109
110 hash_entry->url = a_Url_dup(Url);
111 entry->url = hash_entry->url;
112 hash_entry->next = entry;
113 hash_entry->valid = 1;
114 g_hash_table_insert(dicache_hash, hash_entry->url, hash_entry);
115 }
116
117 return entry;
118 }
119
120 /*
121 * Search an entry in the dicache (given the Url).
122 * Return value: a pointer to the entry of the _newest_ (i.e. highest)
123 * version if found; NULL otherwise.
124 */
125 DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url)
126 {
127 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
128 DICacheEntry *entry;
129
130 if (!hash_entry || !hash_entry->valid)
131 return NULL;
132
133 for (entry = hash_entry->next; (entry && entry->next); entry = entry->next);
134
135 return entry;
136 }
137
138 /*
139 * Search a particular version of an URL in the Dicache.
140 * Return value: a pointer to the entry if found; NULL otherwise.
141 */
142 static DICacheEntry *Dicache_get_entry_version(const DilloUrl *Url,
143 gint version)
144 {
145 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
146 DICacheEntry *entry = hash_entry ? hash_entry->next : NULL;
147
148 while (entry && entry->version != version)
149 entry = entry->next;
150
151 return entry;
152 }
153
154 /*
155 * Actually free a dicache entry, given the URL and the version number.
156 */
157 static void Dicache_remove(const DilloUrl *Url, gint version)
158 {
159 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
160 DICacheEntry *entry = hash_entry ? hash_entry->next : NULL, *prev = entry;
161
162 while (entry && (entry->version != version) ) {
163 prev = entry;
164 entry = entry->next;
165 }
166
167 if ( entry ) {
168 /* Eliminate this dicache entry */
169 g_free(entry->cmap);
170 if (entry->ImageBuffer) {
171 free(entry->ImageBuffer);
172 dicache_size_total -= entry->TotalSize;
173 }
174 a_Bitvec_free(entry->BitVec);
175
176 if (hash_entry->next == entry) {
177 if (!entry->next) {
178 /* last entry with this URL. Remove the hash entry as well */
179 g_hash_table_remove(dicache_hash, hash_entry->url);
180 a_Url_free(hash_entry->url);
181 g_free(hash_entry);
182 } else
183 hash_entry->next = entry->next;
184 } else
185 prev->next = entry->next;
186 g_free(entry);
187 }
188 }
189
190 /*
191 * Unrefs the counter of a dicache entry, and _if_ no DwImage is acessing
192 * this buffer _and_ there is a higher version of this image, then we call
193 * Dicache_free to do the dirty job.
194 */
195 void a_Dicache_unref(const DilloUrl *Url, gint version)
196 {
197 DICacheEntry *entry;
198
199 if ( (entry = Dicache_get_entry_version(Url, version)) )
200 if (--entry->RefCount == 0 && (entry->next || !prefs.use_dicache) )
201 Dicache_remove(Url, version);
202 }
203
204 /*
205 * Refs the counter of a dicache entry.
206 */
207
208 DICacheEntry* a_Dicache_ref(const DilloUrl *Url, gint version)
209 {
210 DICacheEntry *entry;
211
212 if ( (entry = Dicache_get_entry_version(Url, version)) )
213 ++entry->RefCount;
214
215 return entry;
216 }
217
218 /*
219 * Invalidate this entry. This is used for the reloading mechanism.
220 * Can't erase current versions, but a_Dicache_get_entry must return NULL.
221 */
222 void a_Dicache_invalidate_entry(const DilloUrl *Url)
223 {
224 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
225
226 if (hash_entry) hash_entry->valid = 0;
227 }
228
229
230 /* ------------------------------------------------------------------------- */
231
232 /*
233 * This function is a cache client; (but feeds its clients from dicache)
234 */
235 void a_Dicache_callback(int Op, CacheClient_t *Client)
236 {
237 /* todo: Handle Op = CA_Abort (to show what was got) --Jcid */
238 guint i;
239 DilloWeb *Web = Client->Web;
240 DilloImage *Image = Web->Image;
241 DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url);
242
243 g_return_if_fail ( DicEntry != NULL );
244
245 if ( Op == CA_Send ) {
246 if ( Image->height == 0 && DicEntry->State >= DIC_SetParms ) {
247 /* Set parms */
248 a_Image_set_parms(
249 Image, DicEntry->ImageBuffer, DicEntry->url,
250 DicEntry->version, DicEntry->width, DicEntry->height,
251 DicEntry->type);
252 }
253 if (DicEntry->State == DIC_Write) {
254 for (i = 0; i < DicEntry->height; ++i)
255 if (a_Bitvec_get_bit(DicEntry->BitVec, (gint)i) &&
256 !a_Bitvec_get_bit(Image->BitVec, (gint)i) )
257 a_Image_write(
258 Image, DicEntry->ImageBuffer + i*DicEntry->width*3, i,FALSE);
259
260 }
261 } else if ( Op == CA_Close || Op == CA_Abort ) {
262 a_Image_close(Web->Image);
263 a_Interface_close_client(Web->bw, Client->Key);
264 }
265 }
266
267 /* ------------------------------------------------------------------------- */
268
269 /*
270 * Set image's width, height & type
271 * (By now, we'll use the image information despite the html tags --Jcid)
272 */
273 void a_Dicache_set_parms(DilloUrl *url, gint version, DilloImage *Image,
274 guint width, guint height, DilloImgType type)
275 {
276 DICacheEntry *DicEntry;
277 size_t Size = width * height * 3;
278
279 g_return_if_fail ( Image != NULL && width && height );
280 /* Find the DicEntry for this Image */
281 DicEntry = Dicache_get_entry_version(url, version);
282 g_return_if_fail ( DicEntry != NULL );
283
284 /* Initialize the DicEntry */
285 /* using malloc() instead of g_new() avoids crashes on oversized images */
286 DicEntry->ImageBuffer = malloc(sizeof(guchar) * Size);
287 g_return_if_fail ( DicEntry->ImageBuffer != NULL );
288 DicEntry->TotalSize = Size;
289 DicEntry->width = width;
290 DicEntry->height = height;
291 DicEntry->type = type;
292 DicEntry->BitVec = a_Bitvec_new((gint)height);
293 DicEntry->State = DIC_SetParms;
294
295 dicache_size_total += Size;
296
297 /* For giggles, make the background of the undrawn parts interesting */
298 memset(DicEntry->ImageBuffer, 0xdd, DicEntry->TotalSize);
299
300 /* Allocate and initialize this image */
301 a_Image_set_parms(Image, DicEntry->ImageBuffer, url, version,
302 width, height, type);
303 }
304
305 /*
306 * Implement the set_cmap method for the Image
307 */
308 void a_Dicache_set_cmap(DilloUrl *url, gint version, DilloImage *Image,
309 const guchar *cmap, guint num_colors,
310 gint num_colors_max, gint bg_index)
311 {
312 DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
313
314 g_return_if_fail ( DicEntry != NULL );
315
316 g_free(DicEntry->cmap);
317 DicEntry->cmap = g_new0(guchar, 3 * num_colors_max);
318 memcpy(DicEntry->cmap, cmap, 3 * num_colors);
319 if (bg_index >= 0 && (guint)bg_index < num_colors) {
320 DicEntry->cmap[bg_index * 3] = (Image->bg_color >> 16) & 0xff;
321 DicEntry->cmap[bg_index * 3 + 1] = (Image->bg_color >> 8) & 0xff;
322 DicEntry->cmap[bg_index * 3 + 2] = (Image->bg_color) & 0xff;
323 }
324
325 a_Image_set_cmap(Image, DicEntry->cmap);
326 DicEntry->State = DIC_SetCmap;
327 }
328
329 /*
330 * Implement the write method
331 * (Write a scan line into the Dicache entry)
332 * buf: row buffer
333 * Y : row number
334 * x : horizontal offset? (always zero)
335 */
336 void a_Dicache_write(DilloImage *Image, DilloUrl *url, gint version,
337 const guchar *buf, gint x, guint Y)
338 {
339 DICacheEntry *DicEntry;
340
341 g_return_if_fail ( Image != NULL );
342 DicEntry = Dicache_get_entry_version(url, version);
343 g_return_if_fail ( DicEntry != NULL );
344 g_return_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 );
345
346 a_Image_write(Image, buf, Y, TRUE);
347 DicEntry->Y = Y;
348 a_Bitvec_set_bit(DicEntry->BitVec, (gint)Y);
349 DicEntry->State = DIC_Write;
350 }
351
352 /*
353 * Implement the close method of the decoding process
354 */
355 void a_Dicache_close(DilloUrl *url, gint version, CacheClient_t *Client)
356 {
357 DilloWeb *Web = Client->Web;
358 DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
359
360 g_return_if_fail ( DicEntry != NULL );
361
362 DicEntry->State = DIC_Close;
363 if (DicEntry->cmap) {
364 g_free(DicEntry->cmap);
365 DicEntry->cmap = NULL;
366 }
367 a_Image_close(Web->Image);
368 a_Interface_close_client(Web->bw, Client->Key);
369 }
370
371 /* ------------------------------------------------------------------------- */
372
373 static gboolean
374 Dicache_remove_hash_entry(gpointer key, gpointer value, gpointer user_data)
375 {
376 DICacheHashEntry *hash_entry = (DICacheHashEntry *)value;
377 DICacheEntry *entry = hash_entry->next;
378
379 /* Eliminate this (last) dicache entry */
380 g_free(entry->cmap);
381 if (entry->ImageBuffer) {
382 free(entry->ImageBuffer);
383 dicache_size_total -= entry->TotalSize;
384 }
385 a_Bitvec_free(entry->BitVec);
386 a_Url_free(hash_entry->url);
387 g_free(hash_entry);
388
389 return TRUE;
390 }
391
392 /*
393 * Deallocate memory used by dicache module
394 * (Call this one at exit time)
395 */
396 void a_Dicache_freeall(void)
397 {
398 /* Remove every dicache entry */
399 g_hash_table_foreach_remove(dicache_hash,
400 (GHRFunc)Dicache_remove_hash_entry, NULL);
401 /* Remove the dicache hash */
402 g_hash_table_destroy(dicache_hash);
403 }
0 #ifndef __DICACHE_H__
1 #define __DICACHE_H__
2
3 #include "bitvec.h"
4 #include "image.h"
5 #include "cache.h"
6
7 /* These will reflect the entry's "state" */
8 typedef enum {
9 DIC_Empty, /* Just created the entry */
10 DIC_SetParms, /* Parameters set */
11 DIC_SetCmap, /* Color map set */
12 DIC_Write, /* Feeding the entry */
13 DIC_Close, /* Whole image got! */
14 DIC_Abort /* Image transfer aborted */
15 } DicEntryState;
16
17 typedef struct _DICacheEntry DICacheEntry;
18
19 struct _DICacheEntry {
20 DilloUrl *url; /* Image URL for this entry */
21 guint width, height; /* As taken from image data */
22 DilloImgType type; /* Image type */
23 guchar *cmap; /* Color map */
24 guchar *ImageBuffer; /* Decompressed buffer */
25 size_t TotalSize; /* Amount of memory the image takes up */
26 gint Y; /* Current decoding row */
27 bitvec_t *BitVec; /* Bit vector for decoded rows */
28 DicEntryState State; /* Current status for this entry */
29 gint RefCount; /* Reference Counter */
30 gint version; /* Version number, used for different
31 versions of the same URL image */
32
33 DICacheEntry *next; /* Link to the next "newer" version */
34 };
35
36
37 void a_Dicache_init (void);
38
39 DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url);
40 DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url);
41
42 void a_Dicache_callback(gint Op, CacheClient_t *Client);
43
44 void a_Dicache_set_parms(DilloUrl *url, gint version, DilloImage *Image,
45 guint width, guint height, DilloImgType type);
46 void a_Dicache_set_cmap(DilloUrl *url, gint version, DilloImage *Image,
47 const guchar *cmap, guint num_colors,
48 gint num_colors_max, gint bg_index);
49 void a_Dicache_write(DilloImage *Image, DilloUrl *url, gint version,
50 const guchar *buf, gint x, guint Y);
51 void a_Dicache_close(DilloUrl *url, gint version, CacheClient_t *Client);
52
53 void a_Dicache_invalidate_entry(const DilloUrl *Url);
54 DICacheEntry* a_Dicache_ref(const DilloUrl *Url, gint version);
55 void a_Dicache_unref(const DilloUrl *Url, gint version);
56 void a_Dicache_freeall(void);
57
58 #endif /* __DICACHE_H__ */
0 /*
1 * Dillo web browser
2 *
3 * Copyright 1997 Raph Levien <raph@acm.org>
4 * Copyright 1999-2004 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <stdio.h>
22 #include <gtk/gtk.h>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <signal.h>
32 #include <sys/wait.h>
33 #include <ctype.h>
34 #include <locale.h>
35
36 #include <config.h>
37 #include "msg.h"
38 #include "dillo.h"
39 #include "misc.h"
40 #include "nav.h"
41 #include "history.h"
42 #include "bookmark.h"
43 #include "dicache.h"
44 #include "dns.h"
45 #include "IO/mime.h"
46 #include "IO/Url.h"
47 #include "prefs.h"
48 #include "interface.h"
49 #include "dw.h"
50 #include "cookies.h"
51
52
53 /*
54 * Command line options structure
55 */
56 typedef struct {
57 char *sh_opt; /* must be non NULL */
58 char *lg_opt; /* must be non NULL */
59 gint opt_argc; /* positive: mandatory, negative: optional */
60 gint opt_flg; /* 1 << n */
61 char *help; /* can be NULL */
62 } CLI_options;
63
64 enum {
65 DILLO_CLI_XID = 1 << 1,
66 DILLO_CLI_FULLWINDOW = 1 << 2,
67 DILLO_CLI_HELP = 1 << 3,
68 DILLO_CLI_VERSION = 1 << 4,
69 DILLO_CLI_LOCAL = 1 << 5,
70 DILLO_CLI_GEOMETRY = 1 << 6,
71 DILLO_CLI_DBG_RENDERING = 1 << 7,
72 DILLO_CLI_ERROR = 1 << 15
73 };
74
75 enum {
76 O_SEARCH, O_FOUND, O_NOTFOUND, O_OK, O_ERROR, O_DONE
77 };
78
79 /*
80 * Global variables from command line options;
81 */
82 gboolean dillo_dbg_rendering = FALSE;
83
84 /*
85 * Forward declarations
86 */
87 static void Dillo_check_home_dir(char *dir);
88 static gint Dillo_get_opt(CLI_options *options, int argc,
89 char **argv, char ***opt_arg, gint *idx);
90 static void Dillo_print_help(CLI_options *options);
91 static void Dillo_print_version(void);
92
93 /*
94 * Local data
95 */
96 static CLI_options Options[] = {
97 {"-x", "--xid", 1, DILLO_CLI_XID,
98 " -x, --xid XID Open first Dillo window in an existing\n"
99 " GtkSocket which window ID is XID (decimal)."},
100 {"-v", "--version", 0, DILLO_CLI_VERSION,
101 " -v, --version Display version info and exit."},
102 {"-h", "--help", 0, DILLO_CLI_HELP,
103 " -h, --help Display this help text and exit."},
104 {"-f", "--fullwindow", 0, DILLO_CLI_FULLWINDOW,
105 " -f, --fullwindow Start in full window mode: hide address bar,\n"
106 " navigation buttons, menu, and status bar."},
107 {"-l", "--local", 0, DILLO_CLI_LOCAL,
108 " -l, --local Don't follow links for this URL(s)."},
109 {"-g", "-geometry", 1, DILLO_CLI_GEOMETRY,
110 " -g, -geometry GEO Set initial window position where GEO is\n"
111 " <width>x<height>{+-}<x>{+-}<y>"},
112 {"-D", "--debug-rendering",
113 0, DILLO_CLI_DBG_RENDERING,
114 " -D, --debug-rendering Draw additionaly several lines in a web page,\n"
115 " representing its structure. For debugging. "},
116 {NULL, NULL, 0, 0, NULL}
117 };
118
119 /*
120 * Return the maximum number of option arguments
121 */
122 static gint Dillo_get_max_opt(CLI_options *options)
123 {
124 gint i, max;
125
126 for (i = 0, max = 0; options[i].sh_opt; i++)
127 if (abs(options[i].opt_argc) > max)
128 max = abs(options[i].opt_argc);
129 return max;
130 }
131
132 /*
133 * Given a command line argument, build a DilloUrl for it.
134 */
135 static DilloUrl *Dillo_make_start_url(gchar *str)
136 {
137 gchar *url_str, *p, *cd;
138 DilloUrl *start_url;
139 gint is_file = FALSE;
140
141 /* This check is for files with non-URL characters */
142 if (access(str, F_OK) == 0) {
143 p = a_Misc_escape_chars(str, "% ");
144 url_str = g_strdup(p ? p : str);
145 is_file = TRUE;
146 } else {
147 /* Filter URL string */
148 url_str = a_Url_string_strip_delimiters(str);
149 }
150
151 if (is_file || access(url_str, F_OK) == 0) {
152 GString *UrlStr = g_string_sized_new(128);
153
154 if (url_str[0] == '/') {
155 /* prepend the "file:" protocol specifier. */
156 g_string_sprintf(UrlStr, "file:%s", url_str);
157 } else {
158 cd = g_get_current_dir();
159 p = a_Misc_escape_chars(cd, "% ");
160 g_string_sprintf(UrlStr, "file:%s", p ? p : cd);
161 g_free(p);
162 g_free(cd);
163 if (UrlStr->str[UrlStr->len - 1] != '/')
164 g_string_append(UrlStr, "/");
165 g_string_append(UrlStr, url_str);
166 }
167 start_url = a_Url_new(UrlStr->str, NULL, 0, 0, 0);
168 g_string_free(UrlStr, TRUE);
169 } else {
170 start_url = a_Url_new(url_str, NULL, 0, 0, 0);
171 }
172
173 g_free(url_str);
174 return start_url;
175 }
176
177 /*
178 * Get status code to avoid zombies!
179 */
180 static void Dillo_sigchld_handler(int signum)
181 {
182 pid_t pid;
183 int status, serrno;
184
185 serrno = errno;
186 while (1) {
187 pid = waitpid (-1, &status, WNOHANG);
188 if (pid < 0) {
189 perror ("waitpid");
190 break;
191 }
192 if (pid == 0)
193 break;
194 /* notice_termination (pid, status); */
195 }
196 errno = serrno;
197 }
198
199
200 /*
201 * ******************************** MAIN *************************************
202 */
203 gint main(int argc, char *argv[])
204 {
205 gchar *dir, *curr_locale;
206 DilloUrl *start_url;
207 BrowserWindow *bw;
208 guint32 xid = 0;
209 guint options_got = 0;
210 gint idx = 0;
211 gint opt_flg;
212 gint i;
213 char **opt_argv = NULL;
214 gint xpos = D_GEOMETRY_DEFAULT_XPOS, ypos = D_GEOMETRY_DEFAULT_YPOS;
215 gint width = D_GEOMETRY_DEFAULT_WIDTH, height = D_GEOMETRY_DEFAULT_HEIGHT;
216
217 /* set locale */
218 curr_locale = g_strdup(setlocale(LC_ALL, NULL));
219 gtk_set_locale();
220 /* Initialize GUI and parse GTK related args */
221 gtk_init(&argc, &argv);
222 gdk_rgb_init();
223 gtk_widget_set_default_colormap (gdk_rgb_get_cmap());
224 gtk_widget_set_default_visual (gdk_rgb_get_visual());
225
226 /* Handle command line options */
227 while ((opt_flg = Dillo_get_opt(Options, argc, argv, &opt_argv, &idx))) {
228 options_got |= opt_flg;
229 switch (opt_flg) {
230 case DILLO_CLI_VERSION:
231 Dillo_print_version();
232 return 0;
233 break;
234 case DILLO_CLI_HELP:
235 Dillo_print_help(Options);
236 return 0;
237 break;
238 case DILLO_CLI_XID:
239 if (opt_argv[0][0] >= '0' && opt_argv[0][0] <= '9') {
240 xid = strtol(opt_argv[0], NULL, 10);
241 } else {
242 g_printerr("Error: the XID must be an unsigned decimal numerical "
243 "value.\nThe offending value was: %s\n", opt_argv[0]);
244 return -1;
245 }
246 break;
247 case DILLO_CLI_FULLWINDOW:
248 case DILLO_CLI_LOCAL:
249 break;
250 case DILLO_CLI_DBG_RENDERING:
251 dillo_dbg_rendering = TRUE;
252 break;
253 case DILLO_CLI_GEOMETRY:
254 if (a_Misc_parse_geometry(opt_argv[0], &xpos, &ypos, &width, &height))
255 break;
256 default:
257 g_printerr("Error in command line options.\n");
258 return -1;
259 break;
260 }
261 }
262
263 /* Send a delayed locale-related message */
264 MSG("Setting locale to %s...\n", curr_locale);
265 g_free(curr_locale);
266
267 /* This lets threads in the file module end peacefully when aborted
268 * todo: implement a cleaner mechanism (in file.c) */
269 signal(SIGPIPE, SIG_IGN);
270
271 /* This avoids making zombies when dpi-programs finish. */
272 signal(SIGCHLD, Dillo_sigchld_handler);
273
274
275 /* check that ~/.dillo exists, create it if it doesn't */
276 dir = a_Misc_prepend_user_home(".dillo");
277 Dillo_check_home_dir(dir);
278 g_free(dir);
279
280 a_Prefs_init();
281 a_Dns_init();
282 a_Http_init();
283 a_Mime_init();
284 a_Cache_init();
285 a_Dicache_init();
286 a_Interface_init();
287 a_Dw_init();
288 a_Cookies_init();
289
290 /* -f overrides dillorc */
291 if (options_got & DILLO_CLI_FULLWINDOW)
292 prefs.fullwindow_start = TRUE;
293
294 /* override dillorc geometry */
295 if (options_got & DILLO_CLI_GEOMETRY) {
296 prefs.width = width;
297 prefs.height = height;
298 prefs.xpos = xpos;
299 prefs.ypos = ypos;
300 }
301 /* a_Nav_init() has been moved into this call because it needs to be
302 * initialized with the new browser_window structure */
303 bw = a_Interface_browser_window_new(prefs.width, prefs.height, xid);
304 if ((prefs.xpos != D_GEOMETRY_DEFAULT_XPOS) ||
305 (prefs.ypos != D_GEOMETRY_DEFAULT_YPOS))
306 gtk_widget_set_uposition(bw->main_window, prefs.xpos, prefs.ypos);
307
308 a_Bookmarks_init();
309
310 /* Send dillo's startup screen */
311 a_Nav_push(bw, prefs.start_page);
312
313 for (i = idx; i < argc; i++) {
314 /* If more than one URL/FILE, open in new window */
315 if (i > idx)
316 bw = a_Interface_browser_window_new(prefs.width, prefs.height, 0);
317
318 start_url = Dillo_make_start_url(argv[i]);
319 if (options_got & DILLO_CLI_LOCAL)
320 a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
321 a_Nav_push(bw, start_url);
322 a_Url_free(start_url);
323 }
324
325 if (prefs.http_proxyuser && !a_Http_proxy_auth())
326 a_Interface_proxy_passwd_dialog(bw);
327
328 /* Start the GTK+ cycle */
329 gtk_main();
330
331 /*
332 * Memory deallocating routines
333 * (This can be left to the OS, but we'll do it, with a view to test
334 * and fix our memory management)
335 */
336 a_Cookies_freeall();
337 a_Cache_freeall();
338 a_Dicache_freeall();
339 a_Http_freeall();
340 a_Dns_freeall();
341 a_Prefs_freeall();
342 a_Dw_freeall();
343 a_History_free();
344
345 /* a_Dpi_bye_dpid(); */
346 MSG("Dillo: normal exit!\n");
347 return 0;
348 }
349
350 /*
351 * Check if '~/.dillo' directory exists.
352 * If not, try to create it.
353 */
354 static void Dillo_check_home_dir(char *dir)
355 {
356 struct stat st;
357
358 if ( stat(dir, &st) == -1 ) {
359 if ( errno == ENOENT && mkdir(dir, 0700) < 0 ) {
360 MSG("Dillo: error creating directory %s: %s\n",
361 dir, g_strerror(errno));
362 } else
363 MSG("Dillo: error reading %s: %s\n", dir, g_strerror(errno));
364 }
365 }
366
367
368 /*
369 * Get next command line option.
370 * Return value:
371 * opt_flg of the option
372 * 0 if no more options found
373 * -1 if an unrecognised option is found or if a mandatory option
374 * argument is not found.
375 */
376 static gint
377 Dillo_get_opt(CLI_options *options, int argc,
378 char **argv, char ***opt_argv, gint *cli_idx)
379 {
380 gint i = 0, opt_flg = 0, n_arg, state;
381 static int idx = 1;
382 gint ret_val = 0;
383
384 /* Allocate opt_argv */
385 if (*opt_argv == NULL)
386 *opt_argv = g_new0(gchar*, Dillo_get_max_opt(options) + 1);
387
388 state = O_SEARCH;
389 if (idx >= argc)
390 state = O_DONE;
391
392 while (1) {
393 switch (state) {
394 case O_SEARCH:
395 for (i = 0; options[i].sh_opt; i++)
396 if (strcmp(options[i].sh_opt, argv[idx]) == 0 ||
397 strcmp(options[i].lg_opt, argv[idx]) == 0)
398 break;
399 state = (options[i].sh_opt) ? O_FOUND : O_NOTFOUND;
400 break;
401 case O_FOUND:
402 ++idx;
403 opt_flg = options[i].opt_flg;
404 n_arg = options[i].opt_argc;
405 /* Find the required/optional arguments of the option */
406 for (i = 0; idx < argc && i < abs(n_arg) && argv[idx][0] != '-'; i++)
407 (*opt_argv)[i] = argv[idx++];
408 (*opt_argv)[i] = NULL;
409
410 /* Optional arguments have opt_argc < 0 */
411 if (i < n_arg) {
412 g_printerr("Option %s requires %d argument(s)\n",
413 argv[idx - i - 1], n_arg);
414 state = O_ERROR;
415 } else
416 state = O_OK;
417 break;
418 case O_NOTFOUND:
419 state = O_DONE;
420 if (strcmp(argv[idx], "--") == 0)
421 idx++;
422 else if (argv[idx][0] == '-')
423 state = O_ERROR;
424 break;
425 case O_OK:
426 *cli_idx = idx;
427 return opt_flg;
428 break;
429 case O_ERROR:
430 ret_val = DILLO_CLI_ERROR;
431 state = O_DONE;
432 break;
433 case O_DONE:
434 g_free(*opt_argv);
435 *cli_idx = idx;
436 return ret_val;
437 break;
438 }
439 }
440 }
441
442 /*
443 * Print a short help text automatically generated from the options structure
444 */
445 static void Dillo_print_help(CLI_options *options)
446 {
447 g_print("\nUsage: dillo [OPTIONS] [URL|FILE]...\n"
448 "Options:\n");
449
450 for (; options && options->sh_opt; options++) {
451 if (options->help)
452 g_print("%s\n", options->help);
453 else
454 g_print(" %s, %s *Undocumented*\n", options->sh_opt,
455 options->lg_opt);
456 }
457 g_print(" URL URL to browse.\n"
458 " FILE Local FILE to view.\n");
459 g_print("\n");
460 }
461
462 /*
463 * Print version (TODO: and maybe a copyright notice)
464 */
465 static void Dillo_print_version(void)
466 {
467 g_print("Dillo %s\n", VERSION);
468 }
0 #ifndef __DILLO_H__
1 #define __DILLO_H__
2
3 #include "browser.h"
4 #include "web.h"
5
6 extern gboolean dillo_dbg_rendering;
7
8 #endif /* __DILLO_H__ */
0 /*
1 * File: dns.c
2 *
3 * Copyright (C) 1999, 2000, 2001 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Non blocking pthread-handled Dns scheme
13 */
14
15 #include <pthread.h>
16 #include <glib.h>
17
18 #include <netdb.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <string.h>
28
29 #include "msg.h"
30 #include "dns.h"
31 #include "list.h"
32
33 #define DEBUG_LEVEL 5
34 #include "debug.h"
35
36
37 /*
38 * Uncomment the following line for debugging or gprof profiling.
39 */
40 /* #undef D_DNS_THREADED */
41
42 /*
43 * Uncomment the following line for libc5 optimization
44 */
45 /* #define LIBC5 */
46
47
48 /* Maximum dns resolving threads */
49 #ifdef D_DNS_THREADED
50 # define D_DNS_MAX_SERVERS 4
51 #else
52 # define D_DNS_MAX_SERVERS 1
53 #endif
54
55
56 typedef struct {
57 gint channel; /* Index of this channel [0 based] */
58 gboolean in_use; /* boolean to tell if server is doing a lookup */
59 gboolean ip_ready; /* boolean: is IP lookup done? */
60 GSList *addr_list; /* IP address */
61 char *hostname; /* Adress to resolve */
62 guint timeout_id; /* gtk timeout function ID */
63 #ifdef D_DNS_THREADED
64 pthread_t th1; /* Thread id */
65 #endif
66 } DnsServer;
67
68 typedef struct {
69 char *hostname; /* host name for cache */
70 GSList *addr_list; /* address of host */
71 } GDnsCache;
72
73 typedef struct {
74 gint channel; /* -2 if waiting, otherwise index to dns_server[] */
75 char *hostname; /* The one we're resolving */
76 ChainLink *Info; /* CCC info */
77 } GDnsQueue;
78
79
80 /*
81 * Forward declarations
82 */
83 static gboolean Dns_timeout_client(gpointer data);
84
85 /*
86 * Local Data
87 */
88 static DnsServer dns_server[D_DNS_MAX_SERVERS];
89 static gint num_servers;
90 static GDnsCache *dns_cache;
91 static gint dns_cache_size, dns_cache_size_max;
92 static GDnsQueue *dns_queue;
93 static gint dns_queue_size, dns_queue_size_max;
94 static gboolean ipv6_enabled;
95
96
97 /* ----------------------------------------------------------------------
98 * Dns queue functions
99 */
100 static void Dns_queue_add(ChainLink *Info, gint channel, const char *hostname)
101 {
102 a_List_add(dns_queue, dns_queue_size, dns_queue_size_max);
103 dns_queue[dns_queue_size].Info = Info;
104 dns_queue[dns_queue_size].channel = channel;
105 dns_queue[dns_queue_size].hostname = g_strdup(hostname);
106 dns_queue_size++;
107 }
108
109 /*
110 * Find hostname index in dns_queue
111 * (if found, returns queue index; -1 if not)
112 */
113 static gint Dns_queue_find(const char *hostname)
114 {
115 gint i;
116
117 for (i = 0; i < dns_queue_size; i++)
118 if (!strcmp(hostname, dns_queue[i].hostname))
119 return i;
120
121 return -1;
122 }
123
124 /*
125 * Given an index, remove an entry from the dns_queue
126 */
127 static void Dns_queue_remove(int index)
128 {
129 gint i;
130
131 DEBUG_MSG(2, "Dns_queue_remove: deleting client [%d] [queue_size=%d]\n",
132 index, dns_queue_size);
133
134 if (index < dns_queue_size) {
135 g_free(dns_queue[index].hostname);
136 --dns_queue_size; /* you'll find out why ;-) */
137 for (i = index; i < dns_queue_size; i++)
138 dns_queue[i] = dns_queue[i + 1];
139 }
140 }
141
142 /*
143 * Debug function
144 *
145 void Dns_queue_print()
146 {
147 gint i;
148
149 MSG("Queue: [");
150 for (i = 0; i < dns_queue_size; i++)
151 MSG("%d:%s ", dns_queue[i].channel, dns_queue[i].hostname);
152 MSG("]\n");
153 }
154 */
155
156 /*
157 * Given a CCC Info, remove its client
158 */
159 static void Dns_abort_by_ccc(ChainLink *Info)
160 {
161 gint i;
162
163 for (i = 0; i < dns_queue_size; i++)
164 if (dns_queue[i].Info == Info) {
165 Dns_queue_remove(i);
166 break;
167 }
168 }
169
170 /*
171 * Add an IP/hostname pair to Dns-cache
172 */
173 static void Dns_cache_add(char *hostname, GSList *addr_list)
174 {
175 a_List_add(dns_cache, dns_cache_size, dns_cache_size_max);
176 dns_cache[dns_cache_size].hostname = g_strdup(hostname);
177 dns_cache[dns_cache_size].addr_list = addr_list;
178 ++dns_cache_size;
179 DEBUG_MSG(1, "Cache objects: %d\n", dns_cache_size);
180 }
181
182
183 /*
184 * Initializer function
185 */
186 void a_Dns_init(void)
187 {
188 gint i;
189
190 #ifdef D_DNS_THREADED
191 DEBUG_MSG(5, "dillo_dns_init: Here we go! (threaded)\n");
192 #else
193 DEBUG_MSG(5, "dillo_dns_init: Here we go! (not threaded)\n");
194 #endif
195
196 dns_queue_size = 0;
197 dns_queue_size_max = 16;
198 dns_queue = g_new(GDnsQueue, dns_queue_size_max);
199
200 dns_cache_size = 0;
201 dns_cache_size_max = 16;
202 dns_cache = g_new(GDnsCache, dns_cache_size_max);
203
204 num_servers = D_DNS_MAX_SERVERS;
205
206 /* Initialize servers data */
207 for (i = 0; i < num_servers; ++i) {
208 dns_server[i].channel = i;
209 dns_server[i].in_use = FALSE;
210 dns_server[i].ip_ready = FALSE;
211 dns_server[i].addr_list = NULL;
212 dns_server[i].hostname = NULL;
213 dns_server[i].timeout_id = 0;
214 #ifdef D_DNS_THREADED
215 dns_server[i].th1 = (pthread_t) -1;
216 #endif
217 }
218
219 /* IPv6 test */
220 ipv6_enabled = FALSE;
221 #ifdef ENABLE_IPV6
222 {
223 /* If the IPv6 address family is not available there is no point
224 wasting time trying to connect to v6 addresses. */
225 int fd = socket(AF_INET6, SOCK_STREAM, 0);
226 if (fd >= 0) {
227 close(fd);
228 ipv6_enabled = TRUE;
229 }
230 }
231 #endif
232 }
233
234 /*
235 * Allocate a host structure and add it to the list
236 */
237 static GSList *Dns_note_hosts(GSList *list, int af, struct hostent *host)
238 {
239 int i;
240
241 if (host->h_length > DILLO_ADDR_MAX)
242 return list;
243 for (i = 0; host->h_addr_list[i]; i++) {
244 DilloHost *dh = g_new0(DilloHost, 1);
245 dh->af = af;
246 dh->alen = host->h_length;
247 memcpy(&dh->data[0], host->h_addr_list[i], (size_t)host->h_length);
248 list = g_slist_append(list, dh);
249 }
250 return list;
251 }
252
253 #ifdef D_DNS_THREADED
254 /*
255 * Server function (runs on its own thread)
256 */
257 static void *Dns_server(void *data)
258 {
259 struct hostent *host;
260 gint channel = GPOINTER_TO_INT(data);
261 #ifdef LIBC5
262 gint h_err;
263 char buff[1024];
264 struct hostent sh;
265 #endif
266 GSList *hosts = NULL;
267
268 DEBUG_MSG(3, "Dns_server: starting...\n ch: %d host: %s\n",
269 channel, dns_server[channel].hostname);
270
271 #ifdef ENABLE_IPV6
272 if (ipv6_enabled) {
273 host = gethostbyname2(dns_server[channel].hostname, AF_INET6);
274 if (host)
275 hosts = Dns_note_hosts(hosts, AF_INET6, host);
276 }
277 #endif
278
279 #ifdef LIBC5
280 host = gethostbyname_r(dns_server[channel].hostname, &sh, buff,
281 sizeof(buff), &h_err);
282 #else
283 host = gethostbyname(dns_server[channel].hostname);
284 #endif
285
286 if (host)
287 hosts = Dns_note_hosts(hosts, AF_INET, host);
288
289 /* write hostname to client */
290 DEBUG_MSG(5, "Dns_server [%d]: %s is %p\n", channel,
291 dns_server[channel].hostname, hosts);
292 dns_server[channel].addr_list = hosts;
293 dns_server[channel].ip_ready = TRUE;
294
295 return NULL; /* (avoids a compiler warning) */
296 }
297 #endif
298
299 #ifndef D_DNS_THREADED
300 /*
301 * Blocking server-function (it doesn't use threads)
302 */
303 static void Dns_blocking_server(void)
304 {
305 gint channel = 0;
306 struct hostent *host = NULL;
307 GSList *hosts = NULL;
308
309 DEBUG_MSG(3, "Dns_blocking_server: starting...\n");
310 DEBUG_MSG(3, "Dns_blocking_server: dns_server[%d].hostname = %s\n",
311 channel, dns_server[channel].hostname);
312
313 #ifdef ENABLE_IPV6
314 if (ipv6_enabled) {
315 host = gethostbyname2(dns_server[channel].hostname, AF_INET6);
316 if (host)
317 hosts = Dns_note_hosts(hosts, AF_INET6, host);
318 }
319 #endif
320
321 if (!host) {
322 host = gethostbyname(dns_server[channel].hostname);
323 if (host == NULL) {
324 DEBUG_MSG(3, "--> Dns_blocking_server: gethostbyname NULL return\n");
325 } else {
326 DEBUG_MSG(3, "--> Dns_blocking_server - good return\n");
327 hosts = Dns_note_hosts(hosts, AF_INET, host);
328 }
329 }
330
331 /* write IP to server data channel */
332 DEBUG_MSG(3, "Dns_blocking_server: IP of %s is %p\n",
333 dns_server[channel].hostname, hosts);
334 dns_server[channel].addr_list = hosts;
335 dns_server[channel].ip_ready = TRUE;
336
337 DEBUG_MSG(3, "Dns_blocking_server: leaving...\n");
338 }
339 #endif
340
341 /*
342 * Request function (spawn a server and let it handle the request)
343 */
344 static void Dns_server_req(gint channel, const char *hostname)
345 {
346 #ifdef D_DNS_THREADED
347 static pthread_attr_t thrATTR;
348 static gint thrATTRInitialized = 0;
349 #endif
350
351 dns_server[channel].in_use = TRUE;
352 dns_server[channel].ip_ready = FALSE;
353
354 g_free(dns_server[channel].hostname);
355 dns_server[channel].hostname = g_strdup(hostname);
356
357 /* Let's set a timeout client to poll the server channel (5 times/sec) */
358 dns_server[channel].timeout_id =
359 gtk_timeout_add(200, (GtkFunction)Dns_timeout_client,
360 GINT_TO_POINTER(dns_server[channel].channel));
361
362 #ifdef D_DNS_THREADED
363 /* set the thread attribute to the detached state */
364 if (!thrATTRInitialized) {
365 pthread_attr_init(&thrATTR);
366 pthread_attr_setdetachstate(&thrATTR, PTHREAD_CREATE_DETACHED);
367 thrATTRInitialized = 1;
368 }
369 /* Spawn thread */
370 pthread_create(&dns_server[channel].th1, &thrATTR, Dns_server,
371 GINT_TO_POINTER(dns_server[channel].channel));
372 #else
373 Dns_blocking_server();
374 #endif
375 }
376
377 /*
378 * Return the IP for the given hostname.
379 * Side effect: a thread can be spawned later.
380 */
381 static void Dns_lookup(ChainLink *Info, const char *hostname)
382 {
383 gint i, channel;
384
385 g_return_if_fail (hostname != NULL);
386
387 /* check for cache hit. */
388 for (i = 0; i < dns_cache_size; i++)
389 if (!strcmp(hostname, dns_cache[i].hostname))
390 break;
391
392 /* if it hits already resolved, call the Callback inmediately. */
393 if (i < dns_cache_size) {
394 a_Dns_ccc(OpSend, 1, FWD, Info, dns_cache[i].addr_list, NULL);
395 a_Dns_ccc(OpEnd, 1, FWD, Info, NULL, NULL);
396 return;
397 }
398 if ((i = Dns_queue_find(hostname)) != -1) {
399 /* hit in queue, but answer hasn't come back yet. */
400 Dns_queue_add(Info, dns_queue[i].channel, hostname);
401 } else {
402 /* Never requested before -- we must solve it! */
403
404 /* Find a channel we can send the request to */
405 for (channel = 0; channel < num_servers; channel++)
406 if (!dns_server[channel].in_use)
407 break;
408 if (channel < num_servers) {
409 /* Found a free channel! */
410 Dns_queue_add(Info, channel, hostname);
411 Dns_server_req(channel, hostname);
412 } else {
413 /* We'll have to wait for a thread to finish... */
414 Dns_queue_add(Info, -2, hostname);
415 }
416 }
417 }
418
419 /*
420 * Give answer to all queued callbacks on this channel
421 */
422 static void Dns_serve_channel(gint channel)
423 {
424 gint i;
425 ChainLink *Info;
426 DnsServer *srv = &dns_server[channel];
427
428 for (i = 0; i < dns_queue_size; i++) {
429 if (dns_queue[i].channel == channel) {
430 Info = dns_queue[i].Info;
431 if ( srv->addr_list == NULL ) {
432 a_Dns_ccc(OpAbort, 1, FWD, Info, NULL, NULL);
433 } else {
434 a_Dns_ccc(OpSend, 1, FWD, Info, srv->addr_list, NULL);
435 a_Dns_ccc(OpEnd, 1, FWD, Info, NULL, NULL);
436 }
437 Dns_queue_remove(i);
438 --i;
439 }
440 }
441 /* set current channel free */
442 srv->in_use = FALSE;
443 }
444
445 /*
446 * Assign free channels to waiting clients (-2)
447 */
448 static void Dns_assign_channels(void)
449 {
450 gint ch, i, j;
451
452 for (ch = 0; ch < num_servers; ++ch) {
453 if (dns_server[ch].in_use == FALSE) {
454 /* Find the next query in the queue (we're a FIFO) */
455 for (i = 0; i < dns_queue_size; i++)
456 if (dns_queue[i].channel == -2)
457 break;
458
459 if (i < dns_queue_size) {
460 /* assign this channel to every queued request
461 * with the same hostname*/
462 for (j = i; j < dns_queue_size; j++)
463 if (dns_queue[j].channel == -2 &&
464 !strcmp(dns_queue[j].hostname, dns_queue[i].hostname))
465 dns_queue[j].channel = ch;
466 Dns_server_req(ch, dns_queue[i].hostname);
467 } else
468 return;
469 }
470 }
471 }
472
473 /*
474 * This is a Gtk+ timeout function that
475 * reads the DNS results and resumes the stopped jobs.
476 */
477 static gboolean Dns_timeout_client(gpointer data)
478 {
479 gint channel = GPOINTER_TO_INT(data);
480 DnsServer *srv = &dns_server[channel];
481
482 if ( srv->ip_ready ){
483 if (srv->addr_list != NULL) {
484 /* DNS succeeded, let's cache it */
485 Dns_cache_add(srv->hostname, srv->addr_list);
486 }
487 Dns_serve_channel(channel);
488 Dns_assign_channels();
489 srv->timeout_id = 0;
490 return FALSE; /* Done! */
491 }
492
493 /* IP not already solved, keep on trying... */
494 return TRUE;
495 }
496
497 /*
498 * CCC function for the DNS module
499 * ( Data1 = hostname | IP; Data2 = {Not used here} )
500 */
501 void a_Dns_ccc(int Op, int Branch, int Dir, ChainLink *Info,
502 void *Data1, void *Data2)
503 {
504 gchar *Hostname;
505
506 if ( Branch == 1 ){
507 if (Dir == BCK) {
508 /* Solve hostname */
509 switch (Op) {
510 case OpStart:
511 Hostname = g_strdup(Data1 ? (gchar *)Data1 : "");
512 Info->LocalKey = Hostname;
513 Dns_lookup(Info, Hostname);
514 break;
515 case OpAbort:
516 Dns_abort_by_ccc(Info);
517 g_free(Info->LocalKey);
518 g_free(Info);
519 break;
520 }
521 } else { /* FWD */
522 /* Solve hostname answer */
523 switch (Op) {
524 case OpSend:
525 a_Chain_fcb(OpSend, Info, Data1, NULL);
526 break;
527 case OpEnd:
528 Hostname = Info->LocalKey;
529 a_Chain_fcb(OpEnd, Info, NULL, NULL);
530 g_free(Hostname);
531 break;
532 case OpAbort:
533 Hostname = Info->LocalKey;
534 a_Chain_fcb(OpAbort, Info, NULL, NULL);
535 g_free(Hostname);
536 break;
537 }
538 }
539 }
540 }
541
542 /*
543 * Dns memory-deallocation
544 * (Call this one at exit time)
545 * The Dns_queue is deallocated at execution time (no need to do that here)
546 * 'dns_cache' is the only one that grows dinamically
547 */
548 void a_Dns_freeall(void)
549 {
550 gint i;
551
552 for ( i = 0; i < dns_cache_size; ++i ){
553 g_free(dns_cache[i].hostname);
554 }
555 g_free(dns_cache);
556 }
557
0 #ifndef __DNS_H__
1 #define __DNS_H__
2
3 #include <gtk/gtk.h>
4 #include "chain.h"
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10 void a_Dns_init (void);
11 void a_Dns_freeall(void);
12 void a_Dns_ccc(int Op, int Dir, int Br, ChainLink *Info,
13 void *Data1, void *Data2);
14
15 #define DILLO_ADDR_MAX 16
16
17 typedef struct _DilloHost
18 {
19 int af;
20 int alen;
21 char data[DILLO_ADDR_MAX];
22 } DilloHost;
23
24 #ifdef __cplusplus
25 }
26 #endif /* __cplusplus */
27
28 #endif /* __DNS_H__ */
0 /*
1 * File: dpiapi.c
2 *
3 * Copyright (C) 2004 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /* Support for dpi/dpip from Dillo's side */
12
13 #include "msg.h"
14 #include "browser.h"
15 #include "capi.h"
16 #include "interface.h"
17 #include "dpiapi.h" /* for prototypes */
18 #include "../dpip/dpip.h"
19
20
21 /*----------------------------------------------------------------------------
22 * Dialog interface
23 */
24
25 /* This variable can be eliminated as a parameter with a cleaner API. */
26 static char *dialog_server = NULL;
27
28
29 /*
30 * Generic callback function for dpip dialogs.
31 */
32 static void Dpiapi_dialog_answer_cb(BrowserWindow *bw)
33 {
34 DialogAnswer *answer = bw->question_dialog_answer;
35 char *cmd, numstr[16];
36
37 /* make dpip tag with the answer */
38 g_snprintf(numstr, 16, "%d", answer->alt_num);
39 cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
40 "answer", "dialog", numstr);
41
42 /* Send answer */
43 a_Capi_dpi_send_cmd(NULL, bw, cmd, dialog_server, 0);
44
45 /* cleanup */
46 bw->question_dialog_data = NULL;
47 g_free(answer->this);
48 bw->question_dialog_answer = NULL;
49 }
50
51 /*
52 * Process a dpip "dialog" command from any dpi.
53 */
54 void a_Dpiapi_dialog(BrowserWindow *bw, char *server, char *dpip_tag)
55 {
56 char *question, *alt1, *alt2, *alt3, *alt4, *alt5;
57 size_t dpip_tag_len;
58
59 MSG("a_Dpiapi_dialog:\n");
60 MSG(" dpip_tag: %s\n", dpip_tag);
61
62 /* set the module scoped variable */
63 dialog_server = server;
64
65 /* other options can be parsed the same way */
66 dpip_tag_len = strlen(dpip_tag);
67 question = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "msg");
68 alt1 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt1");
69 alt2 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt2");
70 alt3 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt3");
71 alt4 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt4");
72 alt5 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt5");
73
74 a_Interface_question_dialog(
75 bw, question, TRUE,
76 alt1, alt2, alt3, alt4, alt5,
77 (GtkSignalFunc) Dpiapi_dialog_answer_cb);
78
79 g_free(alt1); g_free(alt2); g_free(alt3); g_free(alt4); g_free(alt5);
80 g_free(question);
81 }
82
0
1 void a_Dpiapi_dialog(BrowserWindow *bw, char *server, char *dpip_tag);
2
0 /*
1 * Init and freeall, and some general functions.
2 */
3
4 #include "dw.h"
5 #include "dw_style.h"
6 #include <gdk/gdk.h>
7 #include "debug.h"
8
9 GdkCursor *Dw_cursor_hand;
10
11 void a_Dw_init (void)
12 {
13 DBG_OBJ_COLOR ("^Dw", "ffffe0");
14 DBG_OBJ_COLOR ("^Gtk", "d0ffd0");
15
16 a_Dw_style_init ();
17
18 Dw_cursor_hand = gdk_cursor_new(GDK_HAND2);
19 }
20
21
22 void a_Dw_freeall (void)
23 {
24 a_Dw_style_freeall ();
25
26 gdk_cursor_destroy (Dw_cursor_hand);
27 }
28
29 /*
30 * Return whether src1 and src2 intersect. If yes, return the intersection
31 * rectangle in dest.
32 *
33 * The function has been copied from gdktrectangle.c
34 */
35 gint p_Dw_rectangle_intersect (DwRectangle *src1,
36 DwRectangle *src2,
37 DwRectangle *dest)
38 {
39 DwRectangle *temp;
40 gint src1_x2, src1_y2;
41 gint src2_x2, src2_y2;
42 gint return_val;
43
44 g_return_val_if_fail (src1 != NULL, FALSE);
45 g_return_val_if_fail (src2 != NULL, FALSE);
46 g_return_val_if_fail (dest != NULL, FALSE);
47
48 return_val = FALSE;
49
50 if (src2->x < src1->x) {
51 temp = src1;
52 src1 = src2;
53 src2 = temp;
54 }
55 dest->x = src2->x;
56
57 src1_x2 = src1->x + src1->width;
58 src2_x2 = src2->x + src2->width;
59
60 if (src2->x < src1_x2) {
61 if (src1_x2 < src2_x2)
62 dest->width = src1_x2 - dest->x;
63 else
64 dest->width = src2_x2 - dest->x;
65
66 if (src2->y < src1->y) {
67 temp = src1;
68 src1 = src2;
69 src2 = temp;
70 }
71 dest->y = src2->y;
72
73 src1_y2 = src1->y + src1->height;
74 src2_y2 = src2->y + src2->height;
75
76 if (src2->y < src1_y2) {
77 return_val = TRUE;
78
79 if (src1_y2 < src2_y2)
80 dest->height = src1_y2 - dest->y;
81 else
82 dest->height = src2_y2 - dest->y;
83
84 if (dest->height == 0)
85 return_val = FALSE;
86 if (dest->width == 0)
87 return_val = FALSE;
88 }
89 }
90
91 return return_val;
92 }
93
94
95 /*
96 * Return whether rect1 is a subset of rect2.
97 */
98 gboolean p_Dw_rectangle_is_subset (DwRectangle *rect1,
99 DwRectangle *rect2)
100 {
101 return
102 rect1->x >= rect2->x &&
103 rect1->y >= rect2->y &&
104 rect1->x + rect1->width <= rect2->x + rect2->width &&
105 rect1->y + rect1->height <= rect2->y + rect2->height;
106 }
0 #ifndef __DW_H__
1 #define __DW_H__
2
3 #include <gdk/gdktypes.h>
4
5 typedef struct _DwRectangle DwRectangle;
6
7 struct _DwRectangle
8 {
9 gint32 x;
10 gint32 y;
11 gint32 width;
12 gint32 height;
13 };
14
15 void a_Dw_init (void);
16 void a_Dw_freeall (void);
17
18 gboolean p_Dw_rectangle_intersect (DwRectangle *src1,
19 DwRectangle *src2,
20 DwRectangle *dest);
21 gboolean p_Dw_rectangle_is_subset (DwRectangle *rect1,
22 DwRectangle *rect2);
23
24 /* Needed at several points. */
25 extern GdkCursor *Dw_cursor_hand;
26
27 #endif /* __DW_H__ */
0 /*
1 * File: dw_aligned_page.c
2 *
3 * Copyright (C) 2002, 2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This is an abstract class for horizontical alignment of an vertical
13 * array of text blocks, e.g. list items (with list item markers of
14 * different width) and (in the future) table cells aligned at
15 * characters (e.g. decimal point/comma).
16 *
17 * The aligned text blocks are represented by instances of sub-classes
18 * of DwAlignedPage, which define a value used for alignment
19 * (e.g. list item marker width), and implement the following two
20 * methods:
21 *
22 * get_value: Returns the value independent of all other
23 * values.
24 *
25 * set_max_value: This method is called to set the actual value,
26 * calculated from the maximum of all values.
27 */
28
29 #include "dw_aligned_page.h"
30 #include "list.h"
31
32 static void Dw_aligned_page_class_init (DwAlignedPageClass *klass);
33 static void Dw_aligned_page_init (DwAlignedPage *aligned_page);
34
35 static void Dw_aligned_page_destroy (GtkObject *object);
36
37 static gint32 Dw_aligned_page_real_get_value (DwAlignedPage *aligned_page);
38 static void Dw_aligned_page_real_set_max_value (DwAlignedPage *aligned_page,
39 gint32 max_value,
40 gint32 value);
41
42 static DwPageClass *parent_class;
43
44 /*
45 * Standard Gtk+ function.
46 */
47 GtkType a_Dw_aligned_page_get_type (void)
48 {
49 static GtkType type = 0;
50
51 if (!type) {
52 GtkTypeInfo info = {
53 "DwAlignedPage",
54 sizeof (DwAlignedPage),
55 sizeof (DwAlignedPageClass),
56 (GtkClassInitFunc) Dw_aligned_page_class_init,
57 (GtkObjectInitFunc) Dw_aligned_page_init,
58 (GtkArgSetFunc) NULL,
59 (GtkArgGetFunc) NULL,
60 (GtkClassInitFunc) NULL
61 };
62
63 type = gtk_type_unique (DW_TYPE_PAGE, &info);
64 }
65
66 return type;
67 }
68
69
70 /*
71 * Standard Gtk+ function.
72 */
73 static void Dw_aligned_page_class_init (DwAlignedPageClass *klass)
74 {
75 parent_class = gtk_type_class (DW_TYPE_PAGE);
76 GTK_OBJECT_CLASS(klass)->destroy = Dw_aligned_page_destroy;
77 klass->get_value = Dw_aligned_page_real_get_value;
78 klass->set_max_value = Dw_aligned_page_real_set_max_value;
79 }
80
81
82 /*
83 * Standard Gtk+ function.
84 */
85 static void Dw_aligned_page_init (DwAlignedPage *aligned_page)
86 {
87 aligned_page->list = NULL;
88 }
89
90
91 /*
92 * Standard Gtk+ function.
93 */
94 static void Dw_aligned_page_destroy (GtkObject *object)
95 {
96 DwAlignedPage *aligned_page = DW_ALIGNED_PAGE (object);
97
98 if (aligned_page->list) {
99 if (aligned_page->list->refcount == 1) {
100 /* This is the last page in the array, so the list is removed. */
101 g_free (aligned_page->list->pages);
102 g_free (aligned_page->list->values);
103 g_free (aligned_page->list);
104 } else {
105 /* Remove this page from the list. The values are simply set to
106 NULL and 0. */
107 aligned_page->list->pages[aligned_page->list_pos] = NULL;
108 aligned_page->list->values[aligned_page->list_pos] = 0;
109 aligned_page->list->refcount--;
110 }
111 }
112
113 GTK_OBJECT_CLASS(parent_class)->destroy (object);
114 }
115
116
117 /*
118 * This function should be called by the sub-class to define the
119 * relations between the aligned pages. ref_page is either NULL (this
120 * will create a new array), or one page in an other array.
121 */
122 void p_Dw_aligned_page_set_ref_page (DwAlignedPage *aligned_page,
123 DwAlignedPage *ref_page)
124 {
125 DwAlignedPageList *list;
126
127 if (ref_page == NULL) {
128 list = g_new (DwAlignedPageList, 1);
129 list->num = 0;
130 list->refcount = 0;
131 list->pages = NULL;
132 list->values = NULL;
133 list->num_pages_max = 4;
134 list->num_values_max = 4;
135 list->max_value = 0;
136 } else
137 list = ref_page->list;
138
139 list->num++;
140 list->refcount++;
141 a_List_add (list->pages, list->num, list->num_pages_max);
142 list->pages[list->num - 1] = aligned_page;
143 a_List_add (list->values, list->num, list->num_values_max);
144 list->values[list->num - 1] = 0;
145 aligned_page->list = list;
146 aligned_page->list_pos = list->num - 1;
147 p_Dw_aligned_page_update_value (aligned_page);
148 }
149
150
151 /*
152 * This function should be called by the sub-class whenever the
153 * alignment value changes.
154 */
155 void p_Dw_aligned_page_update_value (DwAlignedPage *aligned_page)
156 {
157 DwAlignedPageClass *klass;
158 int i;
159
160 if (aligned_page->list) {
161 klass = DW_ALIGNED_PAGE_CLASS (GTK_OBJECT(aligned_page)->klass);
162 aligned_page->list->values[aligned_page->list_pos] =
163 klass->get_value (aligned_page);
164
165 if (aligned_page->list->values[aligned_page->list_pos] >
166 aligned_page->list->max_value) {
167 /* New value greater than current maximum -> apply it to others. */
168 aligned_page->list->max_value =
169 aligned_page->list->values[aligned_page->list_pos];
170
171 for (i = 0; i < aligned_page->list->num; i++)
172 if (aligned_page->list->pages[i]) {
173 klass = DW_ALIGNED_PAGE_CLASS
174 (GTK_OBJECT(aligned_page->list->pages[i])->klass);
175 klass->set_max_value (aligned_page->list->pages[i],
176 aligned_page->list->max_value,
177 aligned_page->list->values[i]);
178 }
179 } else {
180 /* No change, apply old max_value only to this page. */
181 klass = DW_ALIGNED_PAGE_CLASS (GTK_OBJECT(aligned_page)->klass);
182 klass->set_max_value (aligned_page, aligned_page->list->max_value,
183 aligned_page->list->values
184 [aligned_page->list_pos]);
185 }
186 }
187 }
188
189
190 /*
191 * Standard implementation of DwAlignedPage::get_value.
192 */
193 static gint32 Dw_aligned_page_real_get_value (DwAlignedPage *aligned_page)
194 {
195 g_warning ("DwAlignedPage::get_value not implemented for `%s'",
196 gtk_type_name (GTK_OBJECT_TYPE (aligned_page)));
197 return 0;
198 }
199
200
201 /*
202 * Standard implementation of DwAlignedPage::set_max_value.
203 */
204 static void Dw_aligned_page_real_set_max_value (DwAlignedPage *aligned_page,
205 gint32 max_value,
206 gint32 value)
207 {
208 g_warning ("DwAlignedPage::set_max_value not implemented for `%s'",
209 gtk_type_name (GTK_OBJECT_TYPE (aligned_page)));
210 }
0 #ifndef __DW_ALIGNED_PAGE_H__
1 #define __DW_ALIGNED_PAGE_H__
2
3 #include "dw_page.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_ALIGNED_PAGE (a_Dw_aligned_page_get_type ())
10 #define DW_ALIGNED_PAGE(obj) GTK_CHECK_CAST (obj, \
11 DW_TYPE_ALIGNED_PAGE, DwAlignedPage)
12 #define DW_ALIGNED_PAGE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_ALIGNED_PAGE, \
14 DwAlignedPageClass)
15 #define DW_IS_ALIGNED_PAGE(obj) GTK_CHECK_TYPE (obj, \
16 DW_TYPE_ALIGNED_PAGE)
17
18 typedef struct _DwAlignedPage DwAlignedPage;
19 typedef struct _DwAlignedPageClass DwAlignedPageClass;
20 typedef struct _DwAlignedPageList DwAlignedPageList;
21
22 struct _DwAlignedPage
23 {
24 DwPage page;
25
26 DwAlignedPageList *list;
27 gint list_pos;
28 };
29
30 struct _DwAlignedPageClass
31 {
32 DwPageClass parent_class;
33
34 gint32 (*get_value) (DwAlignedPage *aligned_page);
35 void (*set_max_value) (DwAlignedPage *aligned_page,
36 gint32 max_value,
37 gint32 value);
38 };
39
40
41 struct _DwAlignedPageList
42 {
43 gint num; /* The index of the last page added (minus one). */
44 gint refcount; /* The numbers of non-NULL pages, may be smaller than num.
45 * The distinction is necessary, because removing is
46 * simply done by assigning NULL. */
47 DwAlignedPage **pages;
48 gint32 *values;
49 gint num_pages_max;
50 gint num_values_max;
51
52 gint32 max_value;
53 };
54
55
56 GtkType a_Dw_aligned_page_get_type (void);
57
58 void p_Dw_aligned_page_set_ref_page (DwAlignedPage *aligned_page,
59 DwAlignedPage *ref_page);
60 void p_Dw_aligned_page_update_value (DwAlignedPage *aligned_page);
61
62 #ifdef __cplusplus
63 }
64 #endif /* __cplusplus */
65
66 #endif /* __DW_ALIGNED_PAGE_H__ */
0 /*
1 * File: dw_bullet.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Luca Rota <drake@freemail.it>
5 * Copyright (C) 2001-2003 Sebastian Geerken <s.geerken@ping.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * Bullets are drawn 1/5 of an x-height above the baseline, and are
15 * 4/5 of an x-height wide and high.
16 */
17
18 #include "dw_bullet.h"
19 #include "dw_gtk_viewport.h"
20
21 static void Dw_bullet_init (DwBullet *bullet);
22 static void Dw_bullet_class_init (DwBulletClass *klass);
23 static void Dw_bullet_size_request (DwWidget *widget,
24 DwRequisition *requisition);
25 static void Dw_bullet_draw (DwWidget *widget,
26 DwRectangle *area,
27 GdkEventExpose *event);
28 static DwIterator* Dw_bullet_iterator (DwWidget *widget,
29 gint32 mask,
30 gboolean at_end);
31 static void Dw_bullet_iterator_highlight (DwIterator *it,
32 gint start,
33 gint end,
34 DwHighlightLayer layer);
35
36
37
38 GtkType a_Dw_bullet_get_type (void)
39 {
40 static GtkType type = 0;
41
42 if (!type) {
43 GtkTypeInfo info = {
44 "DwBullet",
45 sizeof (DwBullet),
46 sizeof (DwBulletClass),
47 (GtkClassInitFunc) Dw_bullet_class_init,
48 (GtkObjectInitFunc) Dw_bullet_init,
49 (GtkArgSetFunc) NULL,
50 (GtkArgGetFunc) NULL,
51 (GtkClassInitFunc) NULL
52 };
53
54 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
55 }
56
57 return type;
58 }
59
60
61 DwWidget* a_Dw_bullet_new (void)
62 {
63 return DW_WIDGET (gtk_object_new (DW_TYPE_BULLET, NULL));
64 }
65
66
67 static void Dw_bullet_init (DwBullet *bullet)
68 {
69 int i;
70 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
71 bullet->selected[i] = FALSE;
72 }
73
74
75 static void Dw_bullet_class_init (DwBulletClass *klass)
76 {
77 DwWidgetClass *widget_class;
78
79 widget_class = (DwWidgetClass*)klass;
80 widget_class->size_request = Dw_bullet_size_request;
81 widget_class->draw = Dw_bullet_draw;
82 widget_class->iterator = Dw_bullet_iterator;
83 }
84
85
86 static void Dw_bullet_size_request (DwWidget *widget,
87 DwRequisition *requisition)
88 {
89 requisition->width = MAX (widget->style->font->x_height * 4 / 5, 1);
90 requisition->ascent = MAX (widget->style->font->x_height, 1);
91 requisition->descent = 0;
92 }
93
94
95 static void Dw_bullet_draw (DwWidget *widget,
96 DwRectangle *area,
97 GdkEventExpose *event)
98 {
99 gint32 x0, y0, x, y;
100 GdkGC *gc;
101 DwStyleColor *bg_color;
102 GdkWindow *window;
103 gint32 l;
104 int i;
105 gboolean selected = FALSE;
106
107 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
108 selected = DW_BULLET(widget)->selected[i];
109
110 l = MIN (widget->allocation.width, widget->allocation.ascent);
111 x = x0 = p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x);
112 y0 = p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y);
113 y = y0 + widget->allocation.ascent - widget->style->font->x_height;
114 gc = selected ? widget->style->color->inverse_gc :
115 widget->style->color->gc;
116 window = DW_WIDGET_WINDOW (widget);
117
118 if (selected) {
119 bg_color = p_Dw_widget_get_bg_color (widget);
120 gdk_draw_rectangle (window, bg_color->inverse_gc,
121 TRUE, x0, y0,
122 widget->allocation.width,
123 DW_WIDGET_HEIGHT(widget));
124 }
125
126 switch (widget->style->list_style_type) {
127 case DW_STYLE_LIST_STYLE_TYPE_DISC:
128 gdk_draw_arc (window, gc, TRUE, x, y, l, l, 0, 360 * 64);
129 break;
130 case DW_STYLE_LIST_STYLE_TYPE_CIRCLE:
131 gdk_draw_arc (window, gc, FALSE, x, y, l, l, 0, 360 * 64);
132 break;
133 case DW_STYLE_LIST_STYLE_TYPE_SQUARE:
134 gdk_draw_rectangle (window, gc, FALSE, x, y, l, l);
135 break;
136 default:
137 break;
138 }
139 }
140
141 static DwIterator *Dw_bullet_iterator (DwWidget *widget,
142 gint32 mask,
143 gboolean at_end)
144 {
145 DwIterator *it;
146 gchar *text;
147
148 switch (widget->style->list_style_type) {
149 case DW_STYLE_LIST_STYLE_TYPE_DISC:
150 text = "*";
151 break;
152 case DW_STYLE_LIST_STYLE_TYPE_CIRCLE:
153 text = "o";
154 break;
155 case DW_STYLE_LIST_STYLE_TYPE_SQUARE:
156 text = "-";
157 break;
158 default:
159 text = "?";
160 break;
161 }
162
163 it = p_Dw_widget_text_iterator (widget, mask, at_end, text);
164 if (it)
165 it->highlight = Dw_bullet_iterator_highlight;
166 return it;
167 }
168
169 static void Dw_bullet_iterator_highlight (DwIterator *it,
170 gint start,
171 gint end,
172 DwHighlightLayer layer)
173 {
174 if (it->content.type == DW_CONTENT_TEXT) {
175 DW_BULLET(it->widget)->selected[layer] = (start == 0 && end >= 1);
176 p_Dw_widget_queue_draw (it->widget);
177 }
178 }
0 #ifndef __DW_BULLET_H__
1 #define __DW_BULLET_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_BULLET (a_Dw_bullet_get_type ())
10 #define DW_BULLET(obj) GTK_CHECK_CAST (obj,DW_TYPE_BULLET, DwBullet)
11 #define DW_BULLET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_BULLET, \
12 DwBulletClass)
13 #define DW_IS_BULLET(obj) GTK_CHECK_TYPE (obj, DW_TYPE_BULLET)
14
15
16 typedef struct _DwBullet DwBullet;
17 typedef struct _DwBulletClass DwBulletClass;
18
19 struct _DwBullet
20 {
21 DwWidget widget;
22
23 gboolean selected[DW_HIGHLIGHT_NUM_LAYERS];
24 };
25
26 struct _DwBulletClass
27 {
28 DwWidgetClass parent_class;
29 };
30
31
32 GtkType a_Dw_bullet_get_type (void);
33 DwWidget* a_Dw_bullet_new (void);
34
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
39 #endif /* __DW_BULLET_H__ */
0 /*
1 * File: dw_button.c
2 *
3 * Copyright (C) 2002 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This widget imitates the look and behavior of GtkButton.
13 *
14 * NOTE: Unlike in X, we do not get release events, if the pointer has
15 * been moved out of the widget with mouse button down (implicit
16 * pointer grab, will perhaps be added to Dw). For this reason, the
17 * behavior of DwButton is a bit differernt from GtkButton: If the
18 * user presses the mouse button within the button, then leaves it
19 * (with mouse button pressed), and enters it again, a GtkButton will
20 * still be in the state "pressed", while DwButton resets this state
21 * when the user leaves the button. This is to avoid problems in the
22 * situation when the user leaves the button with mouse button down,
23 * and releases it outside of the button.
24 *
25 * BUG (reminder): If the user releases the button, it gets somehow a
26 * "leave_notify" event. This bug is in the base code, not here,
27 * somehow it is assumed that the mouse pointer is out of the viewport
28 * (Dw_widget_mouse_event is called with widget == NULL). The effect
29 * is that after releasing the mouse button, the DwButton switches
30 * from "active" to "normal", not to "highlighted", as it should.
31 * Should be fixed at the next opportunity.
32 */
33
34 #include "dw_button.h"
35 #include "dw_gtk_viewport.h"
36 #include "debug.h"
37 #include <gtk/gtk.h>
38
39 static void Dw_button_init (DwButton *button);
40 static void Dw_button_class_init (DwButtonClass *klass);
41
42 static void Dw_button_size_request (DwWidget *widget,
43 DwRequisition *requisition);
44 static void Dw_button_get_extremes (DwWidget *widget,
45 DwExtremes *extremes);
46 static void Dw_button_size_allocate (DwWidget *widget,
47 DwAllocation *allocation);
48 static void Dw_button_set_width (DwWidget *widget,
49 gint32 width);
50 static void Dw_button_set_ascent (DwWidget *widget,
51 gint32 ascent);
52 static void Dw_button_set_descent (DwWidget *widget,
53 gint32 descent);
54 static void Dw_button_draw (DwWidget *widget,
55 DwRectangle *area,
56 GdkEventExpose *event);
57 static gboolean Dw_button_button_press (DwWidget *widget,
58 gint32 x,
59 gint32 y,
60 GdkEventButton *event);
61 static gboolean Dw_button_button_release (DwWidget *widget,
62 gint32 x,
63 gint32 y,
64 GdkEventButton *event);
65 static gboolean Dw_button_enter_notify (DwWidget *widget,
66 DwWidget *last_widget,
67 GdkEventMotion *event);
68 static gboolean Dw_button_leave_notify (DwWidget *widget,
69 DwWidget *next_widget,
70 GdkEventMotion *event);
71
72
73 static void Dw_button_add (DwContainer *container,
74 DwWidget *widget);
75 static void Dw_button_remove (DwContainer *container,
76 DwWidget *widget);
77 static void Dw_button_forall (DwContainer *container,
78 DwCallback callback,
79 gpointer callback_data);
80
81 static DwIterator* Dw_button_iterator (DwWidget *widget,
82 gint mask,
83 gboolean at_end);
84 static gboolean Dw_button_iterator_next (DwIterator *it);
85 static gboolean Dw_button_iterator_prev (DwIterator *it);
86 static gint Dw_button_iterator_compare (DwIterator *it1,
87 DwIterator *it2);
88
89
90 enum
91 {
92 CLICKED,
93 CLICKED_AT,
94 LAST_SIGNAL
95 };
96
97 static DwContainerClass *parent_class;
98 static guint button_signals[LAST_SIGNAL] = { 0 };
99
100
101 /*
102 * Return the type of DwButton
103 */
104 GtkType a_Dw_button_get_type (void)
105 {
106 static GtkType type = 0;
107
108 if (!type) {
109 GtkTypeInfo info = {
110 "DwButton",
111 sizeof (DwButton),
112 sizeof (DwButtonClass),
113 (GtkClassInitFunc) Dw_button_class_init,
114 (GtkObjectInitFunc) Dw_button_init,
115 (GtkArgSetFunc) NULL,
116 (GtkArgGetFunc) NULL,
117 (GtkClassInitFunc) NULL
118 };
119
120 type = gtk_type_unique (DW_TYPE_CONTAINER, &info);
121 }
122
123 return type;
124 }
125
126
127 /*
128 * Standard Gtk+ function.
129 *
130 * - "flags" is a mask for the window flags, it should be the mask the
131 * child uses (e.g. DW_USES_HINT for DwPage, 0 for DwImage). (Will
132 * hopefully be removed soon.)
133 * - When passing FALSE for "relief", the button will neither have a
134 * border, nor paint a background.
135 */
136 DwWidget* a_Dw_button_new (gint flags,
137 gboolean relief)
138 {
139 DwButton *button;
140
141 button = DW_BUTTON (gtk_object_new (DW_TYPE_BUTTON, NULL));
142 DBG_OBJ_CREATE (button, "DwButton");
143 DW_WIDGET_SET_FLAGS (button, flags);
144 button->relief = relief;
145
146 return DW_WIDGET (button);
147 }
148
149
150 /*
151 * Standard Gtk+ function.
152 */
153 static void Dw_button_init (DwButton *button)
154 {
155 button->child = NULL;
156 button->in_button = FALSE;
157 button->pressed = FALSE;
158 button->sensitive = FALSE;
159 }
160
161 /*
162 * Standard Gtk+ function.
163 */
164 static void Dw_button_class_init (DwButtonClass *klass)
165 {
166 GtkObjectClass *object_class;
167 DwWidgetClass *widget_class;
168 DwContainerClass *container_class;
169
170 object_class = (GtkObjectClass*) klass;
171 widget_class = (DwWidgetClass*) klass;
172 container_class = (DwContainerClass*) klass;
173 parent_class = gtk_type_class (DW_TYPE_CONTAINER);
174
175 button_signals[CLICKED] =
176 gtk_signal_new ("clicked",
177 GTK_RUN_FIRST | GTK_RUN_ACTION,
178 object_class->type,
179 GTK_SIGNAL_OFFSET (DwButtonClass, clicked),
180 gtk_marshal_NONE__NONE,
181 GTK_TYPE_NONE, 0);
182 button_signals[CLICKED_AT] =
183 gtk_signal_new ("clicked_at",
184 GTK_RUN_FIRST | GTK_RUN_ACTION,
185 object_class->type,
186 GTK_SIGNAL_OFFSET (DwButtonClass, clicked_at),
187 gtk_marshal_NONE__INT_INT,
188 GTK_TYPE_NONE,
189 2, GTK_TYPE_INT, GTK_TYPE_INT);
190 gtk_object_class_add_signals (object_class, button_signals, LAST_SIGNAL);
191
192 widget_class->size_request = Dw_button_size_request;
193 widget_class->get_extremes = Dw_button_get_extremes;
194 widget_class->size_allocate = Dw_button_size_allocate;
195 widget_class->set_width = Dw_button_set_width;
196 widget_class->set_ascent = Dw_button_set_ascent;
197 widget_class->set_descent = Dw_button_set_descent;
198 widget_class->draw = Dw_button_draw;
199 widget_class->button_press_event = Dw_button_button_press;
200 widget_class->button_release_event = Dw_button_button_release;
201 widget_class->enter_notify_event = Dw_button_enter_notify;
202 widget_class->leave_notify_event = Dw_button_leave_notify;
203 widget_class->iterator = Dw_button_iterator;
204
205 container_class->add = Dw_button_add;
206 container_class->remove = Dw_button_remove;
207 container_class->forall = Dw_button_forall;
208
209 klass->clicked = NULL;
210 klass->clicked_at = NULL;
211 }
212
213
214 /*
215 * Standard Dw function.
216 */
217 static void Dw_button_size_request (DwWidget *widget,
218 DwRequisition *requisition)
219 {
220 DwButton *button = DW_BUTTON (widget);
221 DwRequisition child_requisition;
222 GtkStyleClass *styleclass = widget->viewport->style->klass;
223
224 if (button->child) {
225 p_Dw_widget_size_request (button->child, &child_requisition);
226 *requisition = child_requisition;
227 } else {
228 requisition->width = 0;
229 requisition->ascent = 0;
230 requisition->descent = 0;
231 }
232
233 if (button->relief) {
234 requisition->width += 2 * styleclass->xthickness;
235 requisition->ascent += styleclass->ythickness;
236 requisition->descent += styleclass->ythickness;
237 }
238 }
239
240
241 /*
242 * Standard Dw function.
243 */
244 static void Dw_button_get_extremes (DwWidget *widget,
245 DwExtremes *extremes)
246 {
247 DwButton *button = DW_BUTTON (widget);
248 DwExtremes child_extremes;
249 GtkStyleClass *styleclass = widget->viewport->style->klass;
250
251 if (button->child) {
252 p_Dw_widget_get_extremes (button->child, &child_extremes);
253 *extremes = child_extremes;
254 } else {
255 extremes->min_width = 0;
256 extremes->max_width = 0;
257 }
258
259 if (button->relief) {
260 extremes->min_width += 2 * styleclass->xthickness;
261 extremes->max_width += 2 * styleclass->xthickness;
262 }
263 }
264
265
266 /*
267 * Standard Dw function.
268 */
269 static void Dw_button_size_allocate (DwWidget *widget,
270 DwAllocation *allocation)
271 {
272 DwButton *button = DW_BUTTON (widget);
273 DwAllocation child_allocation;
274 GtkStyleClass *styleclass = widget->viewport->style->klass;
275
276 if (button->child) {
277 child_allocation = *allocation;
278
279 if (button->relief) {
280 child_allocation. x += styleclass->xthickness;
281 child_allocation. y += styleclass->ythickness;
282 child_allocation.width -= 2 * styleclass->xthickness;
283 child_allocation.ascent -= styleclass->ythickness;
284 child_allocation.descent -= styleclass->ythickness;
285 }
286
287 p_Dw_widget_size_allocate (button->child, &child_allocation);
288 }
289 }
290
291
292 /*
293 * Standard Dw function.
294 */
295 static void Dw_button_set_width (DwWidget *widget,
296 gint32 width)
297 {
298 DwButton *button = DW_BUTTON (widget);
299
300 if (button->child)
301 p_Dw_widget_set_width
302 (button->child,
303 width - (button->relief ?
304 (2 * widget->viewport->style->klass->xthickness) : 0));
305 }
306
307
308 /*
309 * Standard Dw function.
310 */
311 static void Dw_button_set_ascent (DwWidget *widget,
312 gint32 ascent)
313 {
314 DwButton *button = DW_BUTTON (widget);
315
316 if (button->child)
317 p_Dw_widget_set_ascent
318 (button->child,
319 ascent - (button->relief ?
320 widget->viewport->style->klass->ythickness : 0));
321 }
322
323
324 /*
325 * Standard Dw function.
326 */
327 static void Dw_button_set_descent (DwWidget *widget,
328 gint32 descent)
329 {
330 DwButton *button = DW_BUTTON (widget);
331
332 if (button->child)
333 p_Dw_widget_set_descent
334 (button->child,
335 descent - (button->relief ?
336 widget->viewport->style->klass->ythickness : 0));
337 }
338
339
340 /*
341 * Standard Dw function.
342 */
343 static void Dw_button_draw (DwWidget *widget,
344 DwRectangle *area,
345 GdkEventExpose *event)
346 {
347 DwButton *button = DW_BUTTON (widget);
348 GtkStateType state;
349 GtkShadowType shadow;
350 GdkRectangle gdk_area;
351 DwRectangle child_area;
352
353 if (button->relief) {
354 if (button->sensitive) {
355 if (button->in_button) {
356 if (button->pressed) {
357 state = GTK_STATE_ACTIVE;
358 shadow = GTK_SHADOW_IN;
359 } else {
360 state = GTK_STATE_PRELIGHT;
361 shadow = GTK_SHADOW_OUT;
362 }
363 } else {
364 state = GTK_STATE_NORMAL;
365 shadow = GTK_SHADOW_OUT;
366 }
367 } else {
368 state = GTK_STATE_INSENSITIVE;
369 shadow = GTK_SHADOW_OUT;
370 }
371
372 gdk_area.x =
373 p_Dw_widget_x_world_to_viewport (widget,
374 area->x + widget->allocation.x);
375 gdk_area.y =
376 p_Dw_widget_y_world_to_viewport (widget,
377 area->y + widget->allocation.y);
378 gdk_area.width = area->width;
379 gdk_area.height = area->height;
380
381 gtk_paint_box (widget->viewport->style, DW_WIDGET_WINDOW (widget),
382 state, shadow, &gdk_area, widget->viewport,
383 "buttondefault",
384 p_Dw_widget_x_world_to_viewport (widget,
385 widget->allocation.x),
386 p_Dw_widget_y_world_to_viewport (widget,
387 widget->allocation.y),
388 widget->allocation.width,
389 DW_WIDGET_HEIGHT(widget));
390 }
391
392 if (button->child &&
393 p_Dw_widget_intersect (button->child, area, &child_area))
394 p_Dw_widget_draw (button->child, &child_area, event);
395 }
396
397
398 /*
399 * Standard Dw function.
400 */
401 static gboolean Dw_button_button_press (DwWidget *widget,
402 gint32 x,
403 gint32 y,
404 GdkEventButton *event)
405 {
406 DwButton *button = DW_BUTTON (widget);
407
408 /* assert it was the left mouse button */
409 if (event->button != 1)
410 return FALSE;
411
412 button->pressed = TRUE;
413 if (button->relief)
414 p_Dw_widget_queue_draw (widget);
415 return TRUE;
416 }
417
418
419 /*
420 * Standard Dw function.
421 */
422 static gboolean Dw_button_button_release (DwWidget *widget,
423 gint32 x,
424 gint32 y,
425 GdkEventButton *event)
426 {
427 DwButton *button = DW_BUTTON (widget);
428 gint32 cx, cy;
429
430 /* Notice that button->pressed may have been set to FALSE in
431 * Dw_button_leave_notify. */
432 if (button->pressed && button->in_button && button->sensitive) {
433 /* simple "click" */
434 gtk_signal_emit (GTK_OBJECT (widget), button_signals[CLICKED]);
435
436 /* "clicked_at": like "clicked", but position is passed, relative
437 * to the child widget. */
438 if (button->child) {
439 cx = x + widget->allocation.x - button->child->allocation.x;
440 cy = y + widget->allocation.y - button->child->allocation.y;
441
442 /* If position is outside of the child, clip at the child
443 allocation */
444 if (cx < 0)
445 cx = 0;
446 else if (cx > button->child->allocation.width - 1)
447 cx = button->child->allocation.width - 1;
448
449 if (cy < 0)
450 cy = 0;
451 else if (cy > (DW_WIDGET_HEIGHT(button->child) - 1))
452 cy = DW_WIDGET_HEIGHT(button->child) - 1;
453
454 gtk_signal_emit (GTK_OBJECT (widget), button_signals[CLICKED_AT],
455 cx, cy);
456 }
457 }
458
459 button->pressed = FALSE;
460 if (button->relief)
461 p_Dw_widget_queue_draw (widget);
462
463 return TRUE;
464 }
465
466
467 /*
468 Standard Dw function.
469
470 */
471 static gboolean Dw_button_enter_notify (DwWidget *widget,
472 DwWidget *last_widget,
473 GdkEventMotion *event)
474 {
475 DwButton *button = DW_BUTTON (widget);
476
477 button->in_button = TRUE;
478 if (button->relief)
479 p_Dw_widget_queue_draw (widget);
480 return TRUE;
481 }
482
483
484 /*
485 * Standard Dw function.
486 */
487 static gboolean Dw_button_leave_notify (DwWidget *widget,
488 DwWidget *next_widget,
489 GdkEventMotion *event)
490 {
491 DwButton *button = DW_BUTTON (widget);
492
493 if (button->child == NULL || next_widget != button->child) {
494 button->in_button = FALSE;
495
496 /* See comment at the beginning. */
497 button->pressed = FALSE;
498
499 if (button->relief)
500 p_Dw_widget_queue_draw (widget);
501 }
502 return TRUE;
503 }
504
505
506 /*
507 * Standard Dw function.
508 */
509 static void Dw_button_add (DwContainer *container,
510 DwWidget *widget)
511 {
512 DwButton *button = DW_BUTTON (container);
513
514 g_return_if_fail (button->child == NULL);
515 button->child = widget;
516 p_Dw_widget_set_parent (widget, DW_WIDGET (container));
517 }
518
519
520 /*
521 * Standard Dw function.
522 */
523 static void Dw_button_remove (DwContainer *container,
524 DwWidget *widget)
525 {
526 DwButton *button = DW_BUTTON (container);
527
528 g_return_if_fail (button->child != NULL);
529 g_return_if_fail (widget == button->child);
530 button->child = NULL;
531 }
532
533
534 /*
535 * Standard Dw function.
536 */
537 static void Dw_button_forall (DwContainer *container,
538 DwCallback callback,
539 gpointer callback_data)
540 {
541 DwButton *button = DW_BUTTON (container);
542
543 if (button->child)
544 callback (button->child, callback_data);
545 }
546
547
548
549 /*
550 * An insensitive button does not respond on user interaction. Used
551 * in HTML forms.
552 */
553 void a_Dw_button_set_sensitive (DwButton *button,
554 gboolean sensitive)
555 {
556 button->sensitive = sensitive;
557 if (button->relief)
558 p_Dw_widget_queue_draw (DW_WIDGET (button));
559 }
560
561
562 static DwIterator *Dw_button_iterator (DwWidget *widget,
563 gint mask,
564 gboolean at_end)
565 {
566 DwIterator *it = g_new (DwIterator, 1);
567 it->widget = widget;
568 it->mask = mask;
569 it->content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
570 it->next = Dw_button_iterator_next;
571 it->prev = Dw_button_iterator_prev;
572 it->clone = p_Dw_iterator_clone_std;
573 it->compare = Dw_button_iterator_compare;
574 it->free = p_Dw_iterator_free_std;
575 it->highlight = p_Dw_iterator_highlight_std;
576 it->get_allocation = p_Dw_iterator_get_allocation_std_only_widgets;
577 return it;
578 }
579
580
581 static gboolean Dw_button_iterator_next (DwIterator *it)
582 {
583 if (it->content.type == DW_CONTENT_START &&
584 (it->mask & DW_CONTENT_WIDGET) &&
585 DW_BUTTON(it->widget)->child) {
586 it->content.type = DW_CONTENT_WIDGET;
587 it->content.data.widget = DW_BUTTON(it->widget)->child;
588 return TRUE;
589 } else {
590 it->content.type = DW_CONTENT_END;
591 return FALSE;
592 }
593 }
594
595 static gboolean Dw_button_iterator_prev (DwIterator *it)
596 {
597 if (it->content.type == DW_CONTENT_END &&
598 (it->mask & DW_CONTENT_WIDGET) &&
599 DW_BUTTON(it->widget)->child) {
600 it->content.type = DW_CONTENT_WIDGET;
601 it->content.data.widget = DW_BUTTON(it->widget)->child;
602 return TRUE;
603 } else {
604 it->content.type = DW_CONTENT_START;
605 return FALSE;
606 }
607 }
608
609
610
611 static gint Dw_button_iterator_compare (DwIterator *it1,
612 DwIterator *it2)
613 {
614 if (it1->content.type == it2->content.type)
615 return 0;
616
617 switch (it1->content.type) {
618 case DW_CONTENT_START:
619 return -1;
620 case DW_CONTENT_TEXT:
621 if (it2->content.type == DW_CONTENT_START)
622 return +1;
623 else
624 return -1;
625 case DW_CONTENT_END:
626 return +1;
627 default:
628 return 0;
629 }
630 }
0 /* This module contains the dw_button widget, which is the "back end" to
1 Web text widgets including html. */
2
3 #ifndef __DW_BUTTON_H__
4 #define __DW_BUTTON_H__
5
6 #include "dw_container.h"
7
8 #ifdef __cplusplus
9 extern "C" {
10 #endif /* __cplusplus */
11
12 #define DW_TYPE_BUTTON (a_Dw_button_get_type ())
13 #define DW_BUTTON(obj) GTK_CHECK_CAST ((obj), DW_TYPE_BUTTON, \
14 DwButton)
15 #define DW_BUTTON_CLASS(klass) GTK_CHECK_CLASS_CAST ((klass), \
16 DW_TYPE_BUTTON, \
17 DwButtonClass)
18 #define DW_IS_BUTTON(obj) GTK_CHECK_TYPE ((obj), DW_TYPE_BUTTON)
19 #define DW_IS_BUTTON_CLASS(klass) GTK_CHECK_CLASS_TYPE ((klass), \
20 DW_TYPE_BUTTON)
21
22 typedef struct _DwButton DwButton;
23 typedef struct _DwButtonClass DwButtonClass;
24
25
26
27 struct _DwButton
28 {
29 DwContainer container;
30
31 DwWidget *child;
32 gboolean relief, in_button, pressed, sensitive;
33 };
34
35
36 struct _DwButtonClass
37 {
38 DwContainerClass parent_class;
39
40 void (*clicked) (DwButton *button);
41 void (*clicked_at) (DwButton *button,
42 gint32 x,
43 gint32 y);
44 };
45
46
47 GtkType a_Dw_button_get_type (void);
48 DwWidget* a_Dw_button_new (gint flags,
49 gboolean relief);
50 void a_Dw_button_set_sensitive (DwButton *button,
51 gboolean sensitive);
52
53
54 #ifdef __cplusplus
55 }
56 #endif /* __cplusplus */
57
58
59 #endif /* __DW_BUTTON_H__ */
0 #include <gtk/gtk.h>
1 #include "dw_container.h"
2
3 static void Dw_container_init (DwContainer *container);
4 static void Dw_container_class_init (DwContainerClass *klass);
5
6 static void Dw_container_destroy (GtkObject *object);
7
8 enum
9 {
10 ADD,
11 REMOVE,
12 LAST_SIGNAL
13 };
14
15 /* static guint container_signals[LAST_SIGNAL] = { 0 }; */
16
17 static DwWidgetClass *parent_class;
18
19
20 /*
21 * Standard Gtk+ function
22 */
23 GtkType a_Dw_container_get_type (void)
24 {
25 static GtkType type = 0;
26
27 if (!type) {
28 GtkTypeInfo info = {
29 "DwContainer",
30 sizeof (DwContainer),
31 sizeof (DwContainerClass),
32 (GtkClassInitFunc) Dw_container_class_init,
33 (GtkObjectInitFunc) Dw_container_init,
34 (GtkArgSetFunc) NULL,
35 (GtkArgGetFunc) NULL,
36 (GtkClassInitFunc) NULL
37 };
38
39 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
40 }
41
42 return type;
43 }
44
45
46 /*
47 * Standard Gtk+ function
48 */
49 static void Dw_container_init (DwContainer *container)
50 {
51 }
52
53
54 /*
55 * Standard Gtk+ function
56 */
57 static void Dw_container_class_init (DwContainerClass *klass)
58 {
59 GtkObjectClass *object_class;
60
61 parent_class = gtk_type_class (DW_TYPE_WIDGET);
62 object_class = GTK_OBJECT_CLASS (klass);
63
64 #if 0
65 container_signals[ADD] =
66 gtk_signal_new ("add",
67 GTK_RUN_FIRST,
68 object_class->type,
69 GTK_SIGNAL_OFFSET (DwContainerClass, add),
70 gtk_marshal_NONE__POINTER,
71 GTK_TYPE_NONE, 1,
72 GTK_TYPE_WIDGET);
73 container_signals[REMOVE] =
74 gtk_signal_new ("remove",
75 GTK_RUN_FIRST,
76 object_class->type,
77 GTK_SIGNAL_OFFSET (DwContainerClass, remove),
78 gtk_marshal_NONE__POINTER,
79 GTK_TYPE_NONE, 1,
80 GTK_TYPE_WIDGET);
81 #endif
82
83 object_class->destroy = Dw_container_destroy;
84 }
85
86
87 /*
88 * Standard Gtk+ function
89 */
90 static void Dw_container_destroy (GtkObject *object)
91 {
92 a_Dw_container_forall (DW_CONTAINER (object),
93 (DwCallback) gtk_object_destroy,
94 NULL);
95
96 GTK_OBJECT_CLASS(parent_class)->destroy (object);
97 }
98
99
100
101 /*
102 * Add a widget in a "standard" way. Currently not used in dillo,
103 * perhaps never needed.
104 */
105 void a_Dw_container_add (DwContainer *container,
106 DwWidget *child)
107 {
108 DwContainerClass *klass;
109
110 klass = DW_CONTAINER_CLASS (GTK_OBJECT (container)->klass);
111 if (klass->add)
112 (* (klass->add)) (container, child);
113 }
114
115
116 /*
117 *
118 */
119 void a_Dw_container_forall (DwContainer *container,
120 DwCallback callback,
121 gpointer callback_data)
122 {
123 DwContainerClass *klass;
124
125 klass = DW_CONTAINER_CLASS (GTK_OBJECT (container)->klass);
126 if (klass->forall)
127 (* (klass->forall)) (container, callback, callback_data);
128
129 /* The following code is handy for testing DwIterator: */
130
131 /*
132 DwIterator *it;
133
134 if ((it = a_Dw_widget_iterator (DW_WIDGET (container),
135 DW_CONTENT_WIDGET))) {
136 while (a_Dw_iterator_next(it))
137 callback (it->content.data.widget, callback_data);
138 a_Dw_iterator_free (it);
139 }
140 */
141 }
142
143 void Dw_container_remove (DwContainer *container,
144 DwWidget *child)
145 {
146 DwContainerClass *klass;
147
148 klass = DW_CONTAINER_CLASS (GTK_OBJECT (container)->klass);
149 if (klass->remove)
150 (* (klass->remove)) (container, child);
151 }
152
153
0 #ifndef __DW_CONTAINER_H__
1 #define __DW_CONTAINER_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_CONTAINER (a_Dw_container_get_type ())
10 #define DW_CONTAINER(obj) GTK_CHECK_CAST (obj, \
11 DW_TYPE_CONTAINER, DwContainer)
12 #define DW_CONTAINER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_CONTAINER, DwContainerClass)
14 #define DW_IS_CONTAINER(obj) GTK_CHECK_TYPE (obj, DW_TYPE_CONTAINER)
15
16
17 typedef void (*DwCallback) (DwWidget *widget, gpointer data);
18
19 typedef struct _DwContainer DwContainer;
20 typedef struct _DwContainerClass DwContainerClass;
21
22 struct _DwContainer
23 {
24 DwWidget widget;
25 };
26
27
28 struct _DwContainerClass
29 {
30 DwWidgetClass parent_class;
31
32 void (* add) (DwContainer *container,
33 DwWidget *child);
34 void (* remove) (DwContainer *container,
35 DwWidget *widget);
36 void (* forall) (DwContainer *container,
37 DwCallback callback,
38 gpointer callbabck_data);
39 };
40
41
42 GtkType a_Dw_container_get_type (void);
43
44 void a_Dw_container_add (DwContainer *container,
45 DwWidget *child);
46 void a_Dw_container_forall (DwContainer *container,
47 DwCallback callback,
48 gpointer callback_data);
49
50 void Dw_container_remove (DwContainer *container,
51 DwWidget *child);
52
53 #ifdef __cplusplus
54 }
55 #endif /* __cplusplus */
56
57 #endif /* __DW_CONTAINER_H__ */
0 #include <gtk/gtk.h>
1 #include "dw_embed_gtk.h"
2 #include "dw_gtk_viewport.h"
3 #include "debug.h"
4
5
6 static void Dw_embed_gtk_init (DwEmbedGtk *embed_gtk);
7 static void Dw_embed_gtk_class_init (DwEmbedGtkClass *klass);
8
9 static void Dw_embed_gtk_destroy (GtkObject *object);
10
11 static void Dw_embed_gtk_size_request (DwWidget *widget,
12 DwRequisition *requisition);
13 static gint Dw_embed_gtk_move_idle (void *data);
14 static void Dw_embed_gtk_size_allocate (DwWidget *widget,
15 DwAllocation *allocation);
16 static void Dw_embed_gtk_draw (DwWidget *widget,
17 DwRectangle *area,
18 GdkEventExpose *event);
19
20 static void Dw_embed_gtk_remove_gtk (DwEmbedGtk *embed_gtk);
21
22 static GtkWidgetClass *parent_class;
23
24 /*
25 * Standard Gtk+ function
26 */
27 GtkType a_Dw_embed_gtk_get_type (void)
28 {
29 static GtkType type = 0;
30
31 if (!type) {
32 GtkTypeInfo info = {
33 "DwEmbedGtk",
34 sizeof (DwEmbedGtk),
35 sizeof (DwEmbedGtkClass),
36 (GtkClassInitFunc) Dw_embed_gtk_class_init,
37 (GtkObjectInitFunc) Dw_embed_gtk_init,
38 (GtkArgSetFunc) NULL,
39 (GtkArgGetFunc) NULL,
40 (GtkClassInitFunc) NULL
41 };
42
43 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
44 }
45
46 return type;
47 }
48
49
50 /*
51 * Standard Gtk+ function
52 */
53 DwWidget* a_Dw_embed_gtk_new (void)
54 {
55 GtkObject *object;
56
57 object = gtk_object_new (DW_TYPE_EMBED_GTK, NULL);
58 DBG_OBJ_CREATE (object, "DwEmbedGtk");
59 return DW_WIDGET (object);
60 }
61
62
63 /*
64 * Standard Gtk+ function
65 */
66 static void Dw_embed_gtk_init (DwEmbedGtk *embed_gtk)
67 {
68 embed_gtk->child = NULL;
69 embed_gtk->idle_id = 0;
70 /*embed_gtk->child_size_request_lock = FALSE;*/
71 }
72
73
74 /*
75 * Standard Gtk+ function
76 */
77 static void Dw_embed_gtk_class_init (DwEmbedGtkClass *klass)
78 {
79 GtkObjectClass *object_class;
80 DwWidgetClass *widget_class;
81
82 parent_class = gtk_type_class (DW_TYPE_WIDGET);
83
84 object_class = GTK_OBJECT_CLASS (klass);
85 object_class->destroy = Dw_embed_gtk_destroy;
86
87 widget_class = (DwWidgetClass*) klass;
88 widget_class->size_allocate = Dw_embed_gtk_size_allocate;
89 widget_class->size_request = Dw_embed_gtk_size_request;
90 widget_class->draw = Dw_embed_gtk_draw;
91 }
92
93
94 /*
95 * Standard Gtk+ function
96 */
97 static void Dw_embed_gtk_destroy (GtkObject *object)
98 {
99 DwEmbedGtk *embed_gtk;
100
101 embed_gtk = DW_EMBED_GTK (object);
102 if (embed_gtk->child)
103 gtk_object_destroy (GTK_OBJECT (embed_gtk->child));
104 if (embed_gtk->idle_id != 0)
105 gtk_idle_remove (embed_gtk->idle_id);
106
107 GTK_OBJECT_CLASS(parent_class)->destroy (object);
108 }
109
110
111
112 /*
113 * Standard Gtk+ function
114 */
115 static void Dw_embed_gtk_size_request (DwWidget *widget,
116 DwRequisition *requisition)
117 {
118 DwEmbedGtk *embed_gtk;
119 GtkRequisition child_requisition;
120
121 embed_gtk = DW_EMBED_GTK (widget);
122 if (embed_gtk->child) {
123 /*embed_gtk->child_size_request_lock = TRUE;*/
124 gtk_widget_size_request (embed_gtk->child, &child_requisition);
125 /*embed_gtk->child_size_request_lock = FALSE;*/
126
127 requisition->width = child_requisition.width;
128 requisition->ascent = child_requisition.height;
129 requisition->descent = 0;
130 } else {
131 requisition->width = 0;
132 requisition->ascent = 0;
133 requisition->descent = 0;
134 }
135 }
136
137
138 /*
139 * Standard Gtk+ function
140 */
141 static gint Dw_embed_gtk_move_idle (void *data)
142 {
143 DwWidget *widget;
144 DwEmbedGtk *embed_gtk;
145
146 widget = DW_WIDGET (data);
147 embed_gtk = DW_EMBED_GTK (data);
148
149 if (embed_gtk->child) {
150 if (embed_gtk->child->parent)
151 gtk_layout_move (GTK_LAYOUT (widget->viewport), embed_gtk->child,
152 widget->allocation.x, widget->allocation.y);
153 else
154 gtk_layout_put (GTK_LAYOUT (widget->viewport), embed_gtk->child,
155 widget->allocation.x, widget->allocation.y);
156 }
157
158 embed_gtk->idle_id = 0;
159 return FALSE;
160 }
161
162
163 /*
164 * Standard Gtk+ function
165 */
166 static void Dw_embed_gtk_size_allocate (DwWidget *widget,
167 DwAllocation *allocation)
168 {
169 DwEmbedGtk *embed_gtk;
170
171 embed_gtk = DW_EMBED_GTK (widget);
172
173 if (embed_gtk->child && widget->viewport) {
174 if (allocation->width == 0 ||
175 allocation->ascent + allocation->descent == 0)
176 gtk_widget_hide (embed_gtk->child);
177 else {
178 gtk_widget_show (embed_gtk->child);
179
180 if (allocation->width != widget->allocation.width ||
181 allocation->ascent + allocation->descent
182 != DW_WIDGET_HEIGHT(widget))
183 /* todo: try implementing it by gtk_widget_size_allocate */
184 gtk_widget_set_usize (embed_gtk->child,
185 allocation->width,
186 allocation->ascent + allocation->descent);
187
188 if (allocation->x != widget->allocation.x ||
189 allocation->y != widget->allocation.y) {
190 /* A simple call to gtk_layout_move does not seem to work, so this
191 strange idle function. */
192 if (embed_gtk->idle_id == 0)
193 embed_gtk->idle_id =
194 gtk_idle_add (Dw_embed_gtk_move_idle, (void*) embed_gtk);
195 }
196 }
197 }
198 }
199
200
201 static void Dw_embed_gtk_draw (DwWidget *widget,
202 DwRectangle *area,
203 GdkEventExpose *event)
204 {
205 /* This is the job of GtkDwViewport (resp. the base class GtkLayout). */
206 }
207
208
209 /*
210 * This function is called when the embedded Gtk+ widget is destroyed.
211 * Don't use this function directly!
212 */
213 static void Dw_embed_gtk_remove_gtk (DwEmbedGtk *embed_gtk)
214 {
215 embed_gtk->child = NULL;
216 p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
217 }
218
219
220 /*
221 * This function is called when the embedded Gtk+ widget is focused and
222 * will set the adjustments of the viewport.
223 * Don't use this function directly!
224 */
225 static int Dw_embed_gtk_child_focus_in (DwEmbedGtk *embed_gtk)
226 {
227 DwWidget *widget;
228 GtkLayout *layout;
229 gint vx, vy, vw, vh, wx, wy, ww, wh;
230
231 widget = DW_WIDGET (embed_gtk);
232 layout = GTK_LAYOUT (widget->viewport);
233
234 vx = layout->xoffset;
235 vy = layout->yoffset;
236 vw = GTK_WIDGET(layout)->allocation.width;
237 vh = GTK_WIDGET(layout)->allocation.height;
238
239 wx = widget->allocation.x;
240 wy = widget->allocation.y;
241 ww = widget->allocation.width;
242 wh = DW_WIDGET_HEIGHT(widget);
243
244 if (vx > wx)
245 gtk_adjustment_set_value (layout->hadjustment, wx);
246 else if (vx < wx + ww - vw)
247 gtk_adjustment_set_value (layout->hadjustment, wx + ww - vw);
248
249 if (vy > wy)
250 gtk_adjustment_set_value (layout->vadjustment, wy);
251 else if (vy < wy + wh - vh)
252 gtk_adjustment_set_value (layout->vadjustment, wy + wh - vh);
253
254 return FALSE;
255 }
256
257 #if 0
258 /*
259 * Called when the embedded Gtk widget emits a "size_request" signal, and used
260 * to resise the embedding Dw widget.
261 *
262 * Currently not used.
263 */
264 static void Dw_embed_gtk_child_size_request (DwEmbedGtk *embed_gtk)
265 {
266 DwWidget *widget;
267 GtkRequisition child_requisition;
268
269 g_return_if_fail (embed_gtk->child);
270
271 if (!embed_gtk->child_size_request_lock) {
272 widget = DW_WIDGET (embed_gtk);
273 embed_gtk->child_size_request_lock = TRUE;
274 gtk_widget_size_request (embed_gtk->child, &child_requisition);
275 embed_gtk->child_size_request_lock = FALSE;
276
277 /* Check wether the size has changed at all. */
278 if (widget->requisition.width != child_requisition.width ||
279 widget->requisition.ascent != child_requisition.height)
280 p_Dw_widget_queue_resize (widget, 0, TRUE);
281 }
282 }
283 #endif
284
285 /*
286 * Add the Gtk+ widget to be embedded.
287 * If there is already one, you have to destroy it before.
288 */
289 void a_Dw_embed_gtk_add_gtk (DwEmbedGtk *embed_gtk,
290 GtkWidget *widget)
291 {
292 /* todo: problems with reparent's? */
293
294 g_return_if_fail (embed_gtk->child == NULL);
295
296 embed_gtk->child = widget;
297
298 if (DW_WIDGET(embed_gtk)->viewport)
299 gtk_layout_put (GTK_LAYOUT (DW_WIDGET (embed_gtk)->viewport),
300 widget,
301 DW_WIDGET (embed_gtk)->allocation.x,
302 DW_WIDGET (embed_gtk)->allocation.y);
303
304 /* todo: We somehow must regognize size changes of the embedded Gtk+
305 * widget. Can this be done with size_request? Anyway, currently
306 * deactivated, since it is not used in dillo. */
307 #if 0
308 gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
309 "size_request",
310 GTK_SIGNAL_FUNC(Dw_embed_gtk_child_size_request),
311 GTK_OBJECT (embed_gtk));
312 #endif
313
314 /* for setting the adjustments */
315 gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
316 "focus_in_event",
317 GTK_SIGNAL_FUNC (Dw_embed_gtk_child_focus_in),
318 GTK_OBJECT (embed_gtk));
319
320 /* todo: An idea: use "remove" signal of DwGtkContainer instead. */
321 gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
322 "destroy",
323 GTK_SIGNAL_FUNC (Dw_embed_gtk_remove_gtk),
324 GTK_OBJECT (embed_gtk));
325
326 p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
327 }
0 #ifndef __DW_EMBED_GTK_H__
1 #define __DW_EMBED_GTK_H__
2
3 #include "dw_widget.h"
4 #include <gtk/gtkwidget.h>
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10 #define DW_TYPE_EMBED_GTK (a_Dw_embed_gtk_get_type ())
11 #define DW_EMBED_GTK(obj) GTK_CHECK_CAST (obj, \
12 DW_TYPE_EMBED_GTK, DwEmbedGtk)
13 #define DW_EMBED_GTK_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
14 DW_TYPE_EMBED_GTK, DwEmbedGtkClass)
15 #define DW_IS_EMBED_GTK(obj) GTK_CHECK_TYPE (obj, DW_TYPE_EMBED_GTK)
16
17 typedef struct _DwEmbedGtk DwEmbedGtk;
18 typedef struct _DwEmbedGtkClass DwEmbedGtkClass;
19
20 struct _DwEmbedGtk
21 {
22 DwWidget container;
23
24 GtkWidget *child;
25 gint idle_id;
26 /*gboolean child_size_request_lock;*/
27 };
28
29
30 struct _DwEmbedGtkClass
31 {
32 DwWidgetClass parent_class;
33 };
34
35 GtkType a_Dw_embed_gtk_get_type (void);
36 DwWidget* a_Dw_embed_gtk_new (void);
37 void a_Dw_embed_gtk_add_gtk (DwEmbedGtk *embed_gtk,
38 GtkWidget *widget);
39
40 #ifdef __cplusplus
41 }
42 #endif /* __cplusplus */
43
44 #endif /* __DW_EMBED_GTK_H__ */
0 /*
1 * File: dw_ext_iterator.c
2 *
3 * Copyright (C) 2002 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * DwExtIterator is an extension of DwIterator, which stores
13 * DwIterator's in a stack, for cases where recursions are not
14 * possible.
15 *
16 * DwWordIterator is a simple wrapper for DwExtIterator, which splits
17 * text segments into words, and is used for text search (in
18 * findtext.c)
19 *
20 * todo: In some cases (e.g. for "<i>foo</i>bar"), DwIterator and
21 * DwExtIterator return two words, where there is actually only
22 * one. In this case, iterator->content.space is set to FALSE.
23 * DwWordIterator currently ignores this flag, but instead
24 * returns two words, instead of concaternating them to one.
25 */
26
27 #include "dw_ext_iterator.h"
28 #include "list.h"
29 #include "misc.h"
30
31 /*#define DEBUG_LEVEL 1*/
32 #include "debug.h"
33
34 /*
35 * The following two functions are used by a_Dw_extiterator_new, when the
36 * passed DwIterator points to a widget. Since a DwExtIterator never returns
37 * a widget, the DwIterator has to be corrected, by searching for the next
38 * content downwards (within the widget pointed to), forwards, and backwards
39 * (in the traversed tree).
40 */
41
42 /*
43 * Search downwards. If from_end is TRUE, start search at the end,
44 * otherwise at the beginning.
45 * The pararameter indent is only for debugging purposes.
46 */
47 static DwIterator *Dw_ext_iterator_search_downward (DwIterator *it,
48 gboolean from_end,
49 int indent)
50 {
51 DwIterator *it2, *it3;
52
53 DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
54 indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
55
56 g_return_val_if_fail (it->content.type == DW_CONTENT_WIDGET, NULL);
57 it2 = a_Dw_widget_iterator (it->content.data.widget, it->mask, from_end);
58
59 if (it2 == NULL) {
60 /* Moving downwards failed. */
61 DEBUG_MSG (1, "%*smoving down failed\n", indent, "");
62 return NULL;
63 }
64
65 while (from_end ? a_Dw_iterator_prev (it2) : a_Dw_iterator_next (it2)) {
66 DEBUG_MSG (1, "%*sexamining %s\n",
67 indent, "", a_Dw_iterator_text (it2));
68
69 if (it2->content.type == DW_CONTENT_WIDGET) {
70 /* Another widget. Search in it downwards. */
71 it3 = Dw_ext_iterator_search_downward (it2, from_end, indent + 3);
72 if (it3 != NULL) {
73 a_Dw_iterator_free (it2);
74 return it3;
75 }
76 /* Else continue in this widget. */
77 } else {
78 /* Success! */
79 DEBUG_MSG (1, "%*smoving down succeeded: %s\n",
80 indent, "", a_Dw_iterator_text (it2));
81 return it2;
82 }
83 }
84
85 /* Nothing found. */
86 a_Dw_iterator_free (it2);
87 DEBUG_MSG (1, "%*smoving down failed (nothing found)\n", indent, "");
88 return NULL;
89 }
90
91 /*
92 * Search sidewards. from_end specifies the direction, FALSE means forwards,
93 * TRUE means backwards.
94 * The pararameter indent is only for debugging purposes.
95 */
96 static DwIterator *Dw_ext_iterator_search_sideward (DwIterator *it,
97 gboolean from_end,
98 int indent)
99 {
100 DwIterator *it2, *it3;
101
102 DEBUG_MSG (1, "%*smoving %swards from %s\n",
103 indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
104
105 g_return_val_if_fail (it->content.type == DW_CONTENT_WIDGET, NULL);
106 it2 = a_Dw_iterator_clone (it);
107
108 while (from_end ? a_Dw_iterator_prev (it2) : a_Dw_iterator_next (it2)) {
109 if (it2->content.type == DW_CONTENT_WIDGET) {
110 /* Search downwards in this widget. */
111 it3 = Dw_ext_iterator_search_downward (it2, from_end, indent + 3);
112 if (it3 != NULL) {
113 a_Dw_iterator_free (it2);
114 DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
115 indent, "", from_end ? "back" : "for",
116 a_Dw_iterator_text (it3));
117 return it3;
118 }
119 /* Else continue in this widget. */
120 } else {
121 /* Success! */
122 DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
123 indent, "", from_end ? "back" : "for",
124 a_Dw_iterator_text (it2));
125 return it2;
126 }
127 }
128
129 /* Nothing found, go upwards in the tree (if possible). */
130 a_Dw_iterator_free (it2);
131 if (it->widget->parent) {
132 it2 = a_Dw_widget_iterator (it->widget->parent, it->mask, FALSE);
133 while (TRUE) {
134 if (!a_Dw_iterator_next(it2)) {
135 g_warning ("BUG in DwExtIterator!");
136 a_Dw_iterator_free (it2);
137 return NULL;
138 }
139 if (it2->content.type == DW_CONTENT_WIDGET &&
140 it2->content.data.widget == it->widget) {
141 it3 = Dw_ext_iterator_search_sideward (it2, from_end, indent + 3);
142 a_Dw_iterator_free (it2);
143 DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
144 indent, "", from_end ? "back" : "for",
145 a_Dw_iterator_text (it3));
146 return it3;
147 }
148 }
149 }
150
151 /* Nothing found at all. */
152 DEBUG_MSG (1, "%*smoving %swards failed (nothing found)\n",
153 indent, "", from_end ? "back" : "for");
154 return NULL;
155 }
156
157
158 /*
159 * Create a new DwExtIterator from an existing DwIterator. The content of the
160 * return value will be the content of "it". If within the widget tree, there
161 * is no non-widget content, NULL is returned.
162 *
163 * NOTES:
164 * (i) If you want to continue using "it", pass a_Dw_iterator_clone (it).
165 * (ii) The mask of "it" must include DW_CONTENT_WIDGET, but
166 * a_Dw_ext_iterator_next will never return widgets.
167 *
168 * TODO: Change in the near future: NULL is by all other functions within this
169 * module interpreted as an "empty" iterator, which makes code outside more
170 * robust.
171 */
172 DwExtIterator* a_Dw_ext_iterator_new (DwIterator *it)
173 {
174 DwExtIterator *eit;
175 DwIterator *it2;
176 DwWidget *w;
177 int sp;
178
179 DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
180
181 /* If "it" points to a widget, find a near non-widget content,
182 * since an DwExtIterator should never return widgets. */
183 if (it->content.type == DW_CONTENT_WIDGET) {
184 /* The second argument of Dw_ext_iterator_search_downward is
185 * actually a matter of taste :-) */
186 if ((it2 = Dw_ext_iterator_search_downward (it, FALSE, 3)) ||
187 (it2 = Dw_ext_iterator_search_sideward (it, FALSE, 3)) ||
188 (it2 = Dw_ext_iterator_search_sideward (it, TRUE, 3))) {
189 a_Dw_iterator_free (it);
190 it = it2;
191 } else {
192 /* This may happen, when a page does not contain any
193 * non-widget content. */
194 DEBUG_MSG (1, "a_Dw_ext_iterator_new got totally helpless!\n");
195 a_Dw_iterator_free (it);
196 return NULL;
197 }
198 }
199
200 DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it));
201
202 eit = g_new (DwExtIterator, 1);
203 eit->stack_top = 0;
204 /* If this widget has parents, we must construct appropiate iterators.
205 * todo: There may be a faster way instead of iterating through the
206 * parent widgets. */
207 for (w = it->widget; w->parent != NULL; w = w->parent)
208 eit->stack_top++;
209 eit->stack_max = 4;
210 while (eit->stack_top >= eit->stack_max)
211 eit->stack_max <<= 1;
212 eit->stack = g_new (DwIterator*, eit->stack_max);
213
214 /* Construct the iterators. */
215 for (w = it->widget, sp = eit->stack_top - 1;
216 w->parent != NULL;
217 w = w->parent, sp--) {
218 eit->stack[sp] = a_Dw_widget_iterator (w->parent, it->mask, FALSE);
219 while (TRUE) {
220 if (!a_Dw_iterator_next(eit->stack[sp])) {
221 g_warning ("BUG in DwExtIterator!");
222 return NULL;
223 }
224 if (eit->stack[sp]->content.type == DW_CONTENT_WIDGET &&
225 eit->stack[sp]->content.data.widget == w)
226 break;
227 }
228 }
229
230 eit->stack[eit->stack_top] = it;
231 eit->content = it->content;
232 return eit;
233 }
234
235
236 /*
237 * This function is similar to a_Dw_ext_iterator_new, but is in many
238 * cases faster, since it tries to copy parts of the stack of "base".
239 * Used for selection, where the old and new position (which are
240 * represented by DwExtIterator) are often quite next to each other.
241 * "base" may be NULL.
242 */
243 DwExtIterator* a_Dw_ext_iterator_new_variant (DwExtIterator *base,
244 DwIterator *it)
245 {
246 /* todo: Not yet implemented, and actually not yet needed very much. */
247 return a_Dw_ext_iterator_new (it);
248 }
249
250
251 /*
252 * Move iterator forward and store content in it. Returns TRUE on
253 * success.
254 */
255 gboolean a_Dw_ext_iterator_next (DwExtIterator *eit)
256 {
257 DwIterator *it = eit->stack[eit->stack_top];
258
259 if (a_Dw_iterator_next(it)) {
260 if (it->content.type == DW_CONTENT_WIDGET) {
261 /* Widget: new iterator on stack, to search in this widget. */
262 eit->stack_top++;
263 a_List_add (eit->stack, eit->stack_top, eit->stack_max);
264 eit->stack[eit->stack_top] =
265 a_Dw_widget_iterator (it->content.data.widget, it->mask, FALSE);
266 return a_Dw_ext_iterator_next (eit);
267 } else {
268 /* Simply return the content of the iterartor. */
269 eit->content = it->content;
270 return TRUE;
271 }
272 } else {
273 /* No more data in the top-most widget. */
274 if (eit->stack_top > 0) {
275 /* Pop iterator from stack, and move to next item in the old one. */
276 a_Dw_iterator_free (it);
277 eit->stack_top--;
278 return a_Dw_ext_iterator_next (eit);
279 } else {
280 /* Stack is empty. */
281 eit->content.type = DW_CONTENT_END;
282 return FALSE;
283 }
284 }
285 }
286
287 /*
288 * Move iterator backward and store content in it. Returns TRUE on
289 * success.
290 */
291 gboolean a_Dw_ext_iterator_prev (DwExtIterator *eit)
292 {
293 DwIterator *it = eit->stack[eit->stack_top];
294
295 if (a_Dw_iterator_prev(it)) {
296 if (it->content.type == DW_CONTENT_WIDGET) {
297 /* Widget: new iterator on stack, to search in this widget. */
298 eit->stack_top++;
299 a_List_add (eit->stack, eit->stack_top, eit->stack_max);
300 eit->stack[eit->stack_top] =
301 a_Dw_widget_iterator (it->content.data.widget, it->mask, TRUE);
302 return a_Dw_ext_iterator_prev (eit);
303 } else {
304 /* Simply return the content of the iterartor. */
305 eit->content = it->content;
306 return TRUE;
307 }
308 } else {
309 /* No more data in the top-most widget. */
310 if (eit->stack_top > 0) {
311 /* Pop iterator from stack, and move to previous item in the
312 old one. */
313 a_Dw_iterator_free (it);
314 eit->stack_top--;
315 return a_Dw_ext_iterator_prev (eit);
316 } else {
317 /* Stack is empty. */
318 eit->content.type = DW_CONTENT_START;
319 return FALSE;
320 }
321 }
322 }
323
324 /*
325 * Create an exact copy of the iterator, which then can be used
326 * independantly of the original one.
327 */
328 DwExtIterator* a_Dw_ext_iterator_clone (DwExtIterator *eit)
329 {
330 int i;
331 DwExtIterator *eit2 = g_new (DwExtIterator, 1);
332
333 *eit2 = *eit;
334 eit2->stack = g_new (DwIterator*, eit2->stack_max);
335 for (i = 0; i <= eit2->stack_top; i++)
336 eit2->stack[i] = a_Dw_iterator_clone (eit->stack[i]);
337
338 return eit2;
339 }
340
341 /*
342 * Return a value < 0, if first iterator is smaller, > 0, if it is
343 * greater, or 0 for equal iterators. It is assumed, that both terators
344 * belong to the same widget tree (i.e. they have the same top-level
345 * widget).
346 */
347 gint a_Dw_ext_iterator_compare (DwExtIterator *eit1,
348 DwExtIterator *eit2)
349 {
350 int nea = 0;
351
352 while (eit1->stack[nea]->widget == eit2->stack[nea]->widget) {
353 if (nea == eit1->stack_top || nea == eit2->stack_top)
354 break;
355 nea++;
356 }
357 if (eit1->stack[nea]->widget != eit2->stack[nea]->widget)
358 nea--;
359 return a_Dw_iterator_compare (eit1->stack[nea], eit2->stack[nea]);
360 }
361
362 /*
363 * Free memory of iterator.
364 */
365 void a_Dw_ext_iterator_free (DwExtIterator *eit)
366 {
367 int i;
368 for (i = 0; i <= eit->stack_top; i++)
369 a_Dw_iterator_free (eit->stack[i]);
370 g_free (eit->stack);
371 g_free (eit);
372 }
373
374 /*
375 * Create a new word iterator. <widget> is the top of the widget
376 * tree over which is iterated.
377 */
378 DwWordIterator* a_Dw_word_iterator_new (DwWidget *widget)
379 {
380 DwWordIterator *it;
381 DwIterator *it0;
382
383 it = g_new (DwWordIterator, 1);
384 it->word = NULL;
385 it0 = a_Dw_widget_iterator (widget, DW_CONTENT_TEXT | DW_CONTENT_WIDGET,
386 FALSE);
387 it->iterator = a_Dw_ext_iterator_new (it0);
388 it->word_splitpos = NULL;
389 it->content_hl_start = -1;
390 it->content_hl_end = -1;
391 return it;
392 }
393
394 /*
395 * Move iterator forward and store word in it. Returns TRUE on
396 * success.
397 */
398 gboolean a_Dw_word_iterator_next (DwWordIterator *it)
399 {
400 if (it->word) {
401 g_free (it->word);
402 it->word = NULL;
403 }
404
405 while (it->word_splitpos == NULL ||
406 it->word_splitpos[2 * (it->word_pos + 1)] == -1) {
407 if (it->word_splitpos) {
408 g_free (it->word_splitpos);
409 it->word_splitpos = NULL;
410 it->content_hl_start = -1;
411 it->content_hl_end = -1;
412 }
413 if (!a_Dw_ext_iterator_next (it->iterator))
414 return FALSE;
415 it->word_splitpos =
416 a_Misc_strsplitpos (it->iterator->content.data.text, " \t\n");
417 it->word_pos = -1;
418 }
419
420 it->word_pos++;
421 it->word = a_Misc_strpdup (it->iterator->content.data.text,
422 it->word_splitpos[2 * it->word_pos],
423 it->word_splitpos[2 * it->word_pos + 1]);
424 return TRUE;
425 }
426
427
428 /*
429 * Move iterator backward and store word in it. Returns TRUE on
430 * success.
431 */
432 gboolean a_Dw_word_iterator_prev (DwWordIterator *it)
433 {
434 if (it->word) {
435 g_free (it->word);
436 it->word = NULL;
437 }
438
439 while (it->word_splitpos == NULL || it->word_pos == 0) {
440 if (it->word_splitpos) {
441 g_free (it->word_splitpos);
442 it->word_splitpos = NULL;
443 it->content_hl_start = -1;
444 it->content_hl_end = -1;
445 }
446 if (!a_Dw_ext_iterator_prev (it->iterator))
447 return FALSE;
448 it->word_splitpos =
449 a_Misc_strsplitpos(it->iterator->content.data.text, " \t\n");
450 it->word_pos = 0;
451 while (it->word_splitpos[2 * it->word_pos] != -1)
452 it->word_pos++;
453 }
454
455 it->word_pos--;
456 it->word = a_Misc_strpdup (it->iterator->content.data.text,
457 it->word_splitpos[2 * it->word_pos],
458 it->word_splitpos[2 * it->word_pos + 1]);
459 return TRUE;
460 }
461
462 /*
463 * Highlight a part of the current word. Unhighlight the current word
464 * by passing -1 as start.
465 */
466 void a_Dw_word_iterator_highlight (DwWordIterator *it,
467 gint start,
468 gint end,
469 DwHighlightLayer layer)
470 {
471 gint new_start, new_end;
472
473 if (start == -1) {
474 /* Unhighlight the whole content word.
475 * todo: This works incorrect, by unhighlighting the whole
476 * current content of the DwExtIterator. Anyway, a correct
477 * behavior is not needed. */
478 it->content_hl_start = -1;
479 it->content_hl_end = -1;
480 a_Dw_ext_iterator_unhighlight (it->iterator, layer);
481 } else {
482 new_start = it->word_splitpos[2 * it->word_pos] + start;
483 new_end = it->word_splitpos[2 * it->word_pos] + end;
484
485 if (it->content_hl_start == -1) {
486 /* nothing selected yet */
487 it->content_hl_start = new_start;
488 it->content_hl_end = new_end;
489 } else {
490 it->content_hl_start = MIN (it->content_hl_start, new_start);
491 it->content_hl_end = MAX (it->content_hl_end, new_end);
492 }
493
494 a_Dw_ext_iterator_highlight (it->iterator, it->content_hl_start,
495 it->content_hl_end, layer);
496 }
497 }
498
499 void a_Dw_word_iterator_get_allocation (DwWordIterator *it,
500 gint start,
501 gint end,
502 DwAllocation *allocation)
503 {
504 /* todo: Implement this. (Although it is not used yet.) */
505 g_assert_not_reached ();
506 }
507
508 void a_Dw_word_iterator_scroll_to (DwWordIterator *it1,
509 DwWordIterator *it2,
510 gint start,
511 gint end,
512 DwHPosition hpos,
513 DwVPosition vpos)
514 {
515 gint real_start, real_end;
516
517 real_start = it1->word_splitpos[2 * it1->word_pos] + start;
518 real_end = it2->word_splitpos[2 * it2->word_pos] + end;
519 a_Dw_ext_iterator_scroll_to (it1->iterator, it2->iterator,
520 real_start, real_end, hpos, vpos);
521 }
522
523
524 DwWordIterator* a_Dw_word_iterator_clone (DwWordIterator *it)
525 {
526 DwWordIterator *it2;
527
528 it2 = g_new (DwWordIterator, 1);
529 *it2 = *it;
530 it2->iterator = a_Dw_ext_iterator_clone (it->iterator);
531 it2->word = it->word ? g_strdup (it->word) : NULL;
532 it2->word_splitpos =
533 it->word_splitpos ? a_Misc_strsplitposdup (it->word_splitpos) : NULL;
534 return it2;
535 }
536
537 void a_Dw_word_iterator_free (DwWordIterator *it)
538 {
539 a_Dw_ext_iterator_free (it->iterator);
540 g_free (it->word);
541 g_free (it->word_splitpos);
542 g_free (it);
543 }
0 #ifndef __DW_EXT_ITERATOR_H__
1 #define __DW_EXT_ITERATOR_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 typedef struct _DwExtIterator
10 {
11 DwIterator **stack;
12 gint stack_top;
13 gint stack_max;
14
15 DwContent content;
16 } DwExtIterator;
17
18 typedef struct _DwWordIterator
19 {
20 /* The current word. NULL, when at the end or at the beginning */
21 gchar *word;
22
23 /* private stuff */
24 DwExtIterator *iterator;
25 gint *word_splitpos; /* the positions of the words within
26 * iterator->content.data.text, as returned
27 * by a_Misc_strsplitpos */
28 gint word_pos; /* the current position within word_splitpos */
29 gint content_hl_start, content_hl_end;
30 } DwWordIterator;
31
32 DwExtIterator* a_Dw_ext_iterator_new (DwIterator *it);
33 DwExtIterator* a_Dw_ext_iterator_new_variant (DwExtIterator *base,
34 DwIterator *it);
35 gboolean a_Dw_ext_iterator_next (DwExtIterator *eit);
36 gboolean a_Dw_ext_iterator_prev (DwExtIterator *eit);
37 DwExtIterator* a_Dw_ext_iterator_clone (DwExtIterator *eit);
38 gint a_Dw_ext_iterator_compare (DwExtIterator *eit1,
39 DwExtIterator *eit2);
40 void a_Dw_ext_iterator_free (DwExtIterator *eit);
41
42 #define a_Dw_ext_iterator_highlight(eit, s, e, l) \
43 a_Dw_iterator_highlight((eit)->stack[(eit)->stack_top], \
44 s, e, l)
45 #define a_Dw_ext_iterator_unhighlight(eit, l) \
46 a_Dw_iterator_unhighlight((eit)->stack[(eit)->stack_top], l)
47 #define a_Dw_ext_iterator_get_allocation(eit, s, e, a) \
48 a_Dw_iterator_get_allocation((eit)->stack[(eit)->stack_top],\
49 s, e, a)
50 #define a_Dw_ext_iterator_scroll_to(eit1, eit2, s, e, hp, vp) \
51 a_Dw_iterator_scroll_to((eit1)->stack[(eit1)->stack_top], \
52 (eit2)->stack[(eit2)->stack_top], \
53 s, e, hp, vp)
54 #define a_Dw_ext_iterator_text(eit) ((eit) ? \
55 a_Dw_iterator_text((eit)->stack[(eit)->stack_top]) : \
56 "[NULL]")
57
58 DwWordIterator* a_Dw_word_iterator_new (DwWidget *widget);
59 gboolean a_Dw_word_iterator_next (DwWordIterator *it);
60 gboolean a_Dw_word_iterator_prev (DwWordIterator *it);
61 void a_Dw_word_iterator_highlight (DwWordIterator *it,
62 gint start,
63 gint end,
64 DwHighlightLayer layer);
65 void a_Dw_word_iterator_get_allocation (DwWordIterator *it,
66 gint start,
67 gint end,
68 DwAllocation *allocation);
69 void a_Dw_word_iterator_scroll_to (DwWordIterator *it1,
70 DwWordIterator *it2,
71 gint start,
72 gint end,
73 DwHPosition hpos,
74 DwVPosition vpos);
75 DwWordIterator* a_Dw_word_iterator_clone (DwWordIterator *it);
76 void a_Dw_word_iterator_free (DwWordIterator *it);
77
78 #define a_Dw_word_iterator_unhighlight(it, l) \
79 a_Dw_word_iterator_highlight(it, -1, -1, l)
80
81
82 #ifdef __cplusplus
83 }
84 #endif /* __cplusplus */
85
86 #endif /* __DW_EXT_ITERATOR_H__ */
0 /*
1 * File: dw_gtk_scrolled_frame.c
2 *
3 * Copyright (C) 2001 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * GtkDwScrolledFrame is a container for the GtkDwViewport, it adds
13 * a focus around the child and is responsible for focus, keys and the
14 * button 2 dragging.
15 */
16
17 #include <gtk/gtk.h>
18 #include <gdk/gdkkeysyms.h>
19 #include "dw_gtk_scrolled_frame.h"
20
21 static GtkBinClass *parent_class = NULL;
22
23 /* object/class initialisation */
24 static void Dw_gtk_scrolled_frame_init (GtkDwScrolledFrame *frame);
25 static void Dw_gtk_scrolled_frame_class_init (GtkDwScrolledFrameClass
26 *klass);
27
28 /* GtkObject methods */
29 static void Dw_gtk_scrolled_frame_destroy (GtkObject *object);
30
31 /* GtkWidget methods */
32 static void Dw_gtk_scrolled_frame_realize (GtkWidget *widget);
33 static void Dw_gtk_scrolled_frame_unrealize (GtkWidget *widget);
34 static void Dw_gtk_scrolled_frame_size_request (GtkWidget *widget,
35 GtkRequisition *requisition);
36 static void Dw_gtk_scrolled_frame_size_allocate (GtkWidget *widget,
37 GtkAllocation *allocation);
38 static void Dw_gtk_scrolled_frame_draw (GtkWidget *widget,
39 GdkRectangle *area);
40 static void Dw_gtk_scrolled_frame_draw_focus (GtkWidget *widget);
41 static gint Dw_gtk_scrolled_frame_expose (GtkWidget *widget,
42 GdkEventExpose *event);
43 static gint Dw_gtk_scrolled_frame_key_press (GtkWidget *widget,
44 GdkEventKey *event);
45 static gint Dw_gtk_scrolled_frame_focus_in (GtkWidget *widget,
46 GdkEventFocus *event);
47 static gint Dw_gtk_scrolled_frame_focus_out (GtkWidget *widget,
48 GdkEventFocus *event);
49 static gint Dw_gtk_scrolled_frame_button_press (GtkWidget *widget,
50 GdkEventButton *event);
51 static gint Dw_gtk_scrolled_frame_button_release(GtkWidget *widget,
52 GdkEventButton *event);
53 static gint Dw_gtk_scrolled_frame_motion_notify (GtkWidget *widget,
54 GdkEventMotion *event);
55
56 /* GtkContainer methods */
57 static void Dw_gtk_scrolled_frame_add (GtkContainer *container,
58 GtkWidget *widget);
59 static gint Dw_gtk_scrolled_frame_focus (GtkContainer *container,
60 GtkDirectionType direction);
61
62 /* GtkDwScrolledFrame methods */
63 static void Dw_gtk_scrolled_frame_set_scroll_adjustments (GtkDwScrolledFrame
64 *frame,
65 GtkAdjustment
66 *hadjustment,
67 GtkAdjustment
68 *vadjustment);
69
70
71 static void Dw_gtk_scrolled_frame_move_x_to (GtkDwScrolledFrame *frame,
72 gfloat x);
73 static void Dw_gtk_scrolled_frame_move_y_to (GtkDwScrolledFrame *frame,
74 gfloat y);
75 static void Dw_gtk_scrolled_frame_move_by (GtkDwScrolledFrame *frame,
76 gfloat dx,
77 gfloat dy);
78
79 static gfloat Dw_gtk_scrolled_frame_correct_adj (GtkAdjustment *adj,
80 gfloat pos);
81
82
83
84 enum
85 {
86 SET_SCROLL_ADJUSTMENTS,
87 USER_HCHANGED,
88 USER_VCHANGED,
89 LAST_SIGNAL
90 };
91
92 static guint frame_signals[LAST_SIGNAL] = { 0 };
93
94 /*
95 * Standard Gtk+ function
96 */
97 GtkType a_Dw_gtk_scrolled_frame_get_type (void)
98 {
99 static GtkType type = 0;
100
101 if (!type) {
102 GtkTypeInfo info = {
103 "GtkDwScrolledFrame",
104 sizeof (GtkDwScrolledFrame),
105 sizeof (GtkDwScrolledFrameClass),
106 (GtkClassInitFunc) Dw_gtk_scrolled_frame_class_init,
107 (GtkObjectInitFunc) Dw_gtk_scrolled_frame_init,
108 (GtkArgSetFunc) NULL,
109 (GtkArgGetFunc) NULL,
110 (GtkClassInitFunc) NULL
111 };
112
113 type = gtk_type_unique (GTK_TYPE_BIN, &info);
114 }
115
116 return type;
117 }
118
119
120 /*
121 * Standard Gtk+ function
122 */
123 GtkWidget* a_Dw_gtk_scrolled_frame_new (GtkAdjustment *hadjustment,
124 GtkAdjustment *vadjustment)
125 {
126 GtkWidget *widget;
127 GtkDwScrolledFrame *frame;
128
129 widget = gtk_widget_new (GTK_TYPE_DW_SCROLLED_FRAME, NULL);
130
131 frame = GTK_DW_SCROLLED_FRAME (widget);
132 frame->hadjustment = hadjustment;
133 frame->vadjustment = vadjustment;
134
135 return widget;
136 }
137
138 /*********************************
139 * *
140 * object/class initialisation *
141 * *
142 *********************************/
143
144 /*
145 * Standard Gtk+ function
146 */
147 static void Dw_gtk_scrolled_frame_init (GtkDwScrolledFrame *frame)
148 {
149 GTK_WIDGET_SET_FLAGS (frame, GTK_CAN_FOCUS);
150 GTK_WIDGET_UNSET_FLAGS (frame, GTK_NO_WINDOW);
151
152 frame->hadjustment = NULL;
153 frame->vadjustment = NULL;
154 frame->button2_pressed = FALSE;
155 frame->move_idle_id = 0;
156 }
157
158
159 /*
160 * Standard Gtk+ function
161 */
162 static void Dw_gtk_scrolled_frame_class_init (GtkDwScrolledFrameClass *klass)
163 {
164 GtkObjectClass *object_class;
165 GtkWidgetClass *widget_class;
166 GtkContainerClass *container_class;
167
168 parent_class = gtk_type_class (gtk_bin_get_type ());
169
170 object_class = (GtkObjectClass*) klass;
171 widget_class = (GtkWidgetClass*) klass;
172 container_class = (GtkContainerClass*) klass;
173
174 frame_signals[SET_SCROLL_ADJUSTMENTS] =
175 gtk_signal_new ("set_scroll_adjustments",
176 GTK_RUN_LAST,
177 object_class->type,
178 GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
179 set_scroll_adjustments),
180 gtk_marshal_NONE__POINTER_POINTER,
181 GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT,
182 GTK_TYPE_ADJUSTMENT);
183 widget_class->set_scroll_adjustments_signal =
184 frame_signals[SET_SCROLL_ADJUSTMENTS];
185
186 frame_signals[USER_HCHANGED] =
187 gtk_signal_new ("user_hchanged",
188 GTK_RUN_LAST | GTK_RUN_ACTION,
189 object_class->type,
190 GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
191 user_hchanged),
192 gtk_marshal_NONE__NONE,
193 GTK_TYPE_NONE, 0);
194 frame_signals[USER_VCHANGED] =
195 gtk_signal_new ("user_vchanged",
196 GTK_RUN_LAST | GTK_RUN_ACTION,
197 object_class->type,
198 GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
199 user_vchanged),
200 gtk_marshal_NONE__NONE,
201 GTK_TYPE_NONE, 0);
202
203 gtk_object_class_add_signals (object_class, frame_signals, LAST_SIGNAL);
204
205 object_class->destroy = Dw_gtk_scrolled_frame_destroy;
206
207 widget_class->realize = Dw_gtk_scrolled_frame_realize;
208 widget_class->unrealize = Dw_gtk_scrolled_frame_unrealize;
209 widget_class->size_request = Dw_gtk_scrolled_frame_size_request;
210 widget_class->size_allocate = Dw_gtk_scrolled_frame_size_allocate;
211 widget_class->draw_focus = Dw_gtk_scrolled_frame_draw_focus;
212 widget_class->draw = Dw_gtk_scrolled_frame_draw;
213 widget_class->expose_event = Dw_gtk_scrolled_frame_expose;
214 widget_class->key_press_event = Dw_gtk_scrolled_frame_key_press;
215 widget_class->focus_in_event = Dw_gtk_scrolled_frame_focus_in;
216 widget_class->focus_out_event = Dw_gtk_scrolled_frame_focus_out;
217 widget_class->button_press_event = Dw_gtk_scrolled_frame_button_press;
218 widget_class->button_release_event = Dw_gtk_scrolled_frame_button_release;
219 widget_class->motion_notify_event = Dw_gtk_scrolled_frame_motion_notify;
220
221 container_class->add = Dw_gtk_scrolled_frame_add;
222 container_class->focus = Dw_gtk_scrolled_frame_focus;
223
224 klass->set_scroll_adjustments =
225 Dw_gtk_scrolled_frame_set_scroll_adjustments;
226 klass->user_hchanged = NULL;
227 klass->user_vchanged = NULL;
228 }
229
230
231 /***********************
232 * *
233 * GtkObject methods *
234 * *
235 ***********************/
236
237 /*
238 * Standard Gtk+ function
239 */
240 static void Dw_gtk_scrolled_frame_destroy (GtkObject *object)
241 {
242 GtkDwScrolledFrame *frame;
243
244 frame = GTK_DW_SCROLLED_FRAME (object);
245 if (frame->move_idle_id != 0)
246 gtk_idle_remove (frame->move_idle_id);
247
248 GTK_OBJECT_CLASS(parent_class)->destroy (object);
249 }
250
251
252 /***********************
253 * *
254 * GtkWidget methods *
255 * *
256 ***********************/
257
258 /*
259 * Standard Gtk+ function
260 */
261 static void Dw_gtk_scrolled_frame_realize (GtkWidget *widget)
262 {
263 GdkWindowAttr attributes;
264
265 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
266
267 attributes.window_type = GDK_WINDOW_CHILD;
268 attributes.x = widget->allocation.x;
269 attributes.y = widget->allocation.y;
270 attributes.width = widget->allocation.width;
271 attributes.height = widget->allocation.height;
272 attributes.wclass = GDK_INPUT_OUTPUT;
273 attributes.visual = gtk_widget_get_visual (widget);
274 attributes.colormap = gtk_widget_get_colormap (widget);
275 attributes.event_mask = (gtk_widget_get_events (widget)
276 | GDK_EXPOSURE_MASK
277 | GDK_KEY_PRESS_MASK
278 | GDK_BUTTON_PRESS_MASK);
279
280 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
281 &attributes,
282 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
283 GDK_WA_COLORMAP);
284 gdk_window_set_user_data (widget->window, widget);
285 widget->style = gtk_style_attach (widget->style, widget->window);
286 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
287
288 GTK_DW_SCROLLED_FRAME(widget)->drag_cursor = gdk_cursor_new (GDK_FLEUR);
289 }
290
291
292 /*
293 * Standard Gtk+ function
294 */
295 static void Dw_gtk_scrolled_frame_unrealize (GtkWidget *widget)
296 {
297 gdk_cursor_destroy (GTK_DW_SCROLLED_FRAME(widget)->drag_cursor);
298 GTK_WIDGET_CLASS(parent_class)->unrealize (widget);
299 }
300
301 /*
302 * Standard Gtk+ function
303 */
304 static void Dw_gtk_scrolled_frame_size_allocate (GtkWidget *widget,
305 GtkAllocation *allocation)
306 {
307 GtkBin *bin;
308 gint border_width;
309 GtkAllocation child_allocation;
310
311 widget->allocation = *allocation;
312 bin = GTK_BIN (widget);
313
314 if (GTK_WIDGET_REALIZED (widget))
315 gdk_window_move_resize (widget->window,
316 allocation->x, allocation->y,
317 allocation->width, allocation->height);
318
319 if (bin->child) {
320 border_width = GTK_CONTAINER(widget)->border_width;
321
322 child_allocation.width =
323 MAX (allocation->width - 2 * (widget->style->klass->xthickness +
324 border_width),
325 1);
326 child_allocation.height =
327 MAX (allocation->height - 2 * (widget->style->klass->ythickness +
328 border_width),
329 1);
330 child_allocation.x = (allocation->width - child_allocation.width) / 2;
331 child_allocation.y = (allocation->height - child_allocation.height) / 2;
332
333 gtk_widget_size_allocate (bin->child, &child_allocation);
334 }
335 }
336
337
338 /*
339 * Standard Gtk+ function
340 */
341 static void Dw_gtk_scrolled_frame_size_request (GtkWidget *widget,
342 GtkRequisition *requisition)
343 {
344 GtkBin *bin;
345 gint border_width;
346 GtkRequisition child_requisition;
347
348 bin = GTK_BIN (widget);
349
350 if (bin->child) {
351 border_width = GTK_CONTAINER(widget)->border_width;
352 gtk_widget_size_request (bin->child, &child_requisition);
353
354 requisition->width =
355 child_requisition.width + 2 * (widget->style->klass->xthickness +
356 border_width);
357 requisition->height =
358 child_requisition.height + 2 * (widget->style->klass->ythickness +
359 border_width);
360 } else {
361 requisition->width = 100;
362 requisition->height = 100;
363 }
364 }
365
366
367 /*
368 * Draw the frame, eventually with a focus border
369 */
370 static void Dw_gtk_scrolled_frame_paint_shadow (GtkWidget *widget,
371 GdkRectangle *area)
372 {
373 gint border_width;
374
375 if (GTK_WIDGET_DRAWABLE (widget)) {
376 border_width = GTK_CONTAINER(widget)->border_width;
377
378 if (GTK_WIDGET_HAS_FOCUS (widget)) {
379 gtk_draw_shadow (widget->style, widget->window,
380 GTK_STATE_NORMAL, GTK_SHADOW_IN,
381 border_width + 1, border_width + 1,
382 widget->allocation.width - 2 * (border_width + 1),
383 widget->allocation.height - 2 * (border_width + 1));
384
385 /* "text" is probably next to what we need */
386 gtk_paint_focus (widget->style, widget->window,
387 area, widget, "text",
388 border_width, border_width,
389 widget->allocation.width - 2 * border_width - 1,
390 widget->allocation.height - 2 * border_width - 1);
391 } else {
392 gtk_draw_shadow (widget->style, widget->window,
393 GTK_STATE_NORMAL, GTK_SHADOW_IN,
394 border_width, border_width,
395 widget->allocation.width - 2 * border_width,
396 widget->allocation.height - 2 * border_width);
397 }
398 }
399 }
400
401
402 /*
403 * Standard Gtk+ function
404 */
405 static void Dw_gtk_scrolled_frame_draw_focus (GtkWidget *widget)
406 {
407 Dw_gtk_scrolled_frame_paint_shadow (widget, NULL);
408 }
409
410
411 /*
412 * Standard Gtk+ function
413 */
414 static void Dw_gtk_scrolled_frame_draw (GtkWidget *widget,
415 GdkRectangle *area)
416 {
417 GTK_WIDGET_CLASS(parent_class)->draw (widget, area);
418 Dw_gtk_scrolled_frame_paint_shadow (widget, area);
419 }
420
421
422 /*
423 * Standard Gtk+ function
424 */
425 static gint Dw_gtk_scrolled_frame_expose (GtkWidget *widget,
426 GdkEventExpose *event)
427 {
428 gint ret_val;
429
430 ret_val = GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
431 Dw_gtk_scrolled_frame_paint_shadow (widget, &(event->area));
432
433 return ret_val;
434 }
435
436
437 /*
438 * Standard Gtk+ function
439 */
440 static gint Dw_gtk_scrolled_frame_key_press (GtkWidget *widget,
441 GdkEventKey *event)
442 {
443 GtkContainer *container;
444 GtkDwScrolledFrame *frame;
445
446 container = GTK_CONTAINER (widget);
447 frame = GTK_DW_SCROLLED_FRAME (widget);
448
449 switch (event->keyval) {
450 case GDK_Up:
451 case GDK_KP_Up:
452 Dw_gtk_scrolled_frame_move_by (frame, 0,
453 - frame->vadjustment->step_increment);
454 return TRUE;
455
456 case GDK_Down:
457 case GDK_KP_Down:
458 Dw_gtk_scrolled_frame_move_by (frame, 0,
459 + frame->vadjustment->step_increment);
460 return TRUE;
461
462 case GDK_Left:
463 case GDK_KP_Left:
464 Dw_gtk_scrolled_frame_move_by (frame,
465 - frame->hadjustment->step_increment, 0);
466 return TRUE;
467
468 case GDK_Right:
469 case GDK_KP_Right:
470 Dw_gtk_scrolled_frame_move_by (frame,
471 + frame->hadjustment->step_increment, 0);
472 return TRUE;
473
474 case GDK_Tab:
475 if (event->state & GDK_SHIFT_MASK)
476 return gtk_container_focus (container, GTK_DIR_TAB_BACKWARD);
477 else
478 return gtk_container_focus (container, GTK_DIR_TAB_FORWARD);
479
480 case GDK_b: case GDK_B:
481 if (event->state & GDK_CONTROL_MASK)
482 return FALSE;
483 /* continues */
484 case GDK_Page_Up:
485 case GDK_KP_Page_Up:
486 if (event->state & GDK_CONTROL_MASK)
487 Dw_gtk_scrolled_frame_move_by (frame,
488 - frame->hadjustment->page_increment,
489 0);
490 else
491 Dw_gtk_scrolled_frame_move_by (frame, 0,
492 - frame->vadjustment->page_increment);
493 return TRUE;
494
495 case GDK_Page_Down:
496 case GDK_KP_Page_Down:
497 case GDK_space:
498 if (event->state & GDK_CONTROL_MASK)
499 Dw_gtk_scrolled_frame_move_by (frame,
500 + frame->hadjustment->page_increment,
501 0);
502 else
503 Dw_gtk_scrolled_frame_move_by (frame, 0,
504 + frame->vadjustment->page_increment);
505 return TRUE;
506
507 case GDK_Home:
508 case GDK_KP_Home:
509 if (event->state & GDK_CONTROL_MASK)
510 Dw_gtk_scrolled_frame_move_x_to (frame, 0);
511 else
512 Dw_gtk_scrolled_frame_move_y_to (frame, 0);
513 return TRUE;
514
515 case GDK_End:
516 case GDK_KP_End:
517 if (event->state & GDK_CONTROL_MASK)
518 Dw_gtk_scrolled_frame_move_x_to (frame, G_MAXFLOAT);
519 else
520 Dw_gtk_scrolled_frame_move_y_to (frame, G_MAXFLOAT);
521 return TRUE;
522
523 default:
524 return FALSE;
525 }
526 }
527
528
529 /*
530 * Standard Gtk+ function
531 */
532 static gint Dw_gtk_scrolled_frame_focus_in (GtkWidget *widget,
533 GdkEventFocus *event)
534 {
535 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
536 gtk_widget_draw_focus (widget);
537 return FALSE;
538 }
539
540
541 /*
542 * Standard Gtk+ function
543 */
544 static gint Dw_gtk_scrolled_frame_focus_out (GtkWidget *widget,
545 GdkEventFocus *event)
546 {
547 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
548 gtk_widget_draw_focus (widget);
549 return FALSE;
550 }
551
552
553 /*
554 * Standard Gtk+ function
555 */
556 static gint Dw_gtk_scrolled_frame_button_press (GtkWidget *widget,
557 GdkEventButton *event)
558 {
559 GtkDwScrolledFrame *frame;
560
561 if (!GTK_WIDGET_HAS_FOCUS (widget))
562 gtk_widget_grab_focus (widget);
563
564 if (event->button == 2) {
565 frame = GTK_DW_SCROLLED_FRAME (widget);
566
567 frame->button2_pressed = TRUE;
568 frame->start_lmx = event->x;
569 frame->start_lmy = event->y;
570
571 gdk_pointer_grab (widget->window, FALSE,
572 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON2_MOTION_MASK,
573 NULL, frame->drag_cursor, event->time);
574
575 }
576
577 return TRUE;
578 }
579
580
581 /*
582 * Standard Gtk+ function
583 */
584 static gint Dw_gtk_scrolled_frame_button_release (GtkWidget *widget,
585 GdkEventButton *event)
586 {
587 GTK_DW_SCROLLED_FRAME(widget)->button2_pressed = FALSE;
588 gdk_pointer_ungrab (event->time);
589 return TRUE;
590 }
591
592
593 /*
594 * Standard Gtk+ function
595 */
596 static gint Dw_gtk_scrolled_frame_motion_notify (GtkWidget *widget,
597 GdkEventMotion *event)
598 {
599 GtkDwScrolledFrame *frame = GTK_DW_SCROLLED_FRAME (widget);
600
601 /* frame->button2_pressed makes sure that the button was pressed
602 in the GtkDwScrolledFrame */
603 if ((event->state & GDK_BUTTON2_MASK) && frame->button2_pressed) {
604 Dw_gtk_scrolled_frame_move_by (frame,
605 frame->start_lmx - event->x,
606 frame->start_lmy - event->y);
607
608 frame->start_lmx = event->x;
609 frame->start_lmy = event->y;
610 }
611
612 return TRUE;
613 }
614
615
616 /**************************
617 * *
618 * GtkContainer methods *
619 * *
620 **************************/
621
622 /*
623 * Standard Gtk+ function
624 */
625 static void Dw_gtk_scrolled_frame_add (GtkContainer *container,
626 GtkWidget *widget)
627 {
628 GtkDwScrolledFrame *frame;
629
630 GTK_CONTAINER_CLASS(parent_class)->add (container, widget);
631
632 frame = GTK_DW_SCROLLED_FRAME (container);
633 if ( !gtk_widget_set_scroll_adjustments (
634 GTK_WIDGET (container), frame->hadjustment, frame->vadjustment) )
635 g_warning ("Dw_gtk_scrolled_frame_set_scroll_adjustments(): "
636 "child is non scrollable");
637 }
638
639
640 /*
641 * Standard Gtk+ function
642 */
643 static gint Dw_gtk_scrolled_frame_focus (GtkContainer *container,
644 GtkDirectionType direction)
645 {
646 /* todo:
647 * This crashed if the child is not a container (what will never happen
648 * in dillo, where GtkDwScrolledFrame always contains a GtkDwViewport).
649 */
650 GtkDwScrolledFrame *frame;
651
652 frame = GTK_DW_SCROLLED_FRAME (container);
653
654 if (!GTK_WIDGET_HAS_FOCUS (container) && container->focus_child == NULL){
655 /* no focus on the GtkDwScrolledFrame or any child widget => grab focus!
656 */
657 gtk_widget_grab_focus (GTK_WIDGET (container));
658 return TRUE;
659 }
660
661 switch (direction) {
662 case GTK_DIR_TAB_FORWARD:
663 /* deliver to child */
664 if (!gtk_container_focus (GTK_CONTAINER (GTK_BIN(container)->child),
665 direction)) {
666 /* todo: it would be nice to keep focus on this last child widget,
667 * instead of the container... Anyway this may change with GTK2 */
668 gtk_widget_grab_focus (GTK_WIDGET (container));
669 }
670 return TRUE;
671
672 case GTK_DIR_TAB_BACKWARD:
673 if (GTK_WIDGET_HAS_FOCUS (container)) {
674 /* will focus the widget "before" */
675 return FALSE;
676 } else {
677 if (!gtk_container_focus (GTK_CONTAINER (GTK_BIN(container)->child),
678 GTK_DIR_TAB_BACKWARD))
679 /* first child of the child unfocussed */
680 gtk_widget_grab_focus (GTK_WIDGET (container));
681
682 return TRUE;
683 }
684 break;
685
686 /*
687 case GTK_DIR_LEFT:
688 case GTK_DIR_RIGHT:
689 case GTK_DIR_UP:
690 case GTK_DIR_DOWN:
691 */
692 default:
693 /* focus the GtkDwScrolledFrame */
694 gtk_widget_grab_focus (GTK_WIDGET (container));
695 return TRUE;
696 }
697
698 /* make compiler happy */
699 return FALSE;
700 }
701
702
703 /********************************
704 * *
705 * GtkDwScrolledFrame methods *
706 * *
707 ********************************/
708
709 /*
710 * Standard Gtk+ function
711 */
712 static void
713 Dw_gtk_scrolled_frame_set_scroll_adjustments (GtkDwScrolledFrame *frame,
714 GtkAdjustment *hadjustment,
715 GtkAdjustment *vadjustment)
716 {
717 GtkBin *bin;
718
719 frame->hadjustment = hadjustment;
720 frame->vadjustment = vadjustment;
721
722 bin = GTK_BIN (frame);
723 if (bin->child) {
724 if (!gtk_widget_set_scroll_adjustments (bin->child,
725 hadjustment,
726 vadjustment))
727 g_warning ("Dw_gtk_scrolled_frame_set_scroll_adjustments(): "
728 "child is non scrollable");
729 }
730 }
731
732
733 /*
734 * Functions for moving the viewport. This is queued and done in an
735 * idle function, to prevent locks by too frequent keypresses, or too
736 * fast mouse movements. (How to reproduce: start dillo on a slow,
737 * overloaded system with nice priority.)
738 */
739
740 /*
741 * The idle function.
742 */
743 static gint Dw_gtk_scrolled_frame_move_idle (gpointer data)
744 {
745 GtkDwScrolledFrame *frame;
746
747 frame = GTK_DW_SCROLLED_FRAME (data);
748
749 g_return_val_if_fail (frame->hadjustment != NULL, FALSE);
750 g_return_val_if_fail (frame->vadjustment != NULL, FALSE);
751
752 if (frame->moveto_x != frame->hadjustment->value) {
753 gtk_adjustment_set_value (frame->hadjustment, frame->moveto_x);
754 gtk_signal_emit (GTK_OBJECT (frame), frame_signals[USER_HCHANGED]);
755 }
756
757 if (frame->moveto_y != frame->vadjustment->value) {
758 gtk_adjustment_set_value (frame->vadjustment, frame->moveto_y);
759 gtk_signal_emit (GTK_OBJECT (frame), frame_signals[USER_VCHANGED]);
760 }
761
762 frame->move_idle_id = 0;
763 return FALSE;
764 }
765
766
767 /*
768 * Change horizontall position to x.
769 */
770 static void Dw_gtk_scrolled_frame_move_x_to (GtkDwScrolledFrame *frame,
771 gfloat x)
772 {
773 frame->moveto_x =
774 Dw_gtk_scrolled_frame_correct_adj (frame->hadjustment, x);
775 if (frame->move_idle_id == 0)
776 frame->move_idle_id = gtk_idle_add (Dw_gtk_scrolled_frame_move_idle,
777 (gpointer) frame);
778 }
779
780
781 /*
782 * Change vertical position to y.
783 */
784 static void Dw_gtk_scrolled_frame_move_y_to (GtkDwScrolledFrame *frame,
785 gfloat y)
786 {
787 frame->moveto_y =
788 Dw_gtk_scrolled_frame_correct_adj (frame->vadjustment, y);
789 if (frame->move_idle_id == 0)
790 frame->move_idle_id = gtk_idle_add (Dw_gtk_scrolled_frame_move_idle,
791 (gpointer) frame);
792 }
793
794
795 /*
796 * Move viewport by dx and dy.
797 */
798 static void Dw_gtk_scrolled_frame_move_by (GtkDwScrolledFrame *frame,
799 gfloat dx,
800 gfloat dy)
801 {
802 if (frame->move_idle_id == 0) {
803 /*
804 * Initialization: if the idle function is not active, set
805 * moveto_x and moveto_y to adjustments values, which may
806 * already have been changed by the scrollbars. This code does
807 * not work when a user uses scrollbars and keys at the same
808 * time. ;-)
809 */
810 frame->moveto_x = frame->hadjustment->value;
811 frame->moveto_y = frame->vadjustment->value;
812 }
813
814 frame->moveto_x =
815 Dw_gtk_scrolled_frame_correct_adj (frame->hadjustment,
816 frame->moveto_x + dx);
817 frame->moveto_y =
818 Dw_gtk_scrolled_frame_correct_adj (frame->vadjustment,
819 frame->moveto_y + dy);
820
821 if (frame->move_idle_id == 0)
822 frame->move_idle_id = gtk_idle_add (Dw_gtk_scrolled_frame_move_idle,
823 (gpointer) frame);
824
825 }
826
827
828 /*
829 * Helper function: Corrects pos to fit into the boundaries of adj.
830 */
831 static gfloat Dw_gtk_scrolled_frame_correct_adj (GtkAdjustment *adj,
832 gfloat pos)
833 {
834 if (pos < adj->lower)
835 return adj->lower;
836 else if (pos > adj->upper - adj->page_size)
837 return adj->upper - adj->page_size;
838 else
839 return pos;
840 }
0 #ifndef __DW_GTK_SCROLLED_FRAME_H__
1 #define __DW_GTK_SCROLLED_FRAME_H__
2
3 #include <gdk/gdk.h>
4 #include <gtk/gtkbin.h>
5 #include <gtk/gtkadjustment.h>
6
7 #ifdef __cplusplus
8 extern "C" {
9 #endif /* __cplusplus */
10
11 #define GTK_TYPE_DW_SCROLLED_FRAME (a_Dw_gtk_scrolled_frame_get_type ())
12 #define GTK_DW_SCROLLED_FRAME(obj) (GTK_CHECK_CAST (obj, \
13 GTK_TYPE_DW_SCROLLED_FRAME, \
14 GtkDwScrolledFrame))
15 #define GTK_DW_SCROLLED_FRAME_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, \
16 GTK_DW_TYPE_SCROLLED_FRAME, \
17 GtkDwScrolledFrameClass))
18 #define GTK_DW_IS_SCROLLED_FRAME(obj) GTK_CHECK_TYPE (obj, \
19 GTK_DW_TYPE_SCROLLED_FRAME)
20
21 typedef struct _GtkDwScrolledFrame GtkDwScrolledFrame;
22 typedef struct _GtkDwScrolledFrameClass GtkDwScrolledFrameClass;
23
24
25 struct _GtkDwScrolledFrame
26 {
27 GtkBin bin;
28
29 GtkAdjustment *hadjustment;
30 GtkAdjustment *vadjustment;
31
32 /* used for button 2 dragging */
33 gboolean button2_pressed; /* TRUE if button 2 has been pressed in
34 the GtkDwScrolledFrame */
35 gfloat start_lmx, start_lmy; /* the last mouse position */
36
37 /* used for fast unblocking moving */
38 gfloat moveto_x, moveto_y; /* the position the idle function should
39 move to */
40 gboolean move_idle_id;
41
42 GdkCursor *drag_cursor;
43 };
44
45
46 struct _GtkDwScrolledFrameClass
47 {
48 GtkBinClass parent_class;
49
50 void (*set_scroll_adjustments) (GtkDwScrolledFrame *scrolled_frame,
51 GtkAdjustment *hadjustment,
52 GtkAdjustment *vadjustment);
53 void (*user_hchanged) (GtkDwScrolledFrame *scrolled_frame);
54 void (*user_vchanged) (GtkDwScrolledFrame *scrolled_frame);
55 };
56
57
58 GtkType a_Dw_gtk_scrolled_frame_get_type (void);
59 GtkWidget* a_Dw_gtk_scrolled_frame_new (GtkAdjustment *hadjustment,
60 GtkAdjustment *vadjustment);
61
62 #ifdef __cplusplus
63 }
64 #endif /* __cplusplus */
65
66 #endif /* __DW_GTK_SCROLLED_FRAME_H__ */
0 /*
1 * File: dw_gtk_scrolled_window.c
2 *
3 * Copyright (C) 2001 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <stddef.h>
12
13 #include "dw_gtk_scrolled_window.h"
14 #include "dw_gtk_scrolled_frame.h"
15 #include "dw_gtk_viewport.h"
16 #include "findtext.h"
17 #include <gtk/gtk.h>
18
19 #include "debug.h"
20
21 static GtkScrolledWindowClass *parent_class = NULL;
22
23 static void Dw_gtk_scrolled_window_init (GtkDwScrolledWindow *scrolled);
24 static void Dw_gtk_scrolled_window_class_init(GtkDwScrolledWindowClass *klass);
25
26 static void Dw_gtk_scrolled_window_destroy (GtkObject *object);
27 static void Dw_gtk_scrolled_window_map (GtkWidget *widget);
28 static void Dw_gtk_scrolled_window_unmap (GtkWidget *widget);
29 static void Dw_gtk_scrolled_window_draw (GtkWidget *widget,
30 GdkRectangle *area);
31 static void Dw_gtk_scrolled_window_size_allocate (GtkWidget *widget,
32 GtkAllocation *allocation);
33
34 static void Dw_gtk_scrolled_window_remove (GtkContainer *container,
35 GtkWidget *widget);
36 static void Dw_gtk_scrolled_window_forall (GtkContainer *container,
37 gboolean include_internals,
38 GtkCallback callback,
39 gpointer callback_data);
40
41 static void Dw_gtk_scrolled_window_changed1 (GtkDwScrolledWindow *scrolled);
42 static void Dw_gtk_scrolled_window_changed2 (GtkDwScrolledWindow *scrolled);
43
44
45 /*
46 * Standard Gtk+ function
47 */
48 GtkType a_Dw_gtk_scrolled_window_get_type (void)
49 {
50 static GtkType type = 0;
51
52 if (!type) {
53 GtkTypeInfo info = {
54 "GtkDwScrolledWindow",
55 sizeof (GtkDwScrolledWindow),
56 sizeof (GtkDwScrolledWindowClass),
57 (GtkClassInitFunc) Dw_gtk_scrolled_window_class_init,
58 (GtkObjectInitFunc) Dw_gtk_scrolled_window_init,
59 (GtkArgSetFunc) NULL,
60 (GtkArgGetFunc) NULL,
61 (GtkClassInitFunc) NULL
62 };
63
64 type = gtk_type_unique (GTK_TYPE_SCROLLED_WINDOW, &info);
65 }
66
67 return type;
68 }
69
70
71 /*
72 * Standard Gtk+ function
73 */
74 GtkWidget* a_Dw_gtk_scrolled_window_new (void)
75 {
76 return gtk_widget_new (GTK_TYPE_DW_SCROLLED_WINDOW, NULL);
77 }
78
79
80 /*
81 * Standard Gtk+ function
82 */
83 static void Dw_gtk_scrolled_window_init (GtkDwScrolledWindow *scrolled)
84 {
85 GtkAdjustment *hadjustment, *vadjustment;
86 GtkWidget *frame, *viewport;
87 size_t i;
88 char *signals[] = {
89 "button_press_event",
90 "button_release_event",
91 "motion_notify_event",
92 "key_press_event" /* although the scrollbars are not focused */
93 };
94
95 hadjustment =
96 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 10.0, 0.0, 0.0));
97 vadjustment =
98 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 10.0, 0.0, 0.0));
99
100 gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (scrolled),
101 hadjustment);
102 gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (scrolled),
103 vadjustment);
104
105 frame = a_Dw_gtk_scrolled_frame_new (hadjustment, vadjustment);
106 gtk_container_add (GTK_CONTAINER (scrolled), frame);
107 gtk_widget_show (frame);
108
109 viewport = a_Dw_gtk_viewport_new (hadjustment, vadjustment);
110 gtk_container_add (GTK_CONTAINER (frame), viewport);
111 gtk_widget_show (viewport);
112
113 scrolled->vadjustment = vadjustment;
114 scrolled->old_vadjustment_value = vadjustment->value;
115 scrolled->gadget = NULL;
116
117 /*
118 * For anchors, we need to recognize when the *user* changes the
119 * viewport and distiguish them from changes caused by the program.
120 * Instead of using the "change" signal and checking where it came
121 * from, the following code connects all possible events by which
122 * users could change the scrollbar adjustments. ...
123 */
124 for (i = 0; i < sizeof (signals) / sizeof (signals[0]); i++) {
125 gtk_signal_connect_object
126 (GTK_OBJECT (GTK_SCROLLED_WINDOW(scrolled)->vscrollbar),
127 signals[i], GTK_SIGNAL_FUNC (Dw_gtk_scrolled_window_changed1),
128 GTK_OBJECT (scrolled));
129 gtk_signal_connect_object_after
130 (GTK_OBJECT (GTK_SCROLLED_WINDOW(scrolled)->vscrollbar),
131 signals[i], GTK_SIGNAL_FUNC (Dw_gtk_scrolled_window_changed2),
132 GTK_OBJECT (scrolled));
133 }
134
135 /* ... The GtkDwScrolledFrame has a signal for this. */
136 gtk_signal_connect_object (GTK_OBJECT (frame), "user_vchanged",
137 GTK_SIGNAL_FUNC (Dw_gtk_viewport_remove_anchor),
138 GTK_OBJECT (viewport));
139 #if 0
140 /* This does not seem to work for GtkLayout's (see also dw_embed_gtk.c): */
141 gtk_container_set_focus_hadjustment (GTK_CONTAINER (viewport),
142 hadjustment);
143 gtk_container_set_focus_vadjustment (GTK_CONTAINER (viewport),
144 vadjustment);
145 #endif
146 }
147
148
149 /*
150 * Standard Gtk+ function
151 */
152 static void Dw_gtk_scrolled_window_class_init (GtkDwScrolledWindowClass *klass)
153 {
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
156 GtkContainerClass *container_class;
157
158 object_class = (GtkObjectClass*) klass;
159 widget_class = (GtkWidgetClass*) klass;
160 container_class = (GtkContainerClass*) klass;
161 parent_class = gtk_type_class (GTK_TYPE_SCROLLED_WINDOW);
162
163 object_class->destroy = Dw_gtk_scrolled_window_destroy;
164
165 widget_class->map = Dw_gtk_scrolled_window_map;
166 widget_class->unmap = Dw_gtk_scrolled_window_unmap;
167 widget_class->draw = Dw_gtk_scrolled_window_draw;
168 widget_class->size_allocate = Dw_gtk_scrolled_window_size_allocate;
169
170 container_class->remove = Dw_gtk_scrolled_window_remove;
171 container_class->forall = Dw_gtk_scrolled_window_forall;
172 }
173
174 /*
175 * Standard Gtk+ function
176 */
177 static void Dw_gtk_scrolled_window_destroy (GtkObject *object)
178 {
179 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (object);
180
181 if (scrolled->gadget)
182 gtk_widget_destroy (scrolled->gadget);
183
184 GTK_OBJECT_CLASS(parent_class)->destroy (object);
185 }
186
187 /*
188 * Standard Gtk+ function
189 */
190 static void Dw_gtk_scrolled_window_map (GtkWidget *widget)
191 {
192 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (widget);
193
194 GTK_WIDGET_CLASS(parent_class)->map (widget);
195
196 if (scrolled->gadget && GTK_WIDGET_VISIBLE (scrolled->gadget) &&
197 !GTK_WIDGET_MAPPED (scrolled->gadget))
198 gtk_widget_map (scrolled->gadget);
199 }
200
201 /*
202 * Standard Gtk+ function
203 */
204 static void Dw_gtk_scrolled_window_unmap (GtkWidget *widget)
205 {
206 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (widget);
207
208 GTK_WIDGET_CLASS(parent_class)->unmap (widget);
209
210 if (scrolled->gadget && GTK_WIDGET_MAPPED (scrolled->gadget))
211 gtk_widget_unmap (scrolled->gadget);
212 }
213
214 /*
215 * Standard Gtk+ function
216 */
217 static void Dw_gtk_scrolled_window_draw (GtkWidget *widget,
218 GdkRectangle *area)
219 {
220 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (widget);
221 GdkRectangle child_area;
222
223 GTK_WIDGET_CLASS(parent_class)->draw (widget, area);
224
225 if (scrolled->gadget && GTK_WIDGET_VISIBLE (scrolled->gadget) &&
226 gtk_widget_intersect (scrolled->gadget, area, &child_area))
227 gtk_widget_draw (scrolled->gadget, &child_area);
228 }
229
230
231 /*
232 * Standard Gtk+ function
233 */
234 static void Dw_gtk_scrolled_window_size_allocate (GtkWidget *widget,
235 GtkAllocation *allocation)
236 {
237 GtkDwViewport *viewport;
238 GtkAllocation old_allocation = widget->allocation, child_allocation;
239 GtkRequisition child_requisition;
240 GtkDwScrolledWindow *scrolled;
241 GtkWidget *hscrollbar, *vscrollbar;
242 gint gx, gy;
243
244 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
245 widget->allocation = *allocation;
246
247 DEBUG_MSG (2, "Dw_gtk_scrolled_window_size_allocate: %d x %d\n",
248 allocation->width, allocation->height);
249 if (old_allocation.width != allocation->width ||
250 old_allocation.height != allocation->height) {
251 viewport = GTK_DW_VIEWPORT (GTK_BIN(GTK_BIN(widget)->child)->child);
252
253 /* It may be that scrollbars are not needed anymore. See
254 Dw_gtk_viewport_calc_size for more details. */
255 if (allocation->width > old_allocation.width)
256 viewport->hscrollbar_used = FALSE;
257 if (allocation->height > old_allocation.height)
258 viewport->vscrollbar_used = FALSE;
259
260 Dw_gtk_viewport_calc_size (viewport);
261 }
262
263 scrolled = GTK_DW_SCROLLED_WINDOW (widget);
264 if (scrolled->gadget && GTK_WIDGET_VISIBLE (scrolled->gadget)) {
265 /* Raise the gagdet, otherwise it may be hidden. */
266 if (GTK_WIDGET_REALIZED (scrolled->gadget))
267 gdk_window_raise (scrolled->gadget->window);
268
269 /* Allocate the gadget. */
270 gtk_widget_size_request (scrolled->gadget, &child_requisition);
271 gx = allocation->x + allocation->width - child_requisition.width;
272 gy = allocation->y + allocation->height - child_requisition.height;
273 child_allocation.x = MAX (gx, 1);
274 child_allocation.y = MAX (gy, 1);
275 child_allocation.width = child_requisition.width;
276 child_allocation.height = child_requisition.height;
277 gtk_widget_size_allocate (scrolled->gadget, &child_allocation);
278
279 /* Re-allocate the scrollbars, when necessary */
280 hscrollbar = GTK_SCROLLED_WINDOW(scrolled)->hscrollbar;
281 if (GTK_WIDGET_VISIBLE (hscrollbar) &&
282 hscrollbar->allocation.x + hscrollbar->allocation.width > gx) {
283 child_allocation = hscrollbar->allocation;
284 child_allocation.width = MAX (gx - hscrollbar->allocation.x, 1);
285 gtk_widget_size_allocate (hscrollbar, &child_allocation);
286 }
287
288 vscrollbar = GTK_SCROLLED_WINDOW(scrolled)->vscrollbar;
289 if (GTK_WIDGET_VISIBLE (vscrollbar) &&
290 vscrollbar->allocation.y + vscrollbar->allocation.height > gy) {
291 child_allocation = vscrollbar->allocation;
292 child_allocation.height = MAX (gy - vscrollbar->allocation.y, 1);
293 gtk_widget_size_allocate (vscrollbar, &child_allocation);
294 }
295 }
296 }
297
298 /*
299 * Standard Gtk+ function
300 */
301 static void Dw_gtk_scrolled_window_remove (GtkContainer *container,
302 GtkWidget *widget)
303 {
304 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (container);
305 gboolean widget_was_visible;
306
307 if (widget == scrolled->gadget) {
308 widget_was_visible = GTK_WIDGET_VISIBLE (widget);
309 gtk_widget_unparent (widget);
310 scrolled->gadget = NULL;
311 } else
312 GTK_CONTAINER_CLASS(parent_class)->remove (container, widget);
313 }
314
315 /*
316 * Standard Gtk+ function
317 */
318 static void Dw_gtk_scrolled_window_forall (GtkContainer *container,
319 gboolean include_internals,
320 GtkCallback callback,
321 gpointer callback_data)
322 {
323 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (container);
324
325 GTK_CONTAINER_CLASS(parent_class)->forall (container, include_internals,
326 callback, callback_data);
327 if (scrolled->gadget)
328 callback (scrolled->gadget, callback_data);
329 }
330
331
332 /*
333 * Adds the gadget widget.
334 */
335 void a_Dw_gtk_scrolled_window_add_gadget (GtkDwScrolledWindow *scrolled,
336 GtkWidget *gadget)
337 {
338 g_return_if_fail (scrolled->gadget == NULL);
339 scrolled->gadget = gadget;
340 gtk_widget_set_parent (gadget, GTK_WIDGET (scrolled));
341 gtk_widget_queue_resize (GTK_WIDGET (scrolled));
342 }
343
344
345 /*
346 * Sets the top-level DwWidget. The old top-level DwWidget is destroyed.
347 */
348 void a_Dw_gtk_scrolled_window_set_dw (GtkDwScrolledWindow *scrolled,
349 DwWidget *widget)
350 {
351 GtkWidget *viewport;
352 DwWidget *old_child;
353
354 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
355
356 if ((old_child = GTK_DW_VIEWPORT (viewport)->child))
357 gtk_object_destroy (GTK_OBJECT (old_child));
358
359 a_Dw_gtk_viewport_add_dw (GTK_DW_VIEWPORT (viewport), widget);
360 }
361
362 /*
363 * Gets the top-level DwWidget.
364 */
365 DwWidget* a_Dw_gtk_scrolled_window_get_dw (GtkDwScrolledWindow *scrolled)
366 {
367 GtkWidget *viewport;
368
369 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
370 return GTK_DW_VIEWPORT(viewport)->child;
371 }
372
373 /*
374 * See a_Dw_gtk_viewport_set_anchor.
375 */
376 void a_Dw_gtk_scrolled_window_set_anchor (GtkDwScrolledWindow *scrolled,
377 const gchar *anchor)
378 {
379 GtkWidget *viewport;
380
381 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
382 a_Dw_gtk_viewport_set_anchor (GTK_DW_VIEWPORT (viewport), anchor);
383 }
384
385 /*
386 * The scrolling position is remembered setting this value in the history-URL
387 */
388 gint a_Dw_gtk_scrolled_window_get_scrolling_position_x (GtkDwScrolledWindow
389 *scrolled)
390 {
391 GtkLayout *viewport = GTK_LAYOUT(GTK_BIN(GTK_BIN(scrolled)->child)->child);
392
393 return ((int) viewport->hadjustment->value);
394 }
395
396 /*
397 * The scrolling position is remembered setting this value in the history-URL
398 */
399 gint a_Dw_gtk_scrolled_window_get_scrolling_position_y (GtkDwScrolledWindow
400 *scrolled)
401 {
402 GtkLayout *viewport = GTK_LAYOUT(GTK_BIN(GTK_BIN(scrolled)->child)->child);
403
404 return ((int) viewport->vadjustment->value);
405 }
406
407 /*
408 * See a_Dw_gtk_viewport_set_scrolling_position.
409 */
410 void a_Dw_gtk_scrolled_window_set_scrolling_position (GtkDwScrolledWindow
411 *scrolled,
412 gint32 x,
413 gint32 y)
414 {
415 GtkWidget *viewport;
416
417 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
418 a_Dw_gtk_viewport_set_scrolling_position (GTK_DW_VIEWPORT (viewport), x, y);
419 }
420
421
422 /*
423 * See also Dw_gtk_scrolled_window_init.
424 * Called before possible change, save the old value.
425 */
426 static void Dw_gtk_scrolled_window_changed1 (GtkDwScrolledWindow *scrolled)
427 {
428 scrolled->old_vadjustment_value = scrolled->vadjustment->value;
429 }
430
431
432 /*
433 * See also Dw_gtk_scrolled_window_init.
434 * Called after possible change, compare old and new values.
435 */
436 static void Dw_gtk_scrolled_window_changed2 (GtkDwScrolledWindow *scrolled)
437 {
438 GtkWidget *viewport;
439
440 if (scrolled->old_vadjustment_value != scrolled->vadjustment->value) {
441 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
442 Dw_gtk_viewport_remove_anchor (GTK_DW_VIEWPORT (viewport));
443 }
444 }
445
446 /*
447 * Convenience function. See a_Findtext_search.
448 */
449 gboolean a_Dw_gtk_scrolled_window_search (GtkDwScrolledWindow *scrolled,
450 gchar *string,
451 gboolean case_sens)
452 {
453 GtkWidget *viewport;
454
455 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
456 return
457 a_Findtext_search (GTK_DW_VIEWPORT(viewport)->findtext_state, string,
458 case_sens);
459 }
460
461 /*
462 * Convenience function. See a_Findtext_reset_search.
463 */
464 void a_Dw_gtk_scrolled_window_reset_search (GtkDwScrolledWindow *scrolled)
465 {
466 GtkWidget *viewport;
467
468 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
469 a_Findtext_reset_search (GTK_DW_VIEWPORT(viewport)->findtext_state);
470 }
471
472 /*
473 * Convenience function. See a_Dw_gtk_viewport_widget_at_viewport_point.
474 */
475 DwWidget* a_Dw_gtk_scrolled_window_widget_at_viewport_point (
476 GtkDwScrolledWindow *scrolled,
477 gint32 vx,
478 gint32 vy)
479 {
480 GtkWidget *viewport;
481
482 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
483 return
484 a_Dw_gtk_viewport_widget_at_viewport_point (GTK_DW_VIEWPORT(viewport),
485 vx, vy);
486 }
0 #ifndef __DW_GTK_SCROLLED_WINDOW_H__
1 #define __DW_GTK_SCROLLED_WINDOW_H__
2
3 #include <gtk/gtkscrolledwindow.h>
4 #include "dw_widget.h"
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 #define GTK_TYPE_DW_SCROLLED_WINDOW (a_Dw_gtk_scrolled_window_get_type ())
12 #define GTK_DW_SCROLLED_WINDOW(obj) (GTK_CHECK_CAST (obj, \
13 GTK_TYPE_DW_SCROLLED_WINDOW, \
14 GtkDwScrolledWindow))
15 #define GTK_DW_SCROLLED_WINDOW_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, \
16 GTK_TYPE_DW_SCROLLED_WINDOW, \
17 GtkDwScrolledWindowClass))
18 #define GTK_IS_DW_SCROLLED_WINDOW(obj) GTK_CHECK_TYPE (obj, \
19 GTK_TYPE_DW_SCROLLED_WINDOW)
20
21
22 typedef struct _GtkDwScrolledWindow GtkDwScrolledWindow;
23 typedef struct _GtkDwScrolledWindowClass GtkDwScrolledWindowClass;
24
25
26 struct _GtkDwScrolledWindow
27 {
28 GtkScrolledWindow scrolled_window;
29
30 GtkWidget *gadget; /* This is a widget shown in the upper right
31 * corner, and in dillo used for the "full
32 * screen off" button. */
33 GtkAdjustment *vadjustment;
34 gfloat old_vadjustment_value;
35 };
36
37
38 struct _GtkDwScrolledWindowClass
39 {
40 GtkScrolledWindowClass parent_class;
41 };
42
43
44 GtkType a_Dw_gtk_scrolled_window_get_type (void);
45 GtkWidget* a_Dw_gtk_scrolled_window_new (void);
46 void a_Dw_gtk_scrolled_window_add_gadget (
47 GtkDwScrolledWindow *scrolled, GtkWidget *gadget);
48
49 void a_Dw_gtk_scrolled_window_set_dw (
50 GtkDwScrolledWindow *scrolled, DwWidget *widget);
51 DwWidget* a_Dw_gtk_scrolled_window_get_dw (GtkDwScrolledWindow *scrolled);
52
53 void a_Dw_gtk_scrolled_window_set_anchor (
54 GtkDwScrolledWindow *scrolled, const gchar *anchor);
55 gint a_Dw_gtk_scrolled_window_get_scrolling_position_x (
56 GtkDwScrolledWindow *scrolled);
57 gint a_Dw_gtk_scrolled_window_get_scrolling_position_y (
58 GtkDwScrolledWindow *scrolled);
59 void a_Dw_gtk_scrolled_window_set_scrolling_position (
60 GtkDwScrolledWindow *scrolled, gint32 x, gint32 y);
61
62 gboolean a_Dw_gtk_scrolled_window_search (
63 GtkDwScrolledWindow *scrolled, gchar *string,
64 gboolean case_sens);
65 void a_Dw_gtk_scrolled_window_reset_search (
66 GtkDwScrolledWindow *scrolled);
67
68 DwWidget* a_Dw_gtk_scrolled_window_widget_at_viewport_point (
69 GtkDwScrolledWindow *scrolled,
70 gint32 vx,
71 gint32 vy);
72
73 #ifdef __cplusplus
74 }
75 #endif /* __cplusplus */
76
77 #endif /* __DW_GTK_SCROLLED_WINDOW_H__ */
0 /*
1 * GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <string.h>
20 #include <gtk/gtklabel.h>
21
22 #include "msg.h"
23 #include "dw_gtk_statuslabel.h"
24
25
26 static void Dw_gtk_statuslabel_class_init(GtkLabelClass * klass);
27 static void Dw_gtk_statuslabel_size_request(GtkWidget * widget,
28 GtkRequisition * requisition);
29 static gint Dw_gtk_statuslabel_expose(GtkWidget * widget,
30 GdkEventExpose * event);
31
32
33 static GtkLabelClass *parent_class = NULL;
34
35 /* todo: provide a Dw_gtk_statuslabel_set function that doesn't do a
36 * check_resize. */
37
38 /*
39 * ?
40 */
41 guint Dw_gtk_statuslabel_get_type()
42 {
43 static guint statuslabel_type = 0;
44
45 if (!statuslabel_type) {
46 GtkTypeInfo statuslabel_info =
47 {
48 "GtkStatusLabel",
49 sizeof(GtkStatusLabel),
50 sizeof(GtkStatusLabelClass),
51 (GtkClassInitFunc) Dw_gtk_statuslabel_class_init,
52 (GtkObjectInitFunc) NULL,
53 (GtkArgSetFunc) NULL,
54 (GtkArgGetFunc) NULL,
55 (GtkClassInitFunc) NULL
56 };
57
58 statuslabel_type = gtk_type_unique(gtk_label_get_type(),
59 &statuslabel_info);
60 }
61 return statuslabel_type;
62 }
63
64 /*
65 * ?
66 */
67 static void Dw_gtk_statuslabel_class_init(GtkLabelClass * class)
68 {
69 GtkWidgetClass *widget_class;
70
71 widget_class = (GtkWidgetClass *) class;
72
73 parent_class = gtk_type_class(gtk_label_get_type());
74
75 widget_class->size_request = Dw_gtk_statuslabel_size_request;
76 widget_class->expose_event = Dw_gtk_statuslabel_expose;
77 }
78
79 /*
80 * ?
81 */
82 GtkWidget *a_Dw_gtk_statuslabel_new(char *str)
83 {
84 GtkLabel *label;
85
86 g_return_val_if_fail(str != NULL, NULL);
87
88 label = gtk_type_new(Dw_gtk_statuslabel_get_type());
89
90 gtk_label_set(label, str);
91
92 return GTK_WIDGET(label);
93 }
94
95 /*
96 * ?
97 */
98 static void Dw_gtk_statuslabel_size_request(GtkWidget * widget,
99 GtkRequisition * requisition)
100 {
101 GtkLabel *label;
102
103 g_return_if_fail(widget != NULL);
104 g_return_if_fail(GTK_IS_LABEL(widget));
105 g_return_if_fail(requisition != NULL);
106
107 label = GTK_LABEL(widget);
108
109 requisition->width = (100 +
110 label->misc.xpad * 2);
111 requisition->height = (GTK_WIDGET(label)->style->font->ascent +
112 GTK_WIDGET(label)->style->font->descent +
113 label->misc.ypad * 2 + 2);
114 }
115
116 /*
117 * ?
118 */
119 static void Dw_gtk_statuslabel_expose_string(GtkWidget * widget,
120 gint state,
121 char *string,
122 gint width)
123 {
124 gint x, y;
125 GtkMisc *misc;
126
127 misc = GTK_MISC(widget);
128 x = (widget->allocation.x * (1.0 - misc->xalign) +
129 (widget->allocation.x + widget->allocation.width - (width -
130 misc->xpad * 2)) *
131 misc->xalign) + 0.5;
132 y = (widget->allocation.y * (1.0 - misc->yalign) +
133 (widget->allocation.y + widget->allocation.height -
134 (widget->requisition.height - misc->ypad * 2)) * misc->yalign +
135 widget->style->font->ascent) + 1.5;
136
137 if (state == GTK_STATE_INSENSITIVE)
138 gdk_draw_string(widget->window,
139 widget->style->font,
140 widget->style->white_gc,
141 x + 1, y + 1, string);
142
143 /*
144 * gdk_draw_rectangle (widget->window,
145 * widget->style->bg_gc[GTK_STATE_SELECTED], FALSE,
146 * widget->allocation.x, widget->allocation.y,
147 * widget->allocation.width - 1, widget->allocation.height - 1);
148 */
149
150 gdk_draw_string(widget->window,
151 widget->style->font,
152 widget->style->fg_gc[state],
153 x, y, string);
154 }
155
156 /*
157 * ?
158 */
159 static gint Dw_gtk_statuslabel_expose(GtkWidget * widget,
160 GdkEventExpose * event)
161 {
162 GtkLabel *label;
163 GtkMisc *misc;
164 gint state;
165 gint width;
166 char *scratch_string;
167 gint len, num_elide, num_prefix;
168
169 g_return_val_if_fail(widget != NULL, FALSE);
170 g_return_val_if_fail(GTK_IS_LABEL(widget), FALSE);
171 g_return_val_if_fail(event != NULL, FALSE);
172
173 if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_MAPPED(widget)) {
174 label = GTK_LABEL(widget);
175 misc = GTK_MISC(widget);
176
177 state = widget->state;
178 if (!GTK_WIDGET_IS_SENSITIVE(widget))
179 state = GTK_STATE_INSENSITIVE;
180
181 width = gdk_string_width(widget->style->font,
182 label->label) +
183 misc->xpad * 2;
184 /* If the label fits in the space allocated, we draw it. If not,
185 * we (somehow) truncate. */
186 if ((widget->allocation.width >= width) &&
187 (widget->allocation.height >= widget->requisition.height)) {
188 Dw_gtk_statuslabel_expose_string(widget, state, label->label, width);
189 } else if (widget->allocation.height >= widget->requisition.height) {
190 /* enough height, but not enough width - truncate */
191 /* I know this algorithm is a bit inefficient, but I seriously
192 * doubt it matters. */
193 len = strlen(label->label);
194 scratch_string = g_new(char, len + 3);
195
196 scratch_string[0] = '\0';
197 for (num_elide = 1; num_elide < len; num_elide++) {
198 num_prefix = (len - num_elide) / 2;
199 memcpy(scratch_string, label->label, num_prefix);
200 scratch_string[num_prefix] = '.';
201 scratch_string[num_prefix + 1] = '.';
202 scratch_string[num_prefix + 2] = '.';
203 memcpy(scratch_string + num_prefix + 3,
204 label->label + num_elide + num_prefix,
205 len + 1 - (num_elide + num_prefix));
206 width = gdk_string_width(widget->style->font, scratch_string) +
207 misc->xpad * 2;
208 if (widget->allocation.width >= width)
209 break;
210 }
211 Dw_gtk_statuslabel_expose_string(widget, state, scratch_string,width);
212 g_free(scratch_string);
213 } else {
214 _MSG("Dw_gtk_statuslabel_expose:"
215 " allocation too small: %d %d ( %d %d )\n",
216 widget->allocation.width, widget->allocation.height,
217 widget->requisition.width, widget->requisition.height);
218 }
219 }
220 return TRUE;
221 }
0 /* GTK - The GIMP Toolkit
1 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17 #ifndef __GTK_STATUSLABEL_H__
18 #define __GTK_STATUSLABEL_H__
19
20
21 #include <gdk/gdk.h>
22 #include <gtk/gtkmisc.h>
23
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif /* __cplusplus */
28
29
30 #define GTK_STATUSLABEL(obj) \
31 GTK_CHECK_CAST (obj, Dw_gtk_statuslabel_get_type (), GtkLabel)
32
33 #define GTK_STATUSLABEL_CLASS(klass) \
34 GTK_CHECK_CLASS_CAST (klass, Dw_gtk_statuslabel_get_type (), GtkLabelClass)
35
36 #define GTK_IS_STATUSLABEL(obj) \
37 GTK_CHECK_TYPE (obj, Dw_gtk_statuslabel_get_type ())
38
39
40 typedef struct _GtkStatusLabel GtkStatusLabel;
41 typedef struct _GtkStatusLabelClass GtkStatusLabelClass;
42
43 struct _GtkStatusLabel
44 {
45 GtkLabel label;
46 };
47
48 struct _GtkStatusLabelClass
49 {
50 GtkMiscClass parent_class;
51 };
52
53
54 guint Dw_gtk_statuslabel_get_type (void);
55 GtkWidget* a_Dw_gtk_statuslabel_new (char *str);
56
57
58 #ifdef __cplusplus
59 }
60 #endif /* __cplusplus */
61
62
63 #endif /* __GTK_STATUSLABEL_H__ */
0 /*
1 * File: dw_gtk_viewport.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <gtk/gtk.h>
12 #include "msg.h"
13 #include "dw_gtk_viewport.h"
14 #include "dw_container.h"
15 #include "list.h"
16
17 /*#define DEBUG_LEVEL 1*/
18 #include "debug.h"
19 #include "msg.h"
20
21 typedef struct
22 {
23 gchar *name;
24 DwWidget *widget;
25 gint32 y;
26 } GtkDwViewportAnchor;
27
28 static GtkLayoutClass *parent_class = NULL;
29
30 /* object/class initialisation */
31 static void Dw_gtk_viewport_init (GtkDwViewport *viewport);
32 static void Dw_gtk_viewport_class_init (GtkDwViewportClass *klass);
33
34 /* GtkObject methods */
35 static void Dw_gtk_viewport_destroy (GtkObject *object);
36
37 /* GtkWidget methods */
38 static void Dw_gtk_viewport_size_allocate (GtkWidget *widget,
39 GtkAllocation *allocation);
40 static void Dw_gtk_viewport_realize (GtkWidget *widget);
41 static void Dw_gtk_viewport_draw (GtkWidget *widget,
42 GdkRectangle *area);
43 static gint Dw_gtk_viewport_expose (GtkWidget *widget,
44 GdkEventExpose *event);
45 static gint Dw_gtk_viewport_button_press (GtkWidget *widget,
46 GdkEventButton *event);
47 static gint Dw_gtk_viewport_button_release(GtkWidget *widget,
48 GdkEventButton *event);
49 static gint Dw_gtk_viewport_motion_notify (GtkWidget *widget,
50 GdkEventMotion *event);
51 static gint Dw_gtk_viewport_enter_notify (GtkWidget *widget,
52 GdkEventCrossing *event);
53 static gint Dw_gtk_viewport_leave_notify (GtkWidget *widget,
54 GdkEventCrossing *event);
55 static void Dw_gtk_viewport_adj_changed (GtkAdjustment *adj,
56 GtkDwViewport *viewport);
57
58 /*
59 * Standard Gtk+ function
60 */
61 GtkType a_Dw_gtk_viewport_get_type (void)
62 {
63 static GtkType type = 0;
64
65 if (!type) {
66 GtkTypeInfo info = {
67 "GtkDwViewport",
68 sizeof (GtkDwViewport),
69 sizeof (GtkDwViewportClass),
70 (GtkClassInitFunc) Dw_gtk_viewport_class_init,
71 (GtkObjectInitFunc) Dw_gtk_viewport_init,
72 (GtkArgSetFunc) NULL,
73 (GtkArgGetFunc) NULL,
74 (GtkClassInitFunc) NULL
75 };
76
77 type = gtk_type_unique (GTK_TYPE_LAYOUT, &info);
78 }
79
80 return type;
81 }
82
83
84 /*
85 * Standard Gtk+ function
86 */
87 GtkWidget* a_Dw_gtk_viewport_new (GtkAdjustment *hadjustment,
88 GtkAdjustment *vadjustment)
89 {
90 GtkWidget *widget;
91
92 widget = gtk_widget_new (GTK_TYPE_DW_VIEWPORT, NULL);
93 gtk_layout_set_hadjustment (GTK_LAYOUT (widget), hadjustment);
94 gtk_layout_set_vadjustment (GTK_LAYOUT (widget), vadjustment);
95
96 /* Following two statements expect that the adjustments are passed as
97 * arguments (!= NULL), and don't change. This is the case in dillo,
98 * however, for more general perposes, the signal function
99 * "set_scroll_adjustments" had to be redefined.
100 */
101 gtk_signal_connect (GTK_OBJECT (hadjustment), "value_changed",
102 GTK_SIGNAL_FUNC (Dw_gtk_viewport_adj_changed),
103 (gpointer) widget);
104 gtk_signal_connect (GTK_OBJECT (vadjustment), "value_changed",
105 GTK_SIGNAL_FUNC (Dw_gtk_viewport_adj_changed),
106 (gpointer) widget);
107
108 return widget;
109 }
110
111 /*********************************
112 * *
113 * object/class initialisation *
114 * *
115 *********************************/
116
117 /*
118 * Standard Gtk+ function
119 */
120 static void Dw_gtk_viewport_init (GtkDwViewport *viewport)
121 {
122 DBG_OBJ_CREATE (viewport, "GtkDwViewport");
123
124 GTK_WIDGET_UNSET_FLAGS (viewport, GTK_NO_WINDOW);
125 GTK_WIDGET_UNSET_FLAGS (viewport, GTK_CAN_FOCUS);
126
127 /* Without this, gtk_layout_{draw|expose} will clear the window.
128 Look at gtklayout.c */
129 GTK_WIDGET_SET_FLAGS (viewport, GTK_APP_PAINTABLE);
130
131 viewport->back_pixmap = NULL;
132 viewport->child = NULL;
133 viewport->last_entered = NULL;
134 viewport->draw_resize_idle_id = 0;
135 viewport->anchor = NULL;
136 viewport->anchor_idle_id = 0;
137 viewport->findtext_state = a_Findtext_state_new ();
138 viewport->selection = a_Selection_new ();
139 viewport->anchors_table = g_hash_table_new (g_str_hash, g_str_equal);
140 viewport->draw_areas = NULL;
141 viewport->num_draw_areas = 0;
142 viewport->num_draw_areas_max = 4;
143
144 DBG_OBJ_ASSOC (viewport->findtext_state, viewport);
145 DBG_OBJ_ASSOC (viewport->selection, viewport);
146 }
147
148
149 /*
150 * Standard Gtk+ function
151 */
152 static void Dw_gtk_viewport_class_init (GtkDwViewportClass *klass)
153 {
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
156
157 parent_class = gtk_type_class (gtk_layout_get_type ());
158
159 object_class = (GtkObjectClass*) klass;
160 widget_class = (GtkWidgetClass*) klass;
161
162 object_class->destroy = Dw_gtk_viewport_destroy;
163
164 widget_class->size_allocate = Dw_gtk_viewport_size_allocate;
165 widget_class->realize = Dw_gtk_viewport_realize;
166 widget_class->draw = Dw_gtk_viewport_draw;
167 widget_class->expose_event = Dw_gtk_viewport_expose;
168 widget_class->button_press_event = Dw_gtk_viewport_button_press;
169 widget_class->button_release_event = Dw_gtk_viewport_button_release;
170 widget_class->motion_notify_event = Dw_gtk_viewport_motion_notify;
171 widget_class->enter_notify_event = Dw_gtk_viewport_enter_notify;
172 widget_class->leave_notify_event = Dw_gtk_viewport_leave_notify;
173 }
174
175
176 /***********************
177 * *
178 * GtkObject methods *
179 * *
180 ***********************/
181
182 static gboolean Dw_gtk_viewport_destroy_anchor (gpointer key,
183 gpointer value,
184 gpointer user_data)
185 {
186 g_free (value);
187 return TRUE;
188 }
189
190 /*
191 * Standard Gtk+ function
192 */
193 static void Dw_gtk_viewport_destroy (GtkObject *object)
194 {
195 GtkDwViewport *viewport;
196
197 viewport = GTK_DW_VIEWPORT (object);
198
199 if (viewport->back_pixmap)
200 gdk_pixmap_unref (viewport->back_pixmap);
201 if (viewport->child)
202 gtk_object_destroy (GTK_OBJECT (viewport->child));
203 if (viewport->draw_resize_idle_id != 0)
204 gtk_idle_remove (viewport->draw_resize_idle_id);
205 if (viewport->anchor_idle_id != 0)
206 gtk_idle_remove (viewport->anchor_idle_id);
207 g_free (viewport->anchor);
208
209 g_hash_table_foreach_remove (viewport->anchors_table,
210 Dw_gtk_viewport_destroy_anchor, NULL);
211 g_hash_table_destroy (viewport->anchors_table);
212
213 g_free (viewport->draw_areas);
214
215 a_Findtext_state_destroy (viewport->findtext_state);
216 a_Selection_free (viewport->selection);
217
218 GTK_OBJECT_CLASS(parent_class)->destroy (object);
219 }
220
221
222 /***********************
223 * *
224 * GtkWidget methods *
225 * *
226 ***********************/
227
228 /*
229 * Standard Gtk+ function
230 */
231 static void Dw_gtk_viewport_size_allocate (GtkWidget *widget,
232 GtkAllocation *allocation)
233 {
234 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
235
236 /* gtk_layout_size_allocate() sets them to different values. */
237 GTK_LAYOUT(widget)->hadjustment->page_increment = allocation->width;
238 GTK_LAYOUT(widget)->vadjustment->page_increment = 0.96 * allocation->height;
239 }
240
241 /*
242 * Standard Gtk+ function
243 */
244 static void Dw_gtk_viewport_realize (GtkWidget *widget)
245 {
246 GtkDwViewport *viewport;
247
248 GTK_WIDGET_CLASS(parent_class)->realize (widget);
249
250 gdk_window_set_events (widget->window,
251 gdk_window_get_events (widget->window)
252 | GDK_BUTTON_PRESS_MASK
253 | GDK_BUTTON_RELEASE_MASK
254 | GDK_POINTER_MOTION_MASK
255 | GDK_ENTER_NOTIFY_MASK
256 | GDK_LEAVE_NOTIFY_MASK);
257
258 viewport = GTK_DW_VIEWPORT (widget);
259 gdk_window_get_geometry (widget->window, NULL, NULL, NULL, NULL,
260 &viewport->depth);
261
262 Dw_gtk_viewport_update_background (viewport);
263 if (viewport->child)
264 Dw_widget_update_cursor (viewport->child);
265 }
266
267
268 /*
269 * (Nearly) standard Gtk+ function
270 */
271 static void Dw_gtk_viewport_paint (GtkWidget *widget,
272 GdkRectangle *area,
273 GdkEventExpose *event)
274 {
275 GtkLayout *layout;
276 DwRectangle parent_area, child_area, intersection;
277 GtkDwViewport *viewport;
278 gboolean new_back_pixmap;
279
280 if (GTK_WIDGET_DRAWABLE (widget)) {
281 layout = GTK_LAYOUT (widget);
282 viewport = GTK_DW_VIEWPORT (widget);
283
284 DEBUG_MSG (2, "Drawing (%d, %d), %d x %d\n",
285 area->x, area->y, area->width, area->height);
286
287 /* Make sure the backing pixmap is large enough. */
288 if (viewport->child) {
289 if (viewport->back_pixmap)
290 new_back_pixmap =
291 (widget->allocation.width > viewport->back_width ||
292 widget->allocation.height > viewport->back_height);
293 else
294 new_back_pixmap = TRUE;
295
296 if (new_back_pixmap) {
297 if (viewport->back_pixmap)
298 gdk_pixmap_unref (viewport->back_pixmap);
299 viewport->back_pixmap = gdk_pixmap_new (widget->window,
300 widget->allocation.width,
301 widget->allocation.height,
302 viewport->depth);
303 viewport->back_width = widget->allocation.width;
304 viewport->back_height = widget->allocation.height;
305 DEBUG_MSG (1, " Creating new pixmap, size = %d x %d\n",
306 widget->allocation.width, widget->allocation.height);
307 }
308
309 /* Draw top-level Dw widget. */
310 parent_area.x =
311 p_Dw_widget_x_viewport_to_world (viewport->child, area->x);
312 parent_area.y =
313 p_Dw_widget_y_viewport_to_world (viewport->child, area->y);
314 parent_area.width = area->width;
315 parent_area.height = area->height;
316
317 child_area.x = viewport->child->allocation.x;
318 child_area.y = viewport->child->allocation.y;
319 child_area.width = viewport->child->allocation.width;
320 child_area.height = DW_WIDGET_HEIGHT(viewport->child);
321
322 if (p_Dw_rectangle_intersect (&parent_area, &child_area,
323 &intersection)) {
324 intersection.x -= viewport->child->allocation.x;
325 intersection.y -= viewport->child->allocation.y;
326
327 /* "Clear" backing pixmap. */
328 gdk_draw_rectangle (viewport->back_pixmap,
329 viewport->child->style->background_color->gc,
330 TRUE, area->x, area->y,
331 area->width, area->height);
332 /* Widgets draw in backing pixmap. */
333 p_Dw_widget_draw (viewport->child, &intersection, event);
334 /* Copy backing pixmap into window. */
335 gdk_draw_pixmap (layout->bin_window, widget->style->black_gc,
336 viewport->back_pixmap, area->x, area->y,
337 area->x, area->y, area->width, area->height);
338 }
339 } else
340 gdk_window_clear_area (layout->bin_window,
341 area->x, area->y, area->width, area->height);
342 }
343 }
344
345
346 /*
347 * Standard Gtk+ function
348 */
349 static void Dw_gtk_viewport_draw (GtkWidget *widget,
350 GdkRectangle *area)
351 {
352 Dw_gtk_viewport_paint (widget, area, NULL);
353 GTK_WIDGET_CLASS(parent_class)->draw (widget, area);
354 }
355
356
357 /*
358 * Standard Gtk+ function
359 */
360 static gint Dw_gtk_viewport_expose (GtkWidget *widget,
361 GdkEventExpose *event)
362 {
363 Dw_gtk_viewport_paint (widget, &(event->area), event);
364 return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
365 }
366
367
368 /*
369 * Handle the mouse event and deliver it to the Dw widget.
370 * Most is done in a_Dw_widget_mouse_event.
371 */
372 static gint Dw_gtk_viewport_mouse_event (GtkWidget *widget,
373 gint32 x,
374 gint32 y,
375 GdkEvent *event)
376 {
377 GtkDwViewport *viewport;
378 DwWidget *dw_widget;
379 gint32 world_x, world_y;
380
381 if (event == NULL || event->any.window == widget->window) {
382 viewport = GTK_DW_VIEWPORT (widget);
383 if (viewport->child) {
384 world_x = x + gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
385 world_y = y + gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
386 dw_widget =
387 Dw_gtk_viewport_widget_at_point (viewport, world_x, world_y);
388
389 return Dw_widget_mouse_event (dw_widget, widget,
390 world_x, world_y, event);
391 }
392 }
393
394 return FALSE;
395 }
396
397
398 /*
399 * Standard Gtk+ function
400 */
401 static gint Dw_gtk_viewport_button_press (GtkWidget *widget,
402 GdkEventButton *event)
403 {
404 /* We focus always the viewport window (i.e., more precisely, the
405 * GktDwScrolledFrame). */
406 if (widget->parent)
407 gtk_widget_grab_focus(widget->parent);
408
409 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y,
410 (GdkEvent*) event);
411 }
412
413
414 /*
415 * Standard Gtk+ function
416 */
417 static gint Dw_gtk_viewport_button_release (GtkWidget *widget,
418 GdkEventButton *event)
419 {
420 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y,
421 (GdkEvent*) event);
422 }
423
424
425 /*
426 * Standard Gtk+ function
427 */
428 static gint Dw_gtk_viewport_motion_notify (GtkWidget *widget,
429 GdkEventMotion *event)
430 {
431 GtkDwViewport *viewport = GTK_DW_VIEWPORT (widget);
432
433 viewport->mouse_x = event->x;
434 viewport->mouse_y = event->y;
435 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y,
436 (GdkEvent*) event);
437 }
438
439
440 /*
441 * Standard Gtk+ function
442 */
443 static gint Dw_gtk_viewport_enter_notify (GtkWidget *widget,
444 GdkEventCrossing *event)
445 {
446 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y, NULL);
447 }
448
449
450 /*
451 * Standard Gtk+ function
452 */
453 static gint Dw_gtk_viewport_leave_notify (GtkWidget *widget,
454 GdkEventCrossing *event)
455 {
456 /* There will anyway be no Dw widget, thus this simple call */
457 return Dw_widget_mouse_event (NULL, widget, 0, 0, NULL);
458 }
459
460
461 /*
462 * This function is called when the viewport changes, and causes
463 * motion_notify events to be simulated.
464 */
465 static void Dw_gtk_viewport_adj_changed (GtkAdjustment *adj,
466 GtkDwViewport *viewport)
467 {
468 Dw_gtk_viewport_mouse_event (GTK_WIDGET (viewport),
469 viewport->mouse_x, viewport->mouse_y, NULL);
470 }
471
472 /*
473 * This function sets the background and the viewport.
474 */
475 void Dw_gtk_viewport_update_background (GtkDwViewport *viewport)
476 {
477 /* The toplevel widget should always have a defined background color,
478 * except at the beginning. Searching a defined background is not
479 * necessary. */
480 if (viewport->child && viewport->child->style &&
481 viewport->child->style->background_color)
482 gdk_window_set_background (GTK_LAYOUT(viewport)->bin_window,
483 &viewport->child->style->background_color
484 ->color);
485 }
486
487 /**********************
488 * *
489 * public functions *
490 * *
491 **********************/
492
493 /*
494 * Set the top-level Dw widget.
495 * If there is already one, you must destroy it before, otherwise the
496 * function will fail.
497 */
498 void a_Dw_gtk_viewport_add_dw (GtkDwViewport *viewport,
499 DwWidget *widget)
500 {
501 g_return_if_fail (viewport->child == NULL);
502
503 viewport->child = widget;
504 DBG_OBJ_ASSOC(widget, viewport);
505
506 widget->parent = NULL;
507 widget->viewport = GTK_WIDGET (viewport);
508
509 Dw_gtk_viewport_update_background (viewport);
510 Dw_widget_update_cursor (viewport->child);
511
512 Dw_gtk_viewport_calc_size (viewport);
513 Dw_gtk_viewport_remove_anchor (viewport);
514
515 a_Findtext_state_set_widget (viewport->findtext_state, widget);
516 a_Selection_reset (viewport->selection);
517 }
518
519 /**************************************************
520 * *
521 * Functions used by GtkDwViewport and DwWidget *
522 * *
523 **************************************************/
524
525 /*
526 * This function only *recognizes* that the top-level Dw widget has
527 * been destroyed, and only removes it from the viewport. It is called
528 * by Dw_widget_destroy. Don't use this function directly!
529 *
530 * N.b.: a widget, which stores anchors, should already have removed
531 * them from the widget, by calling a_Dw_gtk_viewport_remove_anchor().
532 * A good place for this is the implementation of GtkObject::destroy,
533 * the call to the parent destroy method should then be done at the
534 * end of the function, since Dw_widget_destroy calls this function.
535 */
536 void Dw_gtk_viewport_remove_dw (GtkDwViewport *viewport)
537 {
538 /* Test, that all anchors have been removed properly. */
539 gint num_anchors_left = g_hash_table_size (viewport->anchors_table);
540 /* g_assert (num_anchors_left == 0); */
541 if (num_anchors_left != 0)
542 g_warning ("%d anchors left", num_anchors_left);
543
544 a_Findtext_state_set_widget (viewport->findtext_state, NULL);
545 viewport->child = NULL;
546 Dw_gtk_viewport_remove_anchor (viewport);
547 Dw_gtk_viewport_calc_size (viewport);
548 }
549
550
551 /*
552 * Used by Dw_gtk_viewport_calc_size.
553 */
554 static void Dw_gtk_viewport_calc_child_size (GtkDwViewport *viewport,
555 gint32 child_width,
556 gint32 child_height,
557 DwRequisition *child_requisition)
558 {
559 if (child_width < 0) child_width = 0;
560 if (child_height < 0) child_height = 0;
561
562 DEBUG_MSG (2, " width = %d, height = %d ...\n",
563 child_width, child_height);
564
565 p_Dw_widget_set_width (viewport->child, child_width);
566 p_Dw_widget_set_ascent (viewport->child, child_height);
567 p_Dw_widget_set_descent (viewport->child, 0);
568
569 p_Dw_widget_size_request (viewport->child, child_requisition);
570 }
571
572
573 /*
574 * Calculate the size of the scrolled area and allocate the top-level
575 * widget. This function is called when the top-level Dw widget has
576 * changed its size etc.
577 */
578 void Dw_gtk_viewport_calc_size (GtkDwViewport *viewport)
579 {
580 GtkWidget *widget;
581 GtkScrolledWindow *scrolled;
582
583 DwRequisition child_requisition;
584 DwAllocation child_allocation;
585 gint border_width, space;
586
587 GtkRequisition bar_requisition;
588 gint max_width, max_height, bar_width_diff, bar_height_diff, child_height;
589
590 if (viewport->calc_size_blocked)
591 return;
592
593 viewport->calc_size_blocked = TRUE;
594
595 if (viewport->child) {
596 /*
597 * Determine the size hints for the Dw widget. This is a bit
598 * tricky, because you must know if scrollbars are visible or
599 * not, which depends on the size of the Dw widget, which then
600 * depends on the hints. The idea is to test several
601 * configurations, there are four of them, from combining the
602 * cases horizontal/vertical scrollbar visible/invisible.
603 *
604 * For optimization, the horizontal scrollbar is currently not
605 * regarded, the height hint is always the same, as if the
606 * scrollbar was allways visible. In future, this may be
607 * implemented correctly, by using the minimal width to optimize
608 * most cases. (Minimal widths will also be used by tables.)
609 *
610 * Furthermore, the last result (vertical scrollbar visible or
611 * not) is stored in the viewport, and tested first. This will
612 * make a second test only necessary when the visibility
613 * switches, which normally happens only once when filling the
614 * page with text. (Actually, this assumes that the page size is
615 * always *growing*, but this is nevertheless true in dillo.)
616 */
617
618 widget = GTK_WIDGET (viewport);
619 scrolled = GTK_SCROLLED_WINDOW (widget->parent->parent);
620 space = GTK_SCROLLED_WINDOW_CLASS(GTK_OBJECT(scrolled)->klass)
621 ->scrollbar_spacing;
622 border_width = GTK_CONTAINER(viewport)->border_width;
623
624 gtk_widget_size_request (scrolled->vscrollbar, &bar_requisition);
625 bar_width_diff = bar_requisition.width + space;
626 max_width = widget->allocation.width - 2 * border_width;
627 if (scrolled->vscrollbar_visible)
628 max_width += bar_width_diff;
629
630 gtk_widget_size_request (scrolled->hscrollbar, &bar_requisition);
631 bar_height_diff = bar_requisition.height + space;
632 max_height = widget->allocation.height - 2 * border_width;
633 if (scrolled->hscrollbar_visible)
634 max_height += bar_height_diff;
635
636 DEBUG_MSG (2, "------------------------------------------------->\n");
637 DEBUG_MSG (2, "Dw_gtk_viewport_calc_size: %d x %d (%c/%c) -> %d x %d\n",
638 widget->allocation.width, widget->allocation.height,
639 scrolled->vscrollbar_visible ? 't' : 'f',
640 scrolled->hscrollbar_visible ? 't' : 'f',
641 max_width, max_height);
642
643 if (scrolled->vscrollbar_policy == GTK_POLICY_NEVER)
644 child_height = max_height;
645 else
646 child_height = max_height - bar_height_diff;
647
648 switch (scrolled->vscrollbar_policy) {
649 case GTK_POLICY_ALWAYS:
650 Dw_gtk_viewport_calc_child_size (viewport, max_width - bar_width_diff,
651 child_height,
652 &child_requisition);
653 break;
654
655 case GTK_POLICY_AUTOMATIC:
656 if (viewport->vscrollbar_used) {
657 DEBUG_MSG (2, "Testing with vertical scrollbar ...\n");
658 Dw_gtk_viewport_calc_child_size (viewport,
659 max_width - bar_width_diff,
660 child_height,
661 &child_requisition);
662
663 if (child_requisition.ascent
664 + child_requisition.descent <= child_height) {
665 DEBUG_MSG (2, " failed!\n");
666 Dw_gtk_viewport_calc_child_size (viewport, max_width,
667 child_height,
668 &child_requisition);
669 viewport->vscrollbar_used = TRUE;
670 }
671
672 } else {
673 DEBUG_MSG (2, "Testing without vertical scrollbar ...\n");
674 Dw_gtk_viewport_calc_child_size (viewport, max_width,
675 child_height,
676 &child_requisition);
677
678 /* todo: see above */
679 if (child_requisition.ascent
680 + child_requisition.descent > child_height) {
681 DEBUG_MSG (2, " failed!\n");
682 Dw_gtk_viewport_calc_child_size (viewport,
683 max_width - bar_width_diff,
684 child_height,
685 &child_requisition);
686 viewport->vscrollbar_used = TRUE;
687 }
688 }
689 break;
690
691 case GTK_POLICY_NEVER:
692 Dw_gtk_viewport_calc_child_size (viewport, max_width,
693 child_height,
694 &child_requisition);
695 }
696
697 child_allocation.x = border_width;
698 child_allocation.y = border_width;
699 child_allocation.width = child_requisition.width;
700 child_allocation.ascent = child_requisition.ascent;
701 child_allocation.descent = child_requisition.descent;
702 p_Dw_widget_size_allocate (viewport->child, &child_allocation);
703
704 gtk_layout_set_size (GTK_LAYOUT (viewport),
705 child_requisition.width + 2 * border_width,
706 child_requisition.ascent + child_requisition.descent
707 + 2 * border_width);
708
709 DEBUG_MSG (1, "Setting size to %d x %d\n",
710 child_requisition.width + 2 * border_width,
711 child_requisition.ascent + child_requisition.descent
712 + 2 * border_width);
713
714 DEBUG_MSG (2, "<-------------------------------------------------\n");
715 } else {
716 gtk_layout_set_size (GTK_LAYOUT (viewport), 1, 1);
717 viewport->hscrollbar_used = FALSE;
718 viewport->vscrollbar_used = FALSE;
719 }
720
721 Dw_gtk_viewport_update_anchor (viewport);
722 gtk_widget_queue_draw (GTK_WIDGET (viewport));
723
724 viewport->calc_size_blocked = FALSE;
725 }
726
727
728 /* used by Dw_gtk_viewport_widget_at_point */
729 typedef struct
730 {
731 gint32 x;
732 gint32 y;
733 DwWidget *widget;
734 } WidgetAtPointData;
735
736 /* used by Dw_gtk_viewport_widget_at_point */
737 static void Dw_gtk_viewport_widget_at_point_callback (DwWidget *widget,
738 gpointer data)
739 {
740 WidgetAtPointData *callback_data;
741
742 callback_data = (WidgetAtPointData*) data;
743 DEBUG_MSG (1, " Checking %p ...\n", widget);
744
745 if (/* As a special exception, for the top-level widget, not the
746 * allocation is regarded, but the whole viewport. This makes
747 * selections more useful, since so the user can start the
748 * selection outside of the allocation. */
749 widget->parent == NULL ||
750 /* Otherwise, check whether pointer is in the allocation. */
751 (callback_data->x >= widget->allocation.x &&
752 callback_data->y >= widget->allocation.y &&
753 callback_data->x < widget->allocation.x + widget->allocation.width &&
754 callback_data->y < widget->allocation.y + DW_WIDGET_HEIGHT(widget))) {
755 DEBUG_MSG (1, " yes\n");
756 if (DW_IS_CONTAINER (widget))
757 a_Dw_container_forall (DW_CONTAINER (widget),
758 Dw_gtk_viewport_widget_at_point_callback,
759 data);
760
761 if (callback_data->widget == NULL)
762 callback_data->widget = widget;
763 }
764 }
765
766 /*
767 * Return the widget at point (x, y) (world coordinates).
768 */
769 DwWidget* Dw_gtk_viewport_widget_at_point (GtkDwViewport *viewport,
770 gint32 x,
771 gint32 y)
772 {
773 WidgetAtPointData callback_data;
774
775 callback_data.x = x;
776 callback_data.y = y;
777 callback_data.widget = NULL;
778
779 if (viewport->child)
780 Dw_gtk_viewport_widget_at_point_callback (viewport->child,
781 &callback_data);
782
783 return callback_data.widget;
784 }
785
786
787 /*************
788 * *
789 * Anchors *
790 * *
791 *************/
792
793 /*
794 * todo: Currently, no horizontal scrolling is done. This is generally
795 * possible, DW_HPOS_INTO_VIEW should be used for this, but it is
796 * rather complicated to determine the width of an anchor. This would
797 * be the lenght of the region between <a> and </a>, the page widget
798 * would have to have two kinds of content types (opening and closing
799 * anchor), and some changes in the HTML parser are necessary.
800 */
801
802 /*
803 * Add an anchor, and assign a position for it. For all widgets
804 * directly or indirectly assigned to a viewports, anchors must be
805 * unique, this is tested. "name" is copied, so no strdup is
806 * neccessary for the caller.
807 *
808 * Return the copy on success, or NULL, when this anchor had already
809 * been added to the widget tree.
810 *
811 * The viewport gets the responsibility to free "name".
812 */
813 gchar* p_Dw_gtk_viewport_add_anchor (DwWidget *widget,
814 const gchar *name,
815 gint32 y)
816 {
817 GtkDwViewport *viewport;
818 GtkDwViewportAnchor *anchor;
819
820 _MSG("new anchor %p/'%s' -> %d\n", widget, name, y);
821
822 g_return_val_if_fail (widget->viewport != NULL, NULL);
823 viewport = GTK_DW_VIEWPORT (widget->viewport);
824
825 if (g_hash_table_lookup_extended (viewport->anchors_table, name, NULL,NULL))
826 /* Anchor does already exist. */
827 return NULL;
828 else {
829 anchor = g_new (GtkDwViewportAnchor, 1);
830 anchor->name = g_strdup (name);
831 anchor->widget = widget;
832 anchor->y = y;
833
834 g_hash_table_insert (viewport->anchors_table, anchor->name, anchor);
835 Dw_gtk_viewport_update_anchor (viewport);
836
837 return anchor->name;
838 }
839 }
840
841
842 /*
843 * Assign a position for an already existing anchor.
844 */
845 void p_Dw_gtk_viewport_change_anchor (DwWidget *widget,
846 gchar *name,
847 gint32 y)
848 {
849 GtkDwViewport *viewport;
850 GtkDwViewportAnchor *anchor;
851 gpointer tmp_anchor;
852 gboolean exists;
853
854 _MSG("changing anchor %p/'%s' -> %d\n", widget, name, y);
855
856 g_return_if_fail (widget->viewport != NULL);
857 viewport = GTK_DW_VIEWPORT (widget->viewport);
858
859 exists =
860 g_hash_table_lookup_extended (viewport->anchors_table, name, NULL,
861 &tmp_anchor);
862 g_return_if_fail(exists);
863
864 anchor = tmp_anchor;
865 g_return_if_fail(anchor->widget == widget);
866
867 anchor->y = y;
868
869 Dw_gtk_viewport_update_anchor (viewport);
870 }
871
872
873 /*
874 * Remove an anchor from the table in the viewport. Notice that "name"
875 * is freed here.
876 */
877 void p_Dw_gtk_viewport_remove_anchor (DwWidget *widget,
878 gchar *name)
879 {
880 GtkDwViewport *viewport;
881 GtkDwViewportAnchor *anchor;
882 gpointer tmp_anchor;
883 gboolean exists;
884
885 _MSG("removing anchor %p/'%s'\n", widget, name);
886
887 g_return_if_fail (widget->viewport != NULL);
888 viewport = GTK_DW_VIEWPORT (widget->viewport);
889
890 exists =
891 g_hash_table_lookup_extended (viewport->anchors_table, name, NULL,
892 &tmp_anchor);
893 g_return_if_fail(exists);
894
895 anchor = tmp_anchor;
896 g_return_if_fail(anchor->widget == widget);
897
898 g_hash_table_remove (viewport->anchors_table, name);
899 g_free (anchor->name);
900 g_free (anchor);
901 }
902
903
904 /*
905 * Used by Dw_gtk_viewport_update_anchor_idle.
906 */
907 static gboolean Dw_gtk_viewport_calc_into (gint32 requested_value,
908 gint32 requested_size,
909 gint32 current_value,
910 gint32 size,
911 gint32 *return_value)
912 {
913 if (requested_size > size) {
914 /* The viewport size is smaller than the size of the region which will
915 * be shown. If the region is already visible, do not change the
916 * position. Otherwise, show the left/upper border, this is most likely
917 * what is needed. */
918 if (current_value >= requested_value &&
919 current_value + size < requested_value + requested_size)
920 return FALSE;
921 else
922 requested_size = size;
923 }
924
925 if (requested_value < current_value) {
926 *return_value = requested_value;
927 return TRUE;
928 } else if (requested_value + requested_size > current_value + size) {
929 *return_value = requested_value - size + requested_size;
930 return TRUE;
931 } else
932 return FALSE;
933 }
934
935 /*
936 * See Dw_gtk_viewport_scroll_to.
937 */
938 static gint Dw_gtk_viewport_update_anchor_idle (gpointer data)
939 {
940 gint32 vp_width, vp_height, x = 0, y = 0;
941 GtkScrolledWindow *scrolled;
942 GtkDwViewport *viewport;
943 GtkWidget *vwidget;
944 GtkAdjustment *vadj, *hadj;
945 gboolean change_x, change_y;
946
947 DBG_MSG (data, "scrolling", 0, "Dw_gtk_viewport_update_anchor_idle");
948 DBG_MSG_START (data);
949
950 vwidget = GTK_WIDGET (data);
951 viewport = GTK_DW_VIEWPORT (vwidget);
952 scrolled = GTK_SCROLLED_WINDOW (vwidget->parent->parent);
953 hadj = GTK_LAYOUT(viewport)->hadjustment;
954 vadj = GTK_LAYOUT(viewport)->vadjustment;
955
956 vp_width =
957 vwidget->allocation.width - GTK_CONTAINER(viewport)->border_width;
958 vp_height =
959 vwidget->allocation.height - GTK_CONTAINER(viewport)->border_width;
960 DBG_MSGF (viewport, "scrolling", 0, "vp_width = %d", vp_width);
961 DBG_MSGF (viewport, "scrolling", 0, "vp_height = %d", vp_height);
962
963 change_x = TRUE;
964 switch (viewport->anchor_pos.hpos) {
965 case DW_HPOS_LEFT:
966 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_LEFT");
967 x = viewport->anchor_pos.x;
968 break;
969 case DW_HPOS_CENTER:
970 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_CENTER");
971 x = viewport->anchor_pos.x - (vp_width - viewport->anchor_pos.width) / 2;
972 break;
973 case DW_HPOS_RIGHT:
974 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_RIGHT");
975 x = viewport->anchor_pos.x - (vp_width - viewport->anchor_pos.width);
976 break;
977 case DW_HPOS_INTO_VIEW:
978 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_INTO_VIEW");
979 change_x = Dw_gtk_viewport_calc_into (viewport->anchor_pos.x,
980 viewport->anchor_pos.width,
981 hadj->value, vp_width, &x);
982 break;
983 case DW_HPOS_NO_CHANGE:
984 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_NO_CHANGE");
985 change_x = FALSE;
986 break;
987 }
988
989 change_y = TRUE;
990 switch (viewport->anchor_pos.vpos) {
991 case DW_VPOS_TOP:
992 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_TOP");
993 y = viewport->anchor_pos.y;
994 break;
995 case DW_VPOS_CENTER:
996 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_CENTER");
997 y = viewport->anchor_pos.y -
998 (vp_height - viewport->anchor_pos.height) / 2;
999 break;
1000 case DW_VPOS_BOTTOM:
1001 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_BOTTOM");
1002 y = viewport->anchor_pos.y - (vp_height - viewport->anchor_pos.height);
1003 break;
1004 case DW_VPOS_INTO_VIEW:
1005 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_INTO_VIEW");
1006 change_y = Dw_gtk_viewport_calc_into (viewport->anchor_pos.y,
1007 viewport->anchor_pos.height,
1008 vadj->value, vp_height, &y);
1009 case DW_VPOS_NO_CHANGE:
1010 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_NO_CHANGE");
1011 change_y = FALSE;
1012 break;
1013 }
1014
1015 DBG_MSGF (viewport, "scrolling", 0, "scrolling to (%d, %d)\n", x, y);
1016 DBG_MSGF (viewport, "scrolling", 0,
1017 "hadj->upper = %g, hadj->page_size = %g",
1018 hadj->upper, hadj->page_size);
1019 DBG_MSGF (viewport, "scrolling", 0,
1020 "vadj->upper = %g, vadj->page_size = %g",
1021 vadj->upper, vadj->page_size);
1022
1023 if (change_x) {
1024 if (x > hadj->upper - hadj->page_size)
1025 gtk_adjustment_set_value (hadj, hadj->upper - hadj->page_size);
1026 else
1027 gtk_adjustment_set_value (hadj, x);
1028 }
1029
1030 if (change_y) {
1031 if (y > vadj->upper - vadj->page_size)
1032 gtk_adjustment_set_value (vadj, vadj->upper - vadj->page_size);
1033 else
1034 gtk_adjustment_set_value (vadj, y);
1035 }
1036
1037 viewport->anchor_idle_id = 0;
1038
1039 DBG_MSG_END (viewport);
1040 return FALSE;
1041 }
1042
1043
1044 /*
1045 * Called when possibly the scroll position has to be changed because
1046 * of anchors.
1047 */
1048 void Dw_gtk_viewport_update_anchor (GtkDwViewport *viewport)
1049 {
1050 GtkDwViewportAnchor *anchor;
1051 gpointer tmp_anchor;
1052
1053 if (viewport->anchor &&
1054 g_hash_table_lookup_extended (viewport->anchors_table, viewport->anchor,
1055 NULL, &tmp_anchor)) {
1056 anchor = tmp_anchor;
1057 Dw_gtk_viewport_scroll_to (viewport, DW_HPOS_NO_CHANGE, DW_VPOS_TOP,
1058 0, anchor->y + anchor->widget->allocation.y,
1059 0, 0);
1060 }
1061 }
1062
1063
1064 /*
1065 * Sets the anchor to scroll to.
1066 */
1067 void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport,
1068 const gchar *anchor)
1069 {
1070 Dw_gtk_viewport_remove_anchor (viewport);
1071
1072 if (anchor) {
1073 viewport->anchor = g_strdup (anchor);
1074 Dw_gtk_viewport_update_anchor (viewport);
1075 } else {
1076 viewport->anchor = NULL;
1077 gtk_adjustment_set_value (GTK_LAYOUT(viewport)->vadjustment, 0);
1078 }
1079 }
1080
1081
1082 /*
1083 * Sets the position to scroll to. The current anchor will be removed.
1084 */
1085 void a_Dw_gtk_viewport_set_scrolling_position (GtkDwViewport *viewport,
1086 gint32 x,
1087 gint32 y)
1088 {
1089 Dw_gtk_viewport_remove_anchor (viewport);
1090 Dw_gtk_viewport_scroll_to (viewport, DW_HPOS_LEFT, DW_VPOS_TOP, x, y, 0, 0);
1091 }
1092
1093 /*
1094 * Scrolls the viewport, so that the region [x, y, width, height] (world
1095 * coordinates) is seen, according to hpos and vpos.
1096 *
1097 * The actual scrolling is done in an idle function.
1098 */
1099 void Dw_gtk_viewport_scroll_to (GtkDwViewport *viewport,
1100 DwHPosition hpos,
1101 DwVPosition vpos,
1102 gint32 x,
1103 gint32 y,
1104 gint32 width,
1105 gint32 height)
1106 {
1107 viewport->anchor_pos.hpos = hpos;
1108 viewport->anchor_pos.vpos = vpos;
1109 viewport->anchor_pos.x = x;
1110 viewport->anchor_pos.y = y;
1111 viewport->anchor_pos.width = width;
1112 viewport->anchor_pos.height = height;
1113
1114 DBG_OBJ_SET_NUM (viewport, "anchor_pos.hpos", viewport->anchor_pos.hpos);
1115 DBG_OBJ_SET_NUM (viewport, "anchor_pos.vpos", viewport->anchor_pos.vpos);
1116 DBG_OBJ_SET_NUM (viewport, "anchor_pos.x", viewport->anchor_pos.x);
1117 DBG_OBJ_SET_NUM (viewport, "anchor_pos.y", viewport->anchor_pos.y);
1118 DBG_OBJ_SET_NUM (viewport, "anchor_pos.width", viewport->anchor_pos.width);
1119 DBG_OBJ_SET_NUM (viewport, "anchor_pos.height",
1120 viewport->anchor_pos.height);
1121
1122 if (viewport->anchor_idle_id == 0)
1123 viewport->anchor_idle_id = gtk_idle_add
1124 (Dw_gtk_viewport_update_anchor_idle, (gpointer)viewport);
1125 }
1126
1127
1128 /*
1129 * Remove anchor and idle function.
1130 */
1131 void Dw_gtk_viewport_remove_anchor (GtkDwViewport *viewport)
1132 {
1133 if (viewport->anchor) {
1134 g_free (viewport->anchor);
1135 viewport->anchor = NULL;
1136 }
1137
1138 if (viewport->anchor_idle_id != 0) {
1139 gtk_idle_remove (viewport->anchor_idle_id);
1140 viewport->anchor_idle_id = 0;
1141 }
1142 }
1143
1144 /*
1145 * Drawing and resizing is done in this idle function.
1146 */
1147 static gint Dw_gtk_viewport_draw_resize_idle (gpointer data)
1148 {
1149 GtkDwViewport *viewport;
1150 GtkLayout *layout;
1151 GtkWidget *widget;
1152 DwRectangle viewport_area, world_area;
1153 GdkRectangle gtk_area;
1154 int i;
1155
1156 viewport = GTK_DW_VIEWPORT (data);
1157
1158 switch (viewport->draw_resize_action) {
1159 case DW_GTK_VIEWPORT_DRAW:
1160 for (i = 0; i < viewport->num_draw_areas; i++) {
1161 widget = GTK_WIDGET (viewport);
1162 layout = GTK_LAYOUT (viewport);
1163
1164 viewport_area.x = gtk_layout_get_hadjustment(layout)->value;
1165 viewport_area.y = gtk_layout_get_vadjustment(layout)->value;;
1166 viewport_area.width = widget->allocation.width;
1167 viewport_area.height = widget->allocation.height;
1168
1169 if (p_Dw_rectangle_intersect (&viewport->draw_areas[i],
1170 &viewport_area, &world_area)) {
1171 gtk_area.x = world_area.x - viewport_area.x;
1172 gtk_area.y = world_area.y - viewport_area.y;
1173 gtk_area.width = world_area.width;
1174 gtk_area.height = world_area.height;
1175 gtk_widget_draw (widget, &gtk_area);
1176 }
1177 }
1178
1179 /* No more areas to be drawn. */
1180 viewport->num_draw_areas = 0;
1181 break;
1182
1183 case DW_GTK_VIEWPORT_RESIZE:
1184 Dw_gtk_viewport_calc_size (viewport);
1185 break;
1186 }
1187
1188 viewport->draw_resize_idle_id = 0;
1189 return FALSE;
1190 }
1191
1192 /*
1193 * Queue an area for drawing. This function is called by
1194 * p_Dw_widget_queue_draw_area. x and y are passed in world coordinates.
1195 */
1196 void Dw_gtk_viewport_queue_draw (GtkDwViewport *viewport,
1197 gint32 x,
1198 gint32 y,
1199 gint32 width,
1200 gint32 height)
1201 {
1202 DwRectangle area;
1203 int i;
1204
1205 if (viewport->draw_resize_idle_id == 0) {
1206 viewport->draw_resize_action = DW_GTK_VIEWPORT_DRAW;
1207 viewport->draw_resize_idle_id =
1208 gtk_idle_add (Dw_gtk_viewport_draw_resize_idle, (gpointer)viewport);
1209 } else if (viewport->draw_resize_action == DW_GTK_VIEWPORT_RESIZE)
1210 /* Drawing is always overridden by resizing. */
1211 return;
1212
1213 area.x = x;
1214 area.y = y;
1215 area.width = width;
1216 area.height = height;
1217
1218 /* First, try to keep the list as clean as possible. Check whether other
1219 * rectangles interfer with this one in some way. */
1220 /* An idea for optimization: The list could be sorted, and so the part of
1221 * the list we have to consider here, may be reduced, the start may be
1222 * found via linear search. However, this probably makes balanced binary
1223 * trees necessary, since moving elements within the array may be quite
1224 * time-consuming.
1225 */
1226 _MSG(" num_draw_areas = %d\n", viewport->num_draw_areas);
1227 for (i = 0; i < viewport->num_draw_areas; i++) {
1228 if (p_Dw_rectangle_is_subset (&area, &viewport->draw_areas[i]))
1229 /* First case: area is a subset of an already queued rectangle
1230 * -> nothing to do. */
1231 return;
1232 else if (p_Dw_rectangle_is_subset (&viewport->draw_areas[i], &area)) {
1233 /* Second case: area is a subset of an already queued rectangle
1234 * -> replace the other one with area. */
1235 viewport->draw_areas[i] = area;
1236 return;
1237 }
1238 /* Maybe some more tests: if both areas may exactly be combined to a
1239 * rectangle? Very unlikely case ... */
1240 }
1241
1242 /* No interference: add the new area to the list. */
1243 viewport->num_draw_areas++;
1244 a_List_add (viewport->draw_areas, viewport->num_draw_areas,
1245 viewport->num_draw_areas_max);
1246 viewport->draw_areas[viewport->num_draw_areas - 1] = area;
1247 }
1248
1249 /*
1250 * Start the resizing idle. This function is called by
1251 * p_Dw_widget_queue_resize, after the appropriate attributes have been set in
1252 * the widgets, where necessary.
1253 */
1254 void Dw_gtk_viewport_queue_resize (GtkDwViewport *viewport)
1255 {
1256 /* Resizing always overrides drawing. */
1257 viewport->draw_resize_action = DW_GTK_VIEWPORT_RESIZE;
1258 viewport->num_draw_areas = 0;
1259
1260 if (viewport->draw_resize_idle_id == 0)
1261 viewport->draw_resize_idle_id =
1262 gtk_idle_add (Dw_gtk_viewport_draw_resize_idle, (gpointer)viewport);
1263 }
1264
1265
1266 /*
1267 * Return the DwWidget which is at position (vx, vy) in viewport coordinates.
1268 */
1269 DwWidget* a_Dw_gtk_viewport_widget_at_viewport_point (GtkDwViewport *viewport,
1270 gint32 vx,
1271 gint32 vy)
1272 {
1273 gint32 world_x, world_y;
1274
1275 if (viewport->child) {
1276 world_x = vx + gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
1277 world_y = vy + gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
1278 return Dw_gtk_viewport_widget_at_point (viewport, world_x, world_y);
1279 } else
1280 return NULL;
1281 }
0 #ifndef __DW_GTK_VIEWPORT_H__
1 #define __DW_GTK_VIEWPORT_H__
2
3 #include <gtk/gtklayout.h>
4 #include "dw_widget.h"
5 #include "findtext.h"
6 #include "selection.h"
7
8 #ifdef __cplusplus
9 extern "C" {
10 #endif /* __cplusplus */
11
12 #define GTK_TYPE_DW_VIEWPORT (a_Dw_gtk_viewport_get_type ())
13 #define GTK_DW_VIEWPORT(obj) (GTK_CHECK_CAST (obj, \
14 GTK_TYPE_DW_VIEWPORT,GtkDwViewport))
15 #define GTK_DW_VIEWPORT_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, \
16 GTK_TYPE_DW_VIEWPORT, \
17 GtkDwViewportClass)
18 #define GTK_IS_DW_VIEWPORT(obj) GTK_CHECK_TYPE (obj, \
19 GTK_TYPE_DW_VIEWPORT)
20
21 typedef enum {
22 DW_GTK_VIEWPORT_DRAW,
23 DW_GTK_VIEWPORT_RESIZE
24 } DwGtkViewportDrawResizeAction;
25
26 typedef struct _GtkDwViewport GtkDwViewport;
27 typedef struct _GtkDwViewportClass GtkDwViewportClass;
28
29
30 struct _GtkDwViewport
31 {
32 GtkLayout layout;
33
34 GdkPixmap *back_pixmap; /* backing pixmap for buffering */
35 gint back_width, back_height;
36 gint depth;
37
38 DwWidget *child;
39 DwWidget *last_entered;
40 gboolean hscrollbar_used, vscrollbar_used, calc_size_blocked;
41
42 /* updated by Dw_gtk_viewport_motion_notify */
43 gdouble mouse_x, mouse_y;
44
45 gchar *anchor;
46 DwRectPosition anchor_pos;
47 gint anchor_idle_id;
48
49 FindtextState *findtext_state;
50 Selection *selection;
51
52 /* Anchors of the widget tree.
53 * Key: gchar*, has to be stored elsewhere
54 * Value: an instance of GtkDwViewportAnchor (in .c file) */
55 GHashTable *anchors_table;
56
57 /* Queue of draw and resize requests. */
58 gint draw_resize_idle_id;
59 DwGtkViewportDrawResizeAction draw_resize_action;
60
61 /* What has to be redrawn. DwRectangle's are in world coordinates. */
62 DwRectangle *draw_areas;
63 gint num_draw_areas;
64 gint num_draw_areas_max; /* number allocated */
65 };
66
67
68 struct _GtkDwViewportClass
69 {
70 GtkLayoutClass parent_class;
71 };
72
73
74 GtkType a_Dw_gtk_viewport_get_type (void);
75 GtkWidget* a_Dw_gtk_viewport_new (GtkAdjustment *hadjustment,
76 GtkAdjustment *vadjustment);
77 void a_Dw_gtk_viewport_add_dw (GtkDwViewport *viewport,
78 DwWidget *widget);
79
80 void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport,
81 const gchar *anchor);
82 void a_Dw_gtk_viewport_set_scrolling_position (GtkDwViewport
83 *viewport,
84 gint32 x,
85 gint32 y);
86
87 DwWidget* a_Dw_gtk_viewport_widget_at_viewport_point (GtkDwViewport
88 *viewport,
89 gint32 vx,
90 gint32 vy);
91
92 gchar* p_Dw_gtk_viewport_add_anchor (DwWidget *widget,
93 const gchar* name,
94 gint32 y);
95 void p_Dw_gtk_viewport_change_anchor (DwWidget *widget,
96 gchar *name,
97 gint32 y);
98 void p_Dw_gtk_viewport_remove_anchor (DwWidget *widget,
99 gchar *name);
100
101 void Dw_gtk_viewport_remove_dw (GtkDwViewport *viewport);
102 void Dw_gtk_viewport_calc_size (GtkDwViewport *viewport);
103
104 DwWidget* Dw_gtk_viewport_widget_at_point (GtkDwViewport *viewport,
105 gint32 x,
106 gint32 y);
107
108 void Dw_gtk_viewport_update_anchor (GtkDwViewport *viewport);
109 void Dw_gtk_viewport_scroll_to (GtkDwViewport *viewport,
110 DwHPosition hpos,
111 DwVPosition vpos,
112 gint32 x,
113 gint32 y,
114 gint32 width,
115 gint32 height);
116 void Dw_gtk_viewport_remove_anchor (GtkDwViewport *viewport);
117 void Dw_gtk_viewport_queue_draw (GtkDwViewport *viewport,
118 gint32 x,
119 gint32 y,
120 gint32 width,
121 gint32 height);
122 void Dw_gtk_viewport_queue_resize (GtkDwViewport *viewport);
123
124 void Dw_gtk_viewport_update_background (GtkDwViewport *viewport);
125
126 #ifdef __cplusplus
127 }
128 #endif /* __cplusplus */
129
130 #endif /* __DW_GTK_VIEWPORT_H__ */
0 /*
1 * File: dw_hruler.c
2 *
3 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This is really an empty widget, the HTML parser puts a border
13 * around it, and drawing is done in Dw_widget_draw_widget_box. The
14 * only remarkable point is that the DW_HAS_CONTENT flag is
15 * cleared.
16 */
17
18 #include "dw_hruler.h"
19 #include "dw_gtk_viewport.h"
20 #include "debug.h"
21
22 static void Dw_hruler_init (DwHruler *hruler);
23 static void Dw_hruler_class_init (DwHrulerClass *klass);
24
25 static void Dw_hruler_size_request (DwWidget *widget,
26 DwRequisition *requisition);
27 static void Dw_hruler_draw (DwWidget *widget,
28 DwRectangle *area,
29 GdkEventExpose *event);
30 static DwIterator* Dw_hruler_iterator (DwWidget *widget,
31 gint32 mask,
32 gboolean at_end);
33 static void Dw_hruler_iterator_highlight (DwIterator *it,
34 gint start,
35 gint end,
36 DwHighlightLayer layer);
37
38
39 GtkType a_Dw_hruler_get_type (void)
40 {
41 static GtkType type = 0;
42
43 if (!type) {
44 GtkTypeInfo info = {
45 "DwHruler",
46 sizeof (DwHruler),
47 sizeof (DwHrulerClass),
48 (GtkClassInitFunc) Dw_hruler_class_init,
49 (GtkObjectInitFunc) Dw_hruler_init,
50 (GtkArgSetFunc) NULL,
51 (GtkArgGetFunc) NULL,
52 (GtkClassInitFunc) NULL
53 };
54
55 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
56 }
57
58 return type;
59 }
60
61
62 DwWidget* a_Dw_hruler_new (void)
63 {
64 GtkObject *object = gtk_object_new (DW_TYPE_HRULER, NULL);
65 DBG_OBJ_CREATE (object, "DwHRuler");
66 return DW_WIDGET (object);
67 }
68
69
70 static void Dw_hruler_init (DwHruler *hruler)
71 {
72 int i;
73 DW_WIDGET_UNSET_FLAGS (hruler, DW_HAS_CONTENT);
74 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
75 hruler->selected[i] = FALSE;
76 }
77
78
79 static void Dw_hruler_class_init (DwHrulerClass *klass)
80 {
81 GtkObjectClass *object_class;
82 DwWidgetClass *widget_class;
83
84 object_class = GTK_OBJECT_CLASS (klass);
85
86 widget_class = (DwWidgetClass*)klass;
87 widget_class->size_request = Dw_hruler_size_request;
88 widget_class->draw = Dw_hruler_draw;
89 widget_class->iterator = Dw_hruler_iterator;
90 }
91
92
93 static void Dw_hruler_size_request (DwWidget *widget,
94 DwRequisition *requisition)
95 {
96 requisition->width = p_Dw_style_box_diff_width (widget->style);
97 requisition->ascent = p_Dw_style_box_diff_height (widget->style);
98 requisition->descent = 0;
99 }
100
101
102 static void Dw_hruler_draw (DwWidget *widget,
103 DwRectangle *area,
104 GdkEventExpose *event)
105 {
106 int i;
107 gboolean selected = FALSE;
108
109 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
110 selected = DW_HRULER(widget)->selected[i];
111 p_Dw_widget_draw_widget_box (widget, area, selected);
112 if (selected)
113 p_Dw_widget_draw_selected (widget, area);
114 }
115
116
117 static DwIterator *Dw_hruler_iterator (DwWidget *widget,
118 gint32 mask,
119 gboolean at_end)
120 {
121 DwIterator *it;
122
123 it = p_Dw_widget_text_iterator (widget, mask, at_end,
124 "-----------------------------------"
125 "-----------------------------------");
126 if (it)
127 it->highlight = Dw_hruler_iterator_highlight;
128 return it;
129 }
130
131 static void Dw_hruler_iterator_highlight (DwIterator *it,
132 gint start,
133 gint end,
134 DwHighlightLayer layer)
135 {
136 if (it->content.type == DW_CONTENT_TEXT) {
137 DW_HRULER(it->widget)->selected[layer] = (start == 0 && end >= 1);
138 p_Dw_widget_queue_draw (it->widget);
139 }
140 }
0 #ifndef __DW_HRULER_H__
1 #define __DW_HRULER_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_HRULER (a_Dw_hruler_get_type ())
10 #define DW_HRULER(obj) GTK_CHECK_CAST (obj,DW_TYPE_HRULER, DwHruler)
11 #define DW_HRULER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_HRULER, \
12 DwHrulerClass)
13 #define DW_IS_HRULER(obj) GTK_CHECK_TYPE (obj, DW_TYPE_HRULER)
14
15 typedef struct _DwHruler DwHruler;
16 typedef struct _DwHrulerClass DwHrulerClass;
17
18
19 struct _DwHruler
20 {
21 DwWidget widget;
22 gboolean selected[DW_HIGHLIGHT_NUM_LAYERS];
23 };
24
25
26 struct _DwHrulerClass
27 {
28 DwWidgetClass parent_class;
29 };
30
31
32 GtkType a_Dw_hruler_get_type (void);
33 DwWidget* a_Dw_hruler_new (void);
34
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
39 #endif /* __DW_HRULER_H__ */
0 /*
1 * File: dw_image.c
2 *
3 * Copyright (C) 2001 Sebastian Geerken <S.Geerken@ping.de>,
4 * Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include "msg.h"
13 #include "dw_image.h"
14 #include "dw_gtk_viewport.h"
15 #include "prefs.h"
16 #include "dw_marshal.h"
17 #include "list.h"
18 #include "dicache.h"
19 #include "debug.h"
20 #include <gdk/gdk.h>
21 #include <gtk/gtk.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 static void Dw_image_init (DwImage *image);
26 static void Dw_image_class_init (DwImageClass *klass);
27
28 static void Dw_image_destroy (GtkObject *object);
29
30 static void Dw_image_size_request (DwWidget *widget,
31 DwRequisition *requisition);
32 static void Dw_image_size_allocate (DwWidget *widget,
33 DwAllocation *allocation);
34 static void Dw_image_draw (DwWidget *widget,
35 DwRectangle *area,
36 GdkEventExpose *event);
37 static gboolean Dw_image_button_press (DwWidget *widget,
38 gint32 x,
39 gint32 y,
40 GdkEventButton *event);
41 static gboolean Dw_image_button_release (DwWidget *widget,
42 gint32 x,
43 gint32 y,
44 GdkEventButton *event);
45 static gboolean Dw_image_motion_notify (DwWidget *widget,
46 gint32 x,
47 gint32 y,
48 GdkEventMotion *event);
49 static gboolean Dw_image_enter_notify (DwWidget *widget,
50 DwWidget *last_widget,
51 GdkEventMotion *event);
52 static gboolean Dw_image_leave_notify (DwWidget *widget,
53 DwWidget *next_widget,
54 GdkEventMotion *event);
55 static DwIterator* Dw_image_iterator (DwWidget *widget,
56 gint32 mask,
57 gboolean at_end);
58 static void Dw_image_iterator_highlight (DwIterator *it,
59 gint start,
60 gint end,
61 DwHighlightLayer layer);
62
63 static void Dw_image_find_link (DwImage *image,
64 gint x, gint y,
65 gint *link,
66 gint *link_x, gint *link_y);
67 static void Dw_image_scale_row (DwImage *image, gint y_dest);
68 static void Dw_image_scale (DwImage *image);
69
70 static gint Dw_image_map_list_find_link (DwImageMapList *list,
71 DilloUrl *url,
72 gint x,
73 gint y);
74
75
76 #define Dw_image_scaled_y(image, y_src) \
77 ( (y_src) * ( ((DwWidget*)(image))->allocation.ascent + \
78 ((DwWidget*)(image))->allocation.descent - \
79 p_Dw_style_box_diff_height ( ((DwWidget*)(image))->style ) ) \
80 / ((DwImage*)(image))->height )
81
82
83 enum
84 {
85 LINK_ENTERED,
86 LINK_PRESSED,
87 LINK_RELEASED,
88 LINK_CLICKED,
89 LAST_SIGNAL
90 };
91
92 static guint image_signals[LAST_SIGNAL] = { 0 };
93 static DwWidgetClass *parent_class;
94
95
96 /*
97 * Standard Gtk+ function.
98 */
99 GtkType a_Dw_image_get_type (void)
100 {
101 static GtkType type = 0;
102
103 if (!type) {
104 GtkTypeInfo info = {
105 "DwImage",
106 sizeof (DwImage),
107 sizeof (DwImageClass),
108 (GtkClassInitFunc) Dw_image_class_init,
109 (GtkObjectInitFunc) Dw_image_init,
110 (GtkArgSetFunc) NULL,
111 (GtkArgGetFunc) NULL,
112 (GtkClassInitFunc) NULL
113 };
114
115 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
116 }
117
118 return type;
119 }
120
121
122 /*
123 * Standard Gtk+ function.
124 */
125 DwWidget* a_Dw_image_new (DwImageType type, const gchar *alt_text)
126 {
127 GtkObject *object;
128 DwImage *image;
129
130 object = gtk_object_new (DW_TYPE_IMAGE, NULL);
131 DBG_OBJ_CREATE (object, "DwImage");
132 image = DW_IMAGE(object);
133 image->alt_text = g_strdup (alt_text);
134 return DW_WIDGET (object);
135 }
136
137
138 /*
139 * Standard Gtk+ function.
140 */
141 static void Dw_image_init (DwImage *image)
142 {
143 int i;
144
145 image->url = NULL;
146 image->width = 0;
147 image->height = 0;
148 image->alt_text_width = -1; /* not yet calculated */
149 image->buffer = NULL;
150 image->scaled_buffer = NULL;
151 image->alt_text = NULL;
152 image->usemap_url = NULL;
153 image->ismap = FALSE;
154 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
155 image->selected[i] = FALSE;
156 }
157
158
159 /*
160 * Standard Gtk+ function.
161 */
162 static void Dw_image_class_init (DwImageClass *klass)
163 {
164 GtkObjectClass *object_class;
165 DwWidgetClass *widget_class;
166
167 parent_class = gtk_type_class (DW_TYPE_WIDGET);
168
169 object_class = (GtkObjectClass*)klass;
170 object_class->destroy = Dw_image_destroy;
171
172 image_signals[LINK_ENTERED] =
173 gtk_signal_new ("link_entered",
174 GTK_RUN_LAST,
175 object_class->type,
176 GTK_SIGNAL_OFFSET (DwImageClass, link_entered),
177 p_Dw_marshal_link_enter,
178 GTK_TYPE_BOOL,
179 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
180 image_signals[LINK_PRESSED] =
181 gtk_signal_new ("link_pressed",
182 GTK_RUN_LAST,
183 object_class->type,
184 GTK_SIGNAL_OFFSET (DwImageClass, link_pressed),
185 p_Dw_marshal_link_button,
186 GTK_TYPE_BOOL,
187 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
188 GTK_TYPE_GDK_EVENT);
189 image_signals[LINK_RELEASED] =
190 gtk_signal_new ("link_released",
191 GTK_RUN_LAST,
192 object_class->type,
193 GTK_SIGNAL_OFFSET (DwImageClass, link_released),
194 p_Dw_marshal_link_button,
195 GTK_TYPE_BOOL,
196 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
197 GTK_TYPE_GDK_EVENT);
198 image_signals[LINK_CLICKED] =
199 gtk_signal_new ("link_clicked",
200 GTK_RUN_LAST,
201 object_class->type,
202 GTK_SIGNAL_OFFSET (DwImageClass, link_clicked),
203 p_Dw_marshal_link_button,
204 GTK_TYPE_BOOL,
205 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
206 GTK_TYPE_GDK_EVENT);
207 gtk_object_class_add_signals (object_class, image_signals, LAST_SIGNAL);
208
209 widget_class = (DwWidgetClass*)klass;
210 widget_class->size_request = Dw_image_size_request;
211 widget_class->size_allocate = Dw_image_size_allocate;
212 widget_class->draw = Dw_image_draw;
213 widget_class->button_press_event = Dw_image_button_press;
214 widget_class->button_release_event = Dw_image_button_release;
215 widget_class->motion_notify_event = Dw_image_motion_notify;
216 widget_class->enter_notify_event = Dw_image_enter_notify;
217 widget_class->leave_notify_event = Dw_image_leave_notify;
218 widget_class->iterator = Dw_image_iterator;
219 }
220
221
222 /*
223 * Standard Gtk+ function.
224 */
225 static void Dw_image_destroy (GtkObject *object)
226 {
227 DwImage *image = DW_IMAGE (object);
228
229 if (image->usemap_url)
230 a_Url_free (image->usemap_url);
231 if (image->url)
232 a_Dicache_unref (image->url, image->version);
233 g_free (image->alt_text);
234 g_free (image->scaled_buffer);
235
236 GTK_OBJECT_CLASS(parent_class)->destroy (object);
237 }
238
239
240 /*
241 * Standard Dw function.
242 */
243 static void Dw_image_size_request (DwWidget *widget,
244 DwRequisition *requisition)
245 {
246 DwImage *image;
247
248 image = DW_IMAGE (widget);
249
250 if (image->buffer != NULL ||
251 (image->alt_text == NULL || image->alt_text[0] == '\0')) {
252 requisition->width = image->width;
253 requisition->ascent = image->height;
254 requisition->descent = 0;
255 } else {
256 if (image->alt_text_width == -1)
257 image->alt_text_width =
258 gdk_string_width (widget->style->font->font, image->alt_text);
259
260 requisition->width = image->alt_text_width;
261 requisition->ascent = widget->style->font->font->ascent;
262 requisition->descent = widget->style->font->font->descent;
263 }
264
265 requisition->width += p_Dw_style_box_diff_width (widget->style);
266 requisition->ascent += p_Dw_style_box_offset_y (widget->style);
267 requisition->descent += p_Dw_style_box_rest_height (widget->style);
268 }
269
270
271 /*
272 * Standard Dw function.
273 */
274 static void Dw_image_size_allocate (DwWidget *widget,
275 DwAllocation *allocation)
276 {
277 DwImage *image;
278
279 /* if image is moved only */
280 if (allocation->width == widget->allocation.width &&
281 allocation->ascent + allocation->descent == DW_WIDGET_HEIGHT(widget))
282 return;
283
284 /* this is also done in a_Dw_widget_size_allocate, but
285 Dw_image_scale needs this. */
286 widget->allocation = *allocation;
287
288 image = DW_IMAGE (widget);
289 if (image->buffer != NULL && image->width > 0 && image->height > 0)
290 Dw_image_scale (image);
291 }
292
293
294 /*
295 * Standard Dw function.
296 */
297 static void Dw_image_draw (DwWidget *widget,
298 DwRectangle *area,
299 GdkEventExpose *event)
300 {
301 gint32 vx, vy;
302 DwRectangle content, intersection;
303 GdkGC *gc;
304 DwImage *image = DW_IMAGE (widget);
305 guchar *buffer, *bstart;
306 int i;
307 gboolean selected = FALSE;
308
309 if (image->buffer) {
310 p_Dw_widget_draw_widget_box (widget, area, FALSE);
311
312 content.x = p_Dw_style_box_offset_x (widget->style);
313 content.y = p_Dw_style_box_offset_y (widget->style);
314 content.width = DW_WIDGET_CONTENT_WIDTH(widget);
315 content.height = DW_WIDGET_CONTENT_HEIGHT(widget);
316
317 if (p_Dw_rectangle_intersect (area, &content, &intersection)) {
318 vx = p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x);
319 vy = p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y);
320 gc = widget->style->color->gc;
321
322 if (image->scaled_buffer)
323 buffer = image->scaled_buffer;
324 else
325 buffer = image->buffer;
326
327 bstart = buffer +
328 3 * ( intersection.x - p_Dw_style_box_offset_x (widget->style)+
329 content.width * (intersection.y -
330 p_Dw_style_box_offset_y (widget->style)) );
331
332 gdk_draw_rgb_image (
333 DW_WIDGET_WINDOW (widget), gc,
334 vx + intersection.x, vy + intersection.y,
335 intersection.width, intersection.height, GDK_RGB_DITHER_MAX,
336 bstart, content.width * 3);
337 }
338 } else {
339 if (image->alt_text && image->alt_text[0]) {
340 if (image->alt_text_width == -1)
341 image->alt_text_width =
342 gdk_string_width (widget->style->font->font, image->alt_text);
343
344 if (widget->allocation.width < image->alt_text_width ||
345 widget->allocation.ascent + widget->allocation.descent
346 < widget->style->font->font->ascent
347 + widget->style->font->font->descent)
348 p_Dw_widget_will_clip (widget);
349
350 p_Dw_widget_draw_widget_box (widget, area, FALSE);
351 gdk_draw_string
352 (DW_WIDGET_WINDOW (widget), widget->style->font->font,
353 widget->style->color->gc,
354 p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x),
355 p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y)
356 + widget->style->font->font->ascent,
357 image->alt_text);
358 }
359 }
360
361 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
362 selected = image->selected[i];
363 if (selected)
364 p_Dw_widget_draw_selected (widget, area);
365 }
366
367
368 /*
369 * Standard Dw function.
370 */
371 static gboolean Dw_image_enter_notify (DwWidget *widget,
372 DwWidget *last_widget,
373 GdkEventMotion *event)
374 {
375 return FALSE;
376 }
377
378
379 /*
380 * Standard Dw function.
381 */
382 static gboolean Dw_image_leave_notify (DwWidget *widget,
383 DwWidget *next_widget,
384 GdkEventMotion *event)
385 {
386 DwImage *image = DW_IMAGE (widget);
387 gboolean return_val = FALSE;
388
389 if (image->hover_link != -1) {
390 image->hover_link = -1;
391 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_ENTERED],
392 -1, -1, -1, &return_val);
393 return return_val;
394 }
395
396 return FALSE;
397 }
398
399
400 /*
401 * Standard Dw function.
402 */
403 static gboolean Dw_image_button_press (DwWidget *widget,
404 gint32 x,
405 gint32 y,
406 GdkEventButton *event)
407 {
408 DwImage *image = DW_IMAGE (widget);
409 gint link_x, link_y;
410 gboolean return_val = FALSE;
411
412 Dw_image_find_link (image, x, y, &image->pressed_link, &link_x, &link_y);
413
414 if (image->pressed_link >= 0)
415 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_PRESSED],
416 image->pressed_link, link_x, link_y, event,
417 &return_val);
418
419 return return_val;
420 }
421
422
423 /*
424 * Standard Dw function.
425 */
426 static gboolean Dw_image_button_release (DwWidget *widget,
427 gint32 x,
428 gint32 y,
429 GdkEventButton *event)
430 {
431 DwImage *image = DW_IMAGE (widget);
432 gint link_pressed, link_released, link_x, link_y;
433 gboolean return_val1 = FALSE, return_val2 = FALSE;
434
435 link_pressed = image->pressed_link;
436 Dw_image_find_link (image, x, y, &link_released, &link_x, &link_y);
437 image->pressed_link = -1;
438
439 if (link_released >= 0) {
440 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_RELEASED],
441 link_released, link_x, link_y, event, &return_val1);
442 if (link_pressed == link_released) {
443 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_CLICKED],
444 link_released, link_x, link_y, event, &return_val2);
445 return return_val1 || return_val2;
446 }
447
448 return return_val1;
449 }
450
451 return FALSE;
452 }
453
454
455 /*
456 * Standard Dw function.
457 */
458 static gboolean Dw_image_motion_notify (DwWidget *widget,
459 gint32 x, gint32 y,
460 GdkEventMotion *event)
461 {
462 DwImage *image = DW_IMAGE (widget);
463 gint link, link_old, link_x, link_y;
464 gboolean return_val = FALSE;
465
466 link_old = image->hover_link;
467 Dw_image_find_link (image, x, y, &link, &link_x, &link_y);
468 image->hover_link = link;
469
470 /* In case of a ISMAP image, the signal has to be emitted always */
471 if (link_x != -1 || link != link_old) {
472 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_ENTERED],
473 link, link_x, link_y, &return_val);
474 return return_val;
475 } else
476 return (link != -1);
477 }
478
479
480 /*
481 * Find a link given a coordinate location relative to the window.
482 */
483 static void Dw_image_find_link (DwImage *image,
484 gint x, gint y,
485 gint *link,
486 gint *link_x, gint *link_y)
487 {
488 DwStyle *style;
489 DwWidget *widget = DW_WIDGET (image);
490
491 *link_x = -1;
492 *link_y = -1;
493
494 if (image->map_list && image->usemap_url) {
495 *link = Dw_image_map_list_find_link
496 (image->map_list, image->usemap_url,
497 x - p_Dw_style_box_offset_x (widget->style),
498 y - p_Dw_style_box_offset_y (widget->style));
499 if (*link != -1)
500 return;
501 }
502
503 if (image->ismap &&
504 (style = DW_WIDGET(image)->style) && style->x_link != -1) {
505 *link = style->x_link;
506 *link_x = x - p_Dw_style_box_offset_x (widget->style);
507 *link_y = y - p_Dw_style_box_offset_y (widget->style);
508 return;
509 }
510
511 *link = -1;
512 }
513
514
515 /*
516 * Set or resize a image.
517 */
518 void a_Dw_image_size (DwImage *image, gint width, gint height)
519 {
520 gint Resize = (image->width != width || image->height != height);
521
522 image->width = width;
523 image->height = height;
524 if (Resize)
525 p_Dw_widget_queue_resize (DW_WIDGET (image), 0, TRUE);
526
527 if (image->buffer)
528 /* if a_Dw_image_set_buffer has been called before */
529 Dw_image_scale (image);
530 }
531
532 /*
533 * Called after the RGB line buffer has been copied into the full
534 * image buffer. Uses for drawing and scaling.
535 */
536 void a_Dw_image_draw_row (DwImage *image,
537 gint Width, gint Height, gint x, gint y)
538 {
539 DwWidget *widget = DW_WIDGET (image);
540 gint dy1, dy2;
541
542 _MSG("a_Dw_image_draw_row: x=%d y=%d\n", x, y);
543 g_return_if_fail (image->buffer != NULL);
544
545 if (image->scaled_buffer) {
546 Dw_image_scale_row (image, y);
547
548 dy1 = Dw_image_scaled_y (image, y);
549 dy2 = Dw_image_scaled_y (image, y + 1);
550 p_Dw_widget_queue_draw_area(widget,
551 x,
552 dy1 + p_Dw_style_box_offset_y(widget->style),
553 widget->allocation.width, dy2 - dy1);
554 } else
555 p_Dw_widget_queue_draw_area (widget,
556 x,
557 y + p_Dw_style_box_offset_y (widget->style),
558 widget->allocation.width, 1);
559 }
560
561 /*
562 * Set the widget buffer to reference the dicache entry buffer
563 */
564 void a_Dw_image_set_buffer (DwImage *image, guchar *ImageBuffer,
565 DilloUrl *url, gint version)
566 {
567 image->buffer = ImageBuffer;
568 image->url = url;
569 image->version = version;
570
571 if (image->width > 0 && image->height > 0)
572 /* if a_Dw_image_set_size has been called before */
573 Dw_image_scale (image);
574 }
575
576 /*
577 * Sets image as server side image map.
578 */
579 void a_Dw_image_set_ismap (DwImage *image)
580 {
581 image->ismap = TRUE;
582 }
583
584 /*
585 * Sets image as client side image map.
586 */
587 void a_Dw_image_set_usemap (DwImage *image, DwImageMapList *map_list,
588 DilloUrl *usemap_url)
589 {
590 image->map_list = map_list;
591
592 if (image->usemap_url)
593 a_Url_free (image->usemap_url);
594 image->usemap_url = usemap_url ? a_Url_dup (usemap_url) : NULL;
595 }
596
597
598 /*
599 * Scale the whole image: Compare buffer size with allocation, and, if
600 * necessary, allocate a second buffer and scale all rows.
601 */
602 static void Dw_image_scale (DwImage *image)
603 {
604 int w, h, y;
605 DwWidget *widget;
606
607 if (image->scaled_buffer) {
608 g_free (image->scaled_buffer);
609 image->scaled_buffer = NULL;
610 }
611
612 widget = DW_WIDGET (image);
613 w = DW_WIDGET_CONTENT_WIDTH(widget);
614 h = DW_WIDGET_CONTENT_HEIGHT(widget);
615
616 /* Zero or negative sizes? Ignore. */
617 if (w <= 0 || h <= 0)
618 return;
619
620 if (image->width != w || image->height != h) {
621 /* scaled image */
622 image->scaled_buffer = g_malloc ((gulong)3 * w * h);
623
624 for (y = 0; y < image->height; y++)
625 Dw_image_scale_row (image, y);
626 }
627 }
628
629
630 /*
631 * Scale one row. y_src is the row in the dicache buffer.
632 */
633 static void Dw_image_scale_row (DwImage *image, gint y_src)
634 {
635 DwWidget *widget;
636 guchar *src, *dest, *dest1;
637 gint w_src, w_dest, x_src, x_dest, y_dest1, y_dest2, y_dest, delta;
638
639 widget = DW_WIDGET (image);
640 w_src = image->width;
641 w_dest = DW_WIDGET_CONTENT_WIDTH(widget);
642 y_dest1 = Dw_image_scaled_y (image, y_src);
643 y_dest2 = Dw_image_scaled_y (image, y_src + 1);
644
645 src = image->buffer + 3 * y_src * w_src;
646
647 if (y_dest1 != y_dest2) {
648 dest1 = image->scaled_buffer + 3 * y_dest1 * w_dest;
649
650 if (w_src == w_dest)
651 memcpy (dest1, src, 3 * (size_t)w_src);
652 else if (w_dest > w_src) {
653 delta = w_src / 2;
654 x_src = 0;
655 x_dest = 0;
656
657 while (x_dest < w_dest) {
658 memcpy (dest1 + 3 * x_dest, src + 3 * x_src, 3);
659 x_dest++;
660 delta += w_src;
661 while (delta > w_dest) {
662 delta -= w_dest;
663 x_src++;
664 }
665 }
666 } else {
667 delta = w_dest / 2;
668 x_src = 0;
669 x_dest = 0;
670
671 while (x_src < w_src) {
672 memcpy (dest1 + 3 * x_dest, src + 3 * x_src, 3);
673 x_src++;
674 delta += w_dest;
675 while (delta > w_src) {
676 delta -= w_src;
677 x_dest++;
678 }
679 }
680 }
681
682 /* The other lines are simply copied. */
683 for (y_dest = y_dest1 + 1; y_dest < y_dest2; y_dest++) {
684 dest = image->scaled_buffer + 3 * y_dest * w_dest;
685 memcpy (dest, dest1, 3 * (size_t)w_dest);
686 }
687 }
688 }
689
690
691 /*
692 * Image Maps
693 */
694
695 /*
696 * Initialize a DwImageMapList. The memory has to be allocated before.
697 */
698 void a_Dw_image_map_list_init (DwImageMapList *list)
699 {
700 list->num_maps = 0;
701 list->num_maps_max = 8;
702 list->maps = g_new (DwImageMap, list->num_maps_max);
703
704 list->num_shapes = 0;
705 list->num_shapes_max = 8;
706 list->shapes = g_new (DwImageMapShape, list->num_shapes_max);
707 }
708
709
710 /*
711 * Free the content of a DwImageMapList. The memory for the list is
712 * not freed.
713 */
714 void a_Dw_image_map_list_free (DwImageMapList *list)
715 {
716 gint i;
717
718 for (i = 0; i < list->num_maps; i++)
719 a_Url_free (list->maps[i].url);
720 g_free (list->maps);
721
722 for (i = 0; i < list->num_shapes; i++)
723 if (list->shapes[i].type == DW_IMAGE_MAP_SHAPE_POLY)
724 gdk_region_destroy (list->shapes[i].data.poly);
725 g_free (list->shapes);
726 }
727
728
729 /*
730 * Add a new map to the list. Next added shapes will belong to this
731 * map.
732 */
733 void a_Dw_image_map_list_add_map (DwImageMapList *list,
734 DilloUrl *url)
735 {
736 list->num_maps++;
737 a_List_add (list->maps, list->num_maps, list->num_maps_max);
738 list->maps[list->num_maps - 1].url = a_Url_dup (url);
739 list->maps[list->num_maps - 1].start_shape = list->num_shapes;
740 }
741
742
743 void a_Dw_image_map_list_add_shape (DwImageMapList *list,
744 gint type,
745 gint link,
746 GdkPoint *points,
747 gint num_points)
748 {
749 gboolean correct_args;
750 DwImageMapShape *shape;
751
752 switch (type) {
753 case DW_IMAGE_MAP_SHAPE_RECT:
754 correct_args = (num_points >= 2);
755 break;
756
757 case DW_IMAGE_MAP_SHAPE_CIRCLE:
758 correct_args = (num_points >= 1 && points[1].x != 0);
759 break;
760
761 case DW_IMAGE_MAP_SHAPE_POLY:
762 correct_args = (num_points >= 3);
763 break;
764
765 default:
766 correct_args = FALSE;
767 }
768
769 if (correct_args) {
770 list->num_shapes++;
771 a_List_add (list->shapes, list->num_shapes, list->num_shapes_max);
772 shape = &list->shapes[list->num_shapes - 1];
773 shape->type = type;
774 shape->link = link;
775
776 switch (type) {
777 case DW_IMAGE_MAP_SHAPE_RECT:
778 shape->data.rect.left = points[0].x;
779 shape->data.rect.top = points[0].y;
780 shape->data.rect.right = points[1].x;
781 shape->data.rect.bottom = points[1].y;
782 break;
783
784 case DW_IMAGE_MAP_SHAPE_CIRCLE:
785 shape->data.circle.x = points[0].x;
786 shape->data.circle.y = points[0].y;
787 shape->data.circle.r2 = points[1].x * points[1].x;
788 break;
789
790 case DW_IMAGE_MAP_SHAPE_POLY:
791 shape->data.poly = gdk_region_polygon (points, num_points,
792 GDK_WINDING_RULE);
793 break;
794 }
795 }
796 }
797
798
799 /*
800 * Find a link in an image map.
801 */
802 static gint Dw_image_map_list_find_link (DwImageMapList *list,
803 DilloUrl *url,
804 gint x,
805 gint y)
806 {
807 gint i, j, start, end, dx, dy;
808 DwImageMapShape *shape;
809
810 for (i = 0; i < list->num_maps; i++) {
811 if (a_Url_cmp (list->maps[i].url, url) == 0 &&
812 URL_STRCAMP_EQ(URL_FRAGMENT_(list->maps[i].url),URL_FRAGMENT_(url))){
813 /* map found */
814 start = list->maps[i].start_shape;
815 if (i == list->num_maps - 1)
816 end = list->num_shapes;
817 else
818 end = list->maps[i + 1].start_shape;
819
820 for (j = start; j < end; j++) {
821 shape = &list->shapes[j];
822 switch (shape->type) {
823 case DW_IMAGE_MAP_SHAPE_CIRCLE:
824 dx = shape->data.circle.x - x;
825 dy = shape->data.circle.y - y;
826 if (shape->data.circle.r2 >= (dx*dx + dy*dy))
827 return shape->link;
828 break;
829
830 case DW_IMAGE_MAP_SHAPE_RECT:
831 if (x > shape->data.rect.left &&
832 x < shape->data.rect.right &&
833 y > shape->data.rect.top &&
834 y < shape->data.rect.bottom)
835 return shape->link;
836 break;
837
838 case DW_IMAGE_MAP_SHAPE_POLY:
839 if (gdk_region_point_in (shape->data.poly, x, y))
840 return shape->link;
841 break;
842 }
843 }
844
845 /* no shape found */
846 return -1;
847 }
848 }
849
850 /* no map found */
851 return -1;
852 }
853
854 static DwIterator *Dw_image_iterator (DwWidget *widget,
855 gint32 mask,
856 gboolean at_end)
857 {
858 DwIterator *it;
859 DwImage *image = DW_IMAGE (widget);
860
861 if (image->alt_text) {
862 it = p_Dw_widget_text_iterator (widget, mask, at_end, image->alt_text);
863 if (it)
864 it->highlight = Dw_image_iterator_highlight;
865 return it;
866 } else
867 return NULL;
868 }
869
870 static void Dw_image_iterator_highlight (DwIterator *it,
871 gint start,
872 gint end,
873 DwHighlightLayer layer)
874 {
875 if (it->content.type == DW_CONTENT_TEXT) {
876 /* The whole image is highlighted, as soon as something is selected at
877 * all. */
878 DW_IMAGE(it->widget)->selected[layer] = (start != -1 && start != end);
879 p_Dw_widget_queue_draw (it->widget);
880 }
881 }
0 #ifndef __DW_IMAGE_H__
1 #define __DW_IMAGE_H__
2
3 #include <stdio.h>
4 #include "dw_widget.h"
5 #include "url.h" /* for DilloUrl */
6
7 #ifdef __cplusplus
8 extern "C" {
9 #endif /* __cplusplus */
10
11 #define DW_TYPE_IMAGE (a_Dw_image_get_type ())
12 #define DW_IMAGE(obj) GTK_CHECK_CAST (obj,DW_TYPE_IMAGE, DwImage)
13 #define DW_IMAGE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_IMAGE, \
14 DwImageClass)
15 #define DW_IS_IMAGE(obj) GTK_CHECK_TYPE (obj, DW_TYPE_IMAGE)
16
17 typedef enum {
18 DW_IMAGE_RGB
19 } DwImageType;
20
21 typedef struct _DwImage DwImage;
22 typedef struct _DwImageClass DwImageClass;
23
24 typedef struct _DwImageMap DwImageMap;
25 typedef struct _DwImageMapList DwImageMapList;
26 typedef struct _DwImageMapShape DwImageMapShape;
27
28
29 struct _DwImage
30 {
31 DwWidget widget;
32
33 DilloUrl *url;
34 gint version;
35 DwImageType type;
36 guchar *buffer;
37 gint width;
38 gint height;
39 gint alt_text_width;
40
41 /* non NULL if image is scaled */
42 guchar *scaled_buffer;
43
44 /* ALT text (for selection) */
45 gchar *alt_text;
46
47 DwImageMapList *map_list;
48 DilloUrl *usemap_url;
49 gboolean ismap;
50
51 gint hover_link;
52 gint pressed_link;
53 gboolean selected[DW_HIGHLIGHT_NUM_LAYERS];
54 };
55
56 struct _DwImageClass
57 {
58 DwWidgetClass parent_class;
59
60 gboolean (*link_entered) (DwImage *page,
61 gint link, gint x, gint y);
62 gboolean (*link_pressed) (DwImage *page,
63 gint link, gint x, gint y,
64 GdkEventButton *event);
65 gboolean (*link_released) (DwImage *page,
66 gint link, gint x, gint y,
67 GdkEventButton *event);
68 gboolean (*link_clicked) (DwImage *page,
69 gint link, gint x, gint y,
70 GdkEventButton *event);
71 };
72
73
74 /*
75 * Image Maps
76 */
77
78 #define DW_IMAGE_MAP_SHAPE_RECT 0
79 #define DW_IMAGE_MAP_SHAPE_CIRCLE 1
80 #define DW_IMAGE_MAP_SHAPE_POLY 2
81
82 struct _DwImageMapList
83 {
84 DwImageMap *maps;
85 gint num_maps;
86 gint num_maps_max;
87
88 DwImageMapShape *shapes;
89 gint num_shapes;
90 gint num_shapes_max;
91 };
92
93 struct _DwImageMap
94 {
95 DilloUrl *url;
96 gint start_shape;
97 };
98
99 struct _DwImageMapShape
100 {
101 gint type;
102 gint link;
103
104 union {
105 GdkRegion *poly;
106 struct {
107 gint32 x;
108 gint32 y;
109 gint32 r2;
110 } circle;
111 struct {
112 gint32 top;
113 gint32 bottom;
114 gint32 left;
115 gint32 right;
116 } rect;
117 } data;
118 };
119
120
121 /*
122 * Function prototypes
123 */
124 GtkType a_Dw_image_get_type (void);
125 DwWidget* a_Dw_image_new (DwImageType type, const gchar *alt_text);
126 void a_Dw_image_size (DwImage *image, gint width, gint height);
127 void a_Dw_image_draw_row(DwImage *image,
128 gint Width, gint Height, gint x, gint y);
129 void a_Dw_image_set_buffer(DwImage *image, guchar *ImageBuffer,
130 DilloUrl *url, gint version);
131
132 void a_Dw_image_set_ismap (DwImage *image);
133 void a_Dw_image_set_usemap (DwImage *image, DwImageMapList *map_list,
134 DilloUrl *usemap_url);
135
136 /* Image maps */
137 void a_Dw_image_map_list_init (DwImageMapList *list);
138 void a_Dw_image_map_list_free (DwImageMapList *list);
139
140 void a_Dw_image_map_list_add_map (DwImageMapList *list,
141 DilloUrl *url);
142 void a_Dw_image_map_list_add_shape (DwImageMapList *list,
143 gint type,
144 gint link,
145 GdkPoint *points,
146 gint num_points);
147
148 #ifdef __cplusplus
149 }
150 #endif /* __cplusplus */
151
152 #endif /* __DW_IMAGE_H__ */
0 /*
1 * File: dw_aligned_page.c
2 *
3 * Copyright (C) 2002 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "dw_list_item.h"
12 #include "debug.h"
13
14 static void Dw_list_item_class_init (DwListItemClass *klass);
15 static void Dw_list_item_init (DwListItem *list_item);
16
17 static gint32 Dw_list_item_get_value (DwAlignedPage *aligned_page);
18 static void Dw_list_item_set_max_value (DwAlignedPage *aligned_page,
19 gint32 max_value,
20 gint32 value);
21
22 /*
23 * Standard Gtk+ function.
24 */
25 GtkType a_Dw_list_item_get_type (void)
26 {
27 static GtkType type = 0;
28
29 if (!type) {
30 GtkTypeInfo info = {
31 "DwListItem",
32 sizeof (DwListItem),
33 sizeof (DwListItemClass),
34 (GtkClassInitFunc) Dw_list_item_class_init,
35 (GtkObjectInitFunc) Dw_list_item_init,
36 (GtkArgSetFunc) NULL,
37 (GtkArgGetFunc) NULL,
38 (GtkClassInitFunc) NULL
39 };
40
41 type = gtk_type_unique (DW_TYPE_ALIGNED_PAGE, &info);
42 }
43
44 return type;
45 }
46
47
48 /*
49 * Standard Gtk+ function: Create a new list item.
50 * ref_list_item is either another item in the same list, or NULL for
51 * the first item in the list.
52 */
53 DwWidget* a_Dw_list_item_new (DwListItem *ref_list_item)
54 {
55 GtkObject *object;
56
57 object = gtk_object_new (DW_TYPE_LIST_ITEM, NULL);
58 DBG_OBJ_CREATE (object, "DwListItem");
59 p_Dw_aligned_page_set_ref_page (DW_ALIGNED_PAGE (object),
60 (DwAlignedPage*)ref_list_item);
61 return DW_WIDGET (object);
62 }
63
64
65 /*
66 * Standard Gtk+ function.
67 */
68 static void Dw_list_item_class_init (DwListItemClass *klass)
69 {
70 DwAlignedPageClass *aligned_page_class = DW_ALIGNED_PAGE_CLASS (klass);
71
72 aligned_page_class->get_value = Dw_list_item_get_value;
73 aligned_page_class->set_max_value = Dw_list_item_set_max_value;
74 }
75
76
77 /*
78 * Standard Gtk+ function.
79 */
80 static void Dw_list_item_init (DwListItem *list_item)
81 {
82 DW_PAGE(list_item)->list_item = TRUE;
83 }
84
85
86 /*
87 * Implementation of DwAlignedPage::get_value.
88 */
89 static gint32 Dw_list_item_get_value (DwAlignedPage *aligned_page)
90 {
91 DwPage *page = DW_PAGE (aligned_page);
92
93 if (page->num_words == 0)
94 return 0;
95 else
96 return page->words[0].size.width + page->words[0].orig_space;
97 }
98
99
100 /*
101 * Implementation of DwAlignedPage::set_max_value.
102 */
103 static void Dw_list_item_set_max_value (DwAlignedPage *aligned_page,
104 gint32 max_value,
105 gint32 value)
106 {
107 DwPage *page = DW_PAGE (aligned_page);
108
109 page->inner_padding = max_value;
110 page->line1_offset = - value;
111 p_Dw_widget_queue_resize (DW_WIDGET (aligned_page), 0, TRUE);
112 }
113
114
115 /*
116 * This function sets a widget as the list item marker (typically
117 * DwBullet).
118 * (Note: Currently, the following order should be used:
119 * 1. a_Dw_list_item_new,
120 * 2. add the list item to the parent widget, and
121 * 3. a_Dw_list_item_init_with_widget or a_Dw_list_item_init_with_text.)
122 */
123 void a_Dw_list_item_init_with_widget (DwListItem *list_item,
124 DwWidget *widget,
125 DwStyle *style)
126 {
127 DwPage *page = DW_PAGE (list_item);
128
129 a_Dw_page_add_widget (page, widget, style);
130 a_Dw_page_add_space (page, style);
131 p_Dw_aligned_page_update_value (DW_ALIGNED_PAGE (list_item));
132 }
133
134
135 /*
136 * This function sets a text word as the list item marker (typically a
137 * number).
138 */
139 void a_Dw_list_item_init_with_text (DwListItem *list_item,
140 gchar *text,
141 DwStyle *style)
142 {
143 DwPage *page = DW_PAGE (list_item);
144
145 a_Dw_page_add_text (page, text, style);
146 a_Dw_page_add_space (page, style);
147 p_Dw_aligned_page_update_value (DW_ALIGNED_PAGE (list_item));
148 }
0 #ifndef __DW_LIST_ITEM_H__
1 #define __DW_LIST_ITEM_H__
2
3 #include "dw_aligned_page.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_LIST_ITEM (a_Dw_list_item_get_type ())
10 #define DW_LIST_ITEM(obj) GTK_CHECK_CAST (obj,DW_TYPE_LIST_ITEM, \
11 DwListItem)
12 #define DW_LIST_ITEM_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_LIST_ITEM, DwListItemClass)
14 #define DW_IS_LIST_ITEM(obj) GTK_CHECK_TYPE (obj, DW_TYPE_LIST_ITEM)
15
16
17 typedef struct _DwListItem DwListItem;
18 typedef struct _DwListItemClass DwListItemClass;
19
20 struct _DwListItem
21 {
22 DwAlignedPage aligned_page;
23 };
24
25 struct _DwListItemClass
26 {
27 DwAlignedPageClass parent_class;
28 };
29
30
31 GtkType a_Dw_list_item_get_type (void);
32 DwWidget* a_Dw_list_item_new (DwListItem *ref_list_item);
33 void a_Dw_list_item_init_with_widget (DwListItem *list_item,
34 DwWidget *widget,
35 DwStyle *style);
36 void a_Dw_list_item_init_with_text (DwListItem *list_item,
37 gchar *text,
38 DwStyle *style);
39
40 #ifdef __cplusplus
41 }
42 #endif /* __cplusplus */
43
44 #endif /* __DW_LIST_ITEM_H__ */
0 #include "dw_marshal.h"
1
2
3 typedef gboolean (*GtkSignal_BOOL__INT_INT_INT) (GtkObject *object,
4 gint arg1, gint arg2,
5 gint arg3,
6 gpointer user_data);
7
8 void p_Dw_marshal_BOOL__INT_INT_INT (GtkObject *object,
9 GtkSignalFunc Func,
10 gpointer FuncData, GtkArg *args)
11 {
12 GtkSignal_BOOL__INT_INT_INT rfunc;
13 gboolean *return_val;
14 return_val = GTK_RETLOC_BOOL (args[3]);
15 rfunc = (GtkSignal_BOOL__INT_INT_INT) Func;
16 *return_val = rfunc (object,
17 GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
18 GTK_VALUE_INT (args[2]), FuncData);
19 }
20
21
22 typedef gboolean (*GtkSignal_BOOL__INT_INT_INT_POINTER) (GtkObject *object,
23 gint arg1, gint arg2,
24 gint arg3,
25 gpointer arg4,
26 gpointer user_data);
27
28 void p_Dw_marshal_BOOL__INT_INT_INT_POINTER (GtkObject *object,
29 GtkSignalFunc Func,
30 gpointer FuncData, GtkArg *args)
31 {
32 GtkSignal_BOOL__INT_INT_INT_POINTER rfunc;
33 gboolean *return_val;
34 return_val = GTK_RETLOC_BOOL (args[4]);
35 rfunc = (GtkSignal_BOOL__INT_INT_INT_POINTER) Func;
36 *return_val = rfunc (object,
37 GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
38 GTK_VALUE_INT (args[2]), GTK_VALUE_POINTER (args[3]),
39 FuncData);
40 }
41
42 typedef gboolean (*GtkSignal_BOOL__INT_INT_POINTER) (GtkObject *object,
43 gint arg1, gint arg2,
44 gpointer arg3,
45 gpointer user_data);
46
47 void p_Dw_marshal_BOOL__INT_INT_POINTER (GtkObject *object,
48 GtkSignalFunc Func,
49 gpointer FuncData, GtkArg *args)
50 {
51 GtkSignal_BOOL__INT_INT_POINTER rfunc;
52 gboolean *return_val;
53 return_val = GTK_RETLOC_BOOL (args[3]);
54 rfunc = (GtkSignal_BOOL__INT_INT_POINTER) Func;
55 *return_val = rfunc (object,
56 GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
57 GTK_VALUE_POINTER (args[2]), FuncData);
58 }
59
60 typedef gboolean (*GtkSignal_BOOL__POINTER_POINTER) (GtkObject *object,
61 gpointer arg1,
62 gpointer arg2,
63 gpointer user_data);
64
65 void p_Dw_marshal_BOOL__POINTER_POINTER (GtkObject *object,
66 GtkSignalFunc Func,
67 gpointer FuncData, GtkArg *args)
68 {
69 GtkSignal_BOOL__POINTER_POINTER rfunc;
70 gboolean *return_val;
71 return_val = GTK_RETLOC_BOOL (args[2]);
72 rfunc = (GtkSignal_BOOL__POINTER_POINTER) Func;
73 *return_val = rfunc (object,
74 GTK_VALUE_POINTER (args[0]),
75 GTK_VALUE_POINTER (args[1]), FuncData);
76 }
0 #ifndef __DW_MARSHAL_H__
1 #define __DW_MARSHAL_H__
2
3 #include <gtk/gtktypeutils.h>
4 #include <gtk/gtkobject.h>
5
6 void p_Dw_marshal_BOOL__INT_INT_INT (GtkObject *object,
7 GtkSignalFunc Func,
8 gpointer FuncData, GtkArg *args);
9 void p_Dw_marshal_BOOL__INT_INT_INT_POINTER (GtkObject *object,
10 GtkSignalFunc Func,
11 gpointer FuncData, GtkArg *args);
12 void p_Dw_marshal_BOOL__INT_INT_POINTER (GtkObject *object,
13 GtkSignalFunc Func,
14 gpointer FuncData, GtkArg *args);
15 void p_Dw_marshal_BOOL__POINTER_POINTER (GtkObject *object,
16 GtkSignalFunc Func,
17 gpointer FuncData, GtkArg *args);
18
19 /*
20 * Marshal fuctions for standard link signals.
21 */
22 #define p_Dw_marshal_link_enter p_Dw_marshal_BOOL__INT_INT_INT
23 #define p_Dw_marshal_link_button p_Dw_marshal_BOOL__INT_INT_INT_POINTER
24
25
26 #endif /* __DW_MARSHAL_H__ */
0 /*
1 * File: dw_page.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Luca Rota <drake@freemail.it>
5 * Copyright (C) 2001-2003 Sebastian Geerken <s.geerken@ping.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * This module contains the Dw_page widget, which is the "back end" to
15 * Web text widgets including html.
16 */
17
18 #include <ctype.h>
19 #include <string.h>
20 #include <stdio.h>
21
22 #include <gtk/gtk.h>
23
24 #include "msg.h"
25 #include "list.h"
26 #include "dw_page.h"
27 #include "dw_marshal.h"
28 #include "dw_gtk_viewport.h"
29
30 #include "prefs.h"
31
32 #define DEBUG_REWRAP_LEVEL 0
33 #define DEBUG_SIZE_LEVEL 10
34 #define DEBUG_EVENT_LEVEL 0
35 #define DEBUG_ITERATOR_LEVEL 0
36
37 /*#define DEBUG_LEVEL 10*/
38 #include "debug.h"
39
40 #define MAX3(a, b, c) (MAX (a, MAX (b, c)))
41
42 static void Dw_page_init (DwPage *page);
43 static void Dw_page_class_init (DwPageClass *klass);
44
45 static void Dw_page_destroy (GtkObject *object);
46
47 static void Dw_page_size_request (DwWidget *widget,
48 DwRequisition *requisition);
49 static void Dw_page_get_extremes (DwWidget *widget,
50 DwExtremes *extremes);
51 static void Dw_page_size_allocate (DwWidget *widget,
52 DwAllocation *allocation);
53 static void Dw_page_mark_change (DwWidget *widget,
54 gint ref);
55 static void Dw_page_set_width (DwWidget *widget,
56 gint32 width);
57 static void Dw_page_set_ascent (DwWidget *widget,
58 gint32 ascent);
59 static void Dw_page_set_descent (DwWidget *widget,
60 gint32 descent);
61 static void Dw_page_draw (DwWidget *page,
62 DwRectangle *area,
63 GdkEventExpose *event);
64 static gboolean Dw_page_button_press (DwWidget *widget,
65 gint32 x,
66 gint32 y,
67 GdkEventButton *event);
68 static gboolean Dw_page_button_release(DwWidget *widget,
69 gint32 x,
70 gint32 y,
71 GdkEventButton *event);
72 static gboolean Dw_page_motion_notify (DwWidget *widget,
73 gint32 x,
74 gint32 y,
75 GdkEventMotion *event);
76 static gboolean Dw_page_leave_notify (DwWidget *widget,
77 DwWidget *next_widget,
78 GdkEventMotion *event);
79
80
81 static void Dw_page_add (DwContainer *container,
82 DwWidget *widget);
83 static void Dw_page_remove (DwContainer *container,
84 DwWidget *widget);
85 static void Dw_page_forall (DwContainer *container,
86 DwCallback callback,
87 gpointer callback_data);
88 static void Dw_page_real_word_wrap (DwPage *page,
89 gint word_ind);
90
91 static DwIterator* Dw_page_iterator (DwWidget *widget,
92 gint mask,
93 gboolean at_end);
94 static gboolean Dw_page_iterator_next (DwIterator *it);
95 static gboolean Dw_page_iterator_prev (DwIterator *it);
96 static void Dw_page_iterator_highlight (DwIterator *it,
97 gint start,
98 gint end,
99 DwHighlightLayer layer);
100 static void Dw_page_iterator_get_allocation (DwIterator *it,
101 gint start,
102 gint end,
103 DwAllocation *allocation);
104
105 static void Dw_page_rewrap (DwPage *page);
106
107 /*
108 * Returns the x offset (the indentation plus any offset needed for
109 * centering or right justification) for the line. The offset returned
110 * is relative to the page *content* (i.e. without border etc.).
111 */
112 #define Dw_page_line_x_offset(page, line) \
113 ( (page)->inner_padding + (line)->left_offset + \
114 ((line) == (page)->lines ? (page)->line1_offset_eff : 0) )
115
116 /*
117 * Like Dw_style_box_offset_x, but relative to the allocation (i.e.
118 * including border etc.).
119 */
120 #define Dw_page_line_total_x_offset(page, line) \
121 ( Dw_page_line_x_offset (page, line) + \
122 p_Dw_style_box_offset_x (((DwWidget*)(page))->style) )
123
124 /*
125 * Returns the y offset (within the widget) of a line.
126 */
127 #define Dw_page_line_total_y_offset(page, line) \
128 Dw_page_line_total_y_offset_allocation(page, line, \
129 &((DwWidget*)(page))->allocation)
130
131 /*
132 * Like Dw_page_line_total_y_offset, but with the allocation as parameter.
133 */
134 #define Dw_page_line_total_y_offset_allocation(page, line, allocation) \
135 ( (allocation)->ascent - (page)->lines[0].ascent + (line)->top )
136
137 /*
138 * Similar to Dw_page_line_total_y_offset.
139 */
140 #define Dw_page_line_total_y_offset_i(page, line_index) \
141 Dw_page_line_total_y_offset(page, &(page)->lines[line_index])
142
143 #define Dw_page_word_wrap(page, word_ind) \
144 (((DwPageClass*)(((GtkObject*)(page))->klass))->word_wrap (page, word_ind))
145
146 enum
147 {
148 LINK_ENTERED,
149 LINK_PRESSED,
150 LINK_RELEASED,
151 LINK_CLICKED,
152 LAST_SIGNAL
153 };
154
155 static DwContainerClass *parent_class;
156 static guint page_signals[LAST_SIGNAL] = { 0 };
157
158
159 /*
160 * Return the type of DwPage
161 */
162 GtkType a_Dw_page_get_type (void)
163 {
164 static GtkType type = 0;
165
166 if (!type) {
167 GtkTypeInfo info = {
168 "DwPage",
169 sizeof (DwPage),
170 sizeof (DwPageClass),
171 (GtkClassInitFunc) Dw_page_class_init,
172 (GtkObjectInitFunc) Dw_page_init,
173 (GtkArgSetFunc) NULL,
174 (GtkArgGetFunc) NULL,
175 (GtkClassInitFunc) NULL
176 };
177
178 type = gtk_type_unique (DW_TYPE_CONTAINER, &info);
179 }
180
181 return type;
182 }
183
184
185 /*
186 * Create a new DwPage
187 */
188 DwWidget* a_Dw_page_new (void)
189 {
190 GtkObject *object;
191
192 object = gtk_object_new (DW_TYPE_PAGE, NULL);
193 DBG_OBJ_CREATE (object, "DwPage");
194 return DW_WIDGET (object);
195 }
196
197
198 /*
199 * Initialize a DwPage
200 */
201 static void Dw_page_init (DwPage *page)
202 {
203 DW_WIDGET_SET_FLAGS (page, DW_USES_HINTS);
204
205 page->list_item = FALSE;
206 page->inner_padding = 0;
207 page->line1_offset = 0;
208 page->line1_offset_eff = 0;
209 page->ignore_line1_offset_sometimes = FALSE;
210
211 /*
212 * The initial sizes of page->lines and page->words should not be
213 * too high, since this will waste much memory with tables
214 * containing many small cells. The few more calls to realloc
215 * should not decrease the speed considerably.
216 * (Current setting is for minimal memory usage. An interesting fact
217 * is that high values decrease speed due to memory handling overhead!)
218 * todo: Some tests would be useful.
219 */
220 page->num_lines_max = 1; /* 2 */
221 page->num_lines = 0;
222 page->lines = NULL; /* g_new(DwPageLine, page->num_lines_max); */
223
224 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
225
226 page->num_words_max = 1; /* 8 */
227 page->num_words = 0;
228 page->words = NULL; /* g_new(DwPageWord, page->num_words_max); */
229
230 page->last_line_width = 0;
231 page->last_line_par_min = 0;
232 page->last_line_par_max = 0;
233 page->wrap_ref = -1;
234
235 DBG_OBJ_SET_NUM(page, "last_line_width", page->last_line_width);
236 DBG_OBJ_SET_NUM(page, "last_line_par_min", page->last_line_par_min);
237 DBG_OBJ_SET_NUM(page, "last_line_par_max", page->last_line_par_max);
238 DBG_OBJ_SET_NUM(page, "wrap_ref", page->wrap_ref);
239
240 page->hover_link = -1;
241
242 /* random values */
243 page->avail_width = 100;
244 page->avail_ascent = 100;
245 page->avail_descent = 0;
246
247 page->hover_tooltip = NULL;
248 }
249
250 /*
251 * Initialize the DwPage's class
252 */
253 static void Dw_page_class_init (DwPageClass *klass)
254 {
255 GtkObjectClass *object_class;
256 DwWidgetClass *widget_class;
257 DwContainerClass *container_class;
258
259 object_class = (GtkObjectClass*) klass;
260 widget_class = (DwWidgetClass*) klass;
261 container_class = (DwContainerClass*) klass;
262 parent_class = gtk_type_class (DW_TYPE_CONTAINER);
263
264 page_signals[LINK_ENTERED] =
265 gtk_signal_new ("link_entered",
266 GTK_RUN_LAST,
267 object_class->type,
268 GTK_SIGNAL_OFFSET (DwPageClass, link_entered),
269 p_Dw_marshal_link_enter,
270 GTK_TYPE_BOOL,
271 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
272 page_signals[LINK_PRESSED] =
273 gtk_signal_new ("link_pressed",
274 GTK_RUN_LAST,
275 object_class->type,
276 GTK_SIGNAL_OFFSET (DwPageClass, link_pressed),
277 p_Dw_marshal_link_button,
278 GTK_TYPE_BOOL,
279 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
280 GTK_TYPE_GDK_EVENT);
281 page_signals[LINK_RELEASED] =
282 gtk_signal_new ("link_released",
283 GTK_RUN_LAST,
284 object_class->type,
285 GTK_SIGNAL_OFFSET (DwPageClass, link_released),
286 p_Dw_marshal_link_button,
287 GTK_TYPE_BOOL,
288 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
289 GTK_TYPE_GDK_EVENT);
290 page_signals[LINK_CLICKED] =
291 gtk_signal_new ("link_clicked",
292 GTK_RUN_LAST,
293 object_class->type,
294 GTK_SIGNAL_OFFSET (DwPageClass, link_clicked),
295 p_Dw_marshal_link_button,
296 GTK_TYPE_BOOL,
297 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
298 GTK_TYPE_GDK_EVENT);
299 gtk_object_class_add_signals (object_class, page_signals, LAST_SIGNAL);
300
301 object_class->destroy = Dw_page_destroy;
302
303 widget_class->size_request = Dw_page_size_request;
304 widget_class->get_extremes = Dw_page_get_extremes;
305 widget_class->size_allocate = Dw_page_size_allocate;
306 widget_class->mark_size_change = Dw_page_mark_change;
307 widget_class->mark_extremes_change = Dw_page_mark_change;
308 widget_class->set_width = Dw_page_set_width;
309 widget_class->set_ascent = Dw_page_set_ascent;
310 widget_class->set_descent = Dw_page_set_descent;
311 widget_class->draw = Dw_page_draw;
312 widget_class->button_press_event = Dw_page_button_press;
313 widget_class->button_release_event = Dw_page_button_release;
314 widget_class->motion_notify_event = Dw_page_motion_notify;
315 widget_class->leave_notify_event = Dw_page_leave_notify;
316 widget_class->iterator = Dw_page_iterator;
317
318 container_class->add = Dw_page_add;
319 container_class->remove = Dw_page_remove;
320 container_class->forall = Dw_page_forall;
321
322 klass->link_entered = NULL;
323 klass->link_pressed = NULL;
324 klass->link_released = NULL;
325 klass->link_clicked = NULL;
326 klass->word_wrap = Dw_page_real_word_wrap;
327 }
328
329 /*
330 * Destroy the page (standard Gtk+ function)
331 */
332 static void Dw_page_destroy (GtkObject *object)
333 {
334 DwWidget *widget = DW_WIDGET (object);
335 DwPage *page = DW_PAGE (object);
336 DwPageWord *word;
337 gint i;
338
339 _MSG ("Dw_page_destroy\n");
340
341 for (i = 0; i < page->num_words; i++) {
342 word = &page->words[i];
343 if (word->content.type == DW_CONTENT_WIDGET)
344 gtk_object_unref (GTK_OBJECT(word->content.data.widget));
345 else if (word->content.type == DW_CONTENT_TEXT)
346 g_free (word->content.data.text);
347 else if (word->content.type == DW_CONTENT_ANCHOR)
348 /* This also frees the names (see p_Dw_gtk_viewport_remove_anchor()
349 * and related). */
350 p_Dw_gtk_viewport_remove_anchor(widget, word->content.data.anchor);
351
352 a_Dw_style_unref (word->style);
353 a_Dw_style_unref (word->space_style);
354 }
355
356 g_free (page->lines);
357 g_free (page->words);
358
359 /* Make sure we don't own widgets anymore. Necessary before call of
360 parent_class->destroy. */
361 page->num_words = 0;
362 page->num_lines = 0;
363
364 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
365
366 GTK_OBJECT_CLASS(parent_class)->destroy (object);
367 }
368
369
370 /*
371 * Standard Dw function
372 *
373 * The ascent of a page is the ascent of the first line, plus
374 * padding/border/margin. This can be used to align the first lines
375 * of several pages in a horizontal line.
376 */
377 static void Dw_page_size_request (DwWidget *widget,
378 DwRequisition *requisition)
379 {
380 DwPage *page = DW_PAGE (widget);
381 DwPageLine *last_line;
382
383 Dw_page_rewrap (page);
384
385 if (page->num_lines > 0) {
386 last_line = &page->lines[page->num_lines - 1];
387 requisition->width =
388 MAX (last_line->max_line_width, page->last_line_width);
389 /* Note: the break_space of the last line is ignored, so breaks
390 at the end of a page are not visible. */
391 requisition->ascent = page->lines[0].ascent;
392 requisition->descent = last_line->top
393 + last_line->ascent + last_line->descent - page->lines[0].ascent;
394 } else {
395 requisition->width = page->last_line_width;
396 requisition->ascent = 0;
397 requisition->descent = 0;
398 }
399
400 requisition->width +=
401 page->inner_padding + p_Dw_style_box_diff_width (widget->style);
402 requisition->ascent += p_Dw_style_box_offset_y (widget->style);
403 requisition->descent += p_Dw_style_box_rest_height (widget->style);
404
405 if (requisition->width < page->avail_width)
406 requisition->width = page->avail_width;
407 }
408
409
410 /*
411 * Get the extremes of a word within a page.
412 */
413 static void Dw_page_get_word_extremes (DwPageWord *word,
414 DwExtremes *extremes)
415 {
416 if (word->content.type == DW_CONTENT_WIDGET) {
417 if (DW_WIDGET_USES_HINTS (word->content.data.widget))
418 p_Dw_widget_get_extremes (word->content.data.widget, extremes);
419 else {
420 if (DW_STYLE_IS_PER_LENGTH(word->content.data.widget->style->width)) {
421 extremes->min_width = 0;
422 if (DW_WIDGET_HAS_CONTENT (word->content.data.widget))
423 extremes->max_width = DW_INFINITY;
424 else
425 extremes->max_width = 0;
426 } else if (DW_STYLE_IS_ABS_LENGTH
427 (word->content.data.widget->style->width)) {
428 /* Fixed lengths are only applied to the content, so we have to
429 * add padding, border and margin. */
430 extremes->min_width = extremes->max_width =
431 DW_STYLE_ABS_LENGTH_VAL(word->content.data.widget->style->width)
432 + p_Dw_style_box_diff_width(word->style);
433 } else
434 p_Dw_widget_get_extremes (word->content.data.widget, extremes);
435 }
436 } else {
437 extremes->min_width = word->size.width;
438 extremes->max_width = word->size.width;
439 }
440 }
441
442 /*
443 * Standard Dw function
444 */
445 static void Dw_page_get_extremes (DwWidget *widget,
446 DwExtremes *extremes)
447 {
448 DwPage *page = DW_PAGE (widget);
449 DwExtremes word_extremes;
450 DwPageLine *line;
451 DwPageWord *word;
452 gint word_index, line_index;
453 gint32 par_min, par_max;
454 gboolean nowrap;
455
456 DBG_MSG (widget, "extremes", 0, "Dw_page_get_extremes");
457 DBG_MSG_START (widget);
458
459 if (page->num_lines == 0) {
460 /* empty page */
461 extremes->min_width = 0;
462 extremes->max_width = 0;
463 } else if (page->wrap_ref == -1) {
464 /* no rewrap necessary -> values in lines are up to date */
465 line = &page->lines[page->num_lines - 1];
466 /* Historical note: The former distinction between lines with and without
467 * words[first_word]->nowrap set is no longer necessary, since
468 * Dw_page_real_word_wrap sets max_word_min to the correct value in any
469 * case. */
470 extremes->min_width = line->max_word_min;
471 extremes->max_width = MAX (line->max_par_max, page->last_line_par_max);
472 DBG_MSG (widget, "extremes", 0, "simple case");
473 } else {
474 /* Calculate the extremes, based on the values in the line from
475 where a rewrap is necessary. */
476 DBG_MSG (widget, "extremes", 0, "complex case");
477
478 if (page->wrap_ref == 0) {
479 extremes->min_width = 0;
480 extremes->max_width = 0;
481 par_min = 0;
482 par_max = 0;
483 } else {
484 line = &page->lines[page->wrap_ref];
485 extremes->min_width = line->max_word_min;
486 extremes->max_width = line->max_par_max;
487 par_min = line->par_min;
488 par_max = line->par_max;
489
490 DBG_MSGF (widget, "extremes", 0, "par_min = %d", par_min);
491 }
492
493 _MSG ("*** par_min = %d\n", par_min);
494
495 for (line_index = page->wrap_ref; line_index < page->num_lines;
496 line_index++) {
497
498 DBG_MSGF (widget, "extremes", 0, "line %d", line_index);
499 DBG_MSG_START (widget);
500
501 line = &page->lines[line_index];
502 nowrap = page->words[line->first_word].style->white_space
503 != DW_STYLE_WHITE_SPACE_NORMAL;
504
505 DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n",
506 line_index, page->num_lines, nowrap);
507
508 for (word_index = line->first_word; word_index < line->last_word;
509 word_index++) {
510 word = &page->words[word_index];
511 Dw_page_get_word_extremes (word, &word_extremes);
512
513 /* For the first word, we simply add the line1_offset. */
514 if (!page->ignore_line1_offset_sometimes && word_index == 0) {
515 word_extremes.min_width += page->line1_offset;
516 DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
517 " (next plus %d)\n", page->line1_offset);
518 }
519
520 if (nowrap) {
521 par_min += word_extremes.min_width + word->orig_space;
522 DBG_MSGF (widget, "extremes", 0, "par_min = %d", par_min);
523 } else
524 if (extremes->min_width < word_extremes.min_width)
525 extremes->min_width = word_extremes.min_width;
526
527 par_max += word_extremes.max_width + word->orig_space;
528
529 DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
530 " word %s: max_width = %d\n",
531 a_Dw_content_text (&word->content),
532 word_extremes.max_width);
533 }
534
535 if ( ( line->last_word > line->first_word &&
536 page->words[line->last_word - 1].content.type
537 == DW_CONTENT_BREAK ) ||
538 line_index == page->num_lines - 1 ) {
539 word = &page->words[line->last_word - 1];
540 par_max -= word->orig_space;
541
542 DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
543 " par_max = %d, after word %d (%s)\n",
544 par_max, line->last_word - 1,
545 a_Dw_content_text (&word->content));
546
547 if (extremes->max_width < par_max)
548 extremes->max_width = par_max;
549
550 if (nowrap) {
551 par_min -= word->orig_space;
552 DBG_MSGF (widget, "extremes", 0, "par_min = %d", par_min);
553 if (extremes->min_width < par_min)
554 extremes->min_width = par_min;
555
556 DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
557 " par_min = %d, after word %d (%s)\n",
558 par_min, line->last_word - 1,
559 a_Dw_content_text (&word->content));
560 }
561
562 par_min = 0;
563 par_max = 0;
564 }
565
566 DBG_MSG_END (widget);
567 }
568
569 DEBUG_MSG (DEBUG_SIZE_LEVEL + 3, " Result: %d, %d\n",
570 extremes->min_width, extremes->max_width);
571 }
572
573 DBG_MSGF (widget, "extremes", 0, "width difference: %d + %d",
574 page->inner_padding, p_Dw_style_box_diff_width (widget->style));
575
576 extremes->min_width +=
577 page->inner_padding + p_Dw_style_box_diff_width (widget->style);
578 extremes->max_width +=
579 page->inner_padding + p_Dw_style_box_diff_width (widget->style);
580
581 DBG_MSG_END (widget);
582 }
583
584
585 /*
586 * Standard Dw function
587 */
588 static void Dw_page_size_allocate (DwWidget *widget,
589 DwAllocation *allocation)
590 {
591 DwPage *page;
592 int line_index, word_index;
593 DwPageLine *line;
594 DwPageWord *word;
595 int x_cursor;
596 DwAllocation child_allocation;
597
598 page = DW_PAGE (widget);
599
600 for (line_index = 0; line_index < page->num_lines; line_index++) {
601 line = &(page->lines[line_index]);
602 x_cursor = Dw_page_line_total_x_offset (page, line);
603
604 for (word_index = line->first_word; word_index < line->last_word;
605 word_index++) {
606 word = &(page->words[word_index]);
607
608 switch (word->content.type) {
609 case DW_CONTENT_WIDGET:
610 /* todo: justification within the line is done here! */
611 child_allocation.x = x_cursor + allocation->x;
612 /* align=top:
613 child_allocation.y = line->top + allocation->y;
614 */
615 /* align=bottom (base line) */
616 child_allocation.y = allocation->y +
617 Dw_page_line_total_y_offset_allocation (page, line, allocation)
618 + (line->ascent - word->size.ascent)
619 - word->content.data.widget->style->margin.top;
620 child_allocation.width = word->size.width;
621 child_allocation.ascent = word->size.ascent +
622 word->content.data.widget->style->margin.top;
623 child_allocation.descent = word->size.descent +
624 word->content.data.widget->style->margin.bottom;
625 p_Dw_widget_size_allocate (word->content.data.widget,
626 &child_allocation);
627 break;
628
629 case DW_CONTENT_ANCHOR:
630 p_Dw_gtk_viewport_change_anchor
631 (widget, word->content.data.anchor,
632 Dw_page_line_total_y_offset_allocation (page, line,
633 allocation));
634 break;
635
636 default:
637 /* make compiler happy */
638 break;
639 }
640
641 x_cursor += (word->size.width + word->eff_space);
642 }
643 }
644 }
645
646
647 /*
648 * Implementation for both mark_size_change and mark_extremes_change.
649 */
650 static void Dw_page_mark_change (DwWidget *widget,
651 gint ref)
652 {
653 DwPage *page;
654
655 if (ref != -1) {
656 page = DW_PAGE (widget);
657
658 DBG_MSGF (page, "wrap", 0, "Dw_page_mark_size_change (ref = %d)", ref);
659
660 if (page->wrap_ref == -1)
661 page->wrap_ref = ref;
662 else
663 page->wrap_ref = MIN (page->wrap_ref, ref);
664
665 DBG_OBJ_SET_NUM (page, "wrap_ref", page->wrap_ref);
666 }
667 }
668
669 /*
670 * Standard Dw function
671 */
672 static void Dw_page_set_width (DwWidget *widget,
673 gint32 width)
674 {
675 DwPage *page;
676
677 page = DW_PAGE (widget);
678
679 /* If limit_text_width is set to YES, a queue_resize may also be
680 necessary. */
681 if (page->avail_width != width || prefs.limit_text_width) {
682 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
683 "Dw_page_set_width: Calling p_Dw_widget_queue_resize, "
684 "in page with %d word(s)\n",
685 page->num_words);
686
687 page->avail_width = width;
688 p_Dw_widget_queue_resize (widget, 0, FALSE);
689 page->must_queue_resize = FALSE;
690 }
691 }
692
693
694 /*
695 * Standard Dw function
696 */
697 static void Dw_page_set_ascent (DwWidget *widget,
698 gint32 ascent)
699 {
700 DwPage *page;
701
702 page = DW_PAGE (widget);
703
704 if (page->avail_ascent != ascent) {
705 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
706 "Dw_page_set_ascent: Calling p_Dw_widget_queue_resize, "
707 "in page with %d word(s)\n",
708 page->num_words);
709
710 page->avail_ascent = ascent;
711 p_Dw_widget_queue_resize (widget, 0, FALSE);
712 page->must_queue_resize = FALSE;
713 }
714 }
715
716
717 /*
718 * Standard Dw function
719 */
720 static void Dw_page_set_descent (DwWidget *widget,
721 gint32 descent)
722 {
723 DwPage *page;
724
725 page = DW_PAGE (widget);
726
727 if (page->avail_descent != descent) {
728 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
729 "Dw_page_set_descent: Calling p_Dw_widget_queue_resize, "
730 "in page with %d word(s)\n",
731 page->num_words);
732
733 page->avail_descent = descent;
734 p_Dw_widget_queue_resize (widget, 0, FALSE);
735 page->must_queue_resize = FALSE;
736 }
737 }
738
739
740 /*
741 * Standard Dw function
742 */
743 static void Dw_page_add (DwContainer *container,
744 DwWidget *widget)
745 {
746 /* todo */
747 }
748
749
750 /*
751 * Standard Dw function
752 */
753 static void Dw_page_remove (DwContainer *container,
754 DwWidget *widget)
755 {
756 /* todo */
757 }
758
759
760 /*
761 * Standard Dw function
762 */
763 static void Dw_page_forall (DwContainer *container,
764 DwCallback callback,
765 gpointer callback_data)
766 {
767 DwPage *page;
768 int word_index;
769 DwPageWord *word;
770
771 page = DW_PAGE (container);
772
773 for (word_index = 0; word_index < page->num_words; word_index++) {
774 word = &page->words[word_index];
775
776 if (word->content.type == DW_CONTENT_WIDGET)
777 (*callback) (word->content.data.widget, callback_data);
778 }
779 }
780
781
782 /*
783 * ...
784 *
785 * avail_line is passed from Dw_page_real_word_wrap, to avoid calculating
786 * it twice.
787 */
788 static void Dw_page_justify_line (DwPage *page, DwPageLine *line,
789 gint32 avail_width)
790 {
791 /* To avoid rounding errors, the calculation is based on accumulated
792 * values (*_cum). */
793 gint i;
794 gint32 orig_space_sum, orig_space_cum;
795 gint32 eff_space_diff_cum, last_eff_space_diff_cum;
796 gint32 diff;
797
798 diff = avail_width - page->last_line_width;
799 if (diff > 0) {
800 orig_space_sum = 0;
801 for (i = line->first_word; i < line->last_word - 1; i++)
802 orig_space_sum += page->words[i].orig_space;
803
804 orig_space_cum = 0;
805 last_eff_space_diff_cum = 0;
806 for (i = line->first_word; i < line->last_word - 1; i++) {
807 orig_space_cum += page->words[i].orig_space;
808
809 if (orig_space_cum == 0)
810 eff_space_diff_cum = last_eff_space_diff_cum;
811 else
812 eff_space_diff_cum = diff * orig_space_cum / orig_space_sum;
813
814 page->words[i].eff_space = page->words[i].orig_space +
815 (eff_space_diff_cum - last_eff_space_diff_cum);
816 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", i,
817 page->words[i].eff_space);
818
819 last_eff_space_diff_cum = eff_space_diff_cum;
820 }
821 }
822 }
823
824 /*
825 * ...
826 */
827 static void Dw_page_add_line (DwPage *page, gint word_ind, gboolean new_par)
828 {
829 DwPageLine *last_line, *plast_line;
830
831 DBG_MSG (page, "wrap", 0, "Dw_page_add_line");
832 DBG_MSG_START (page);
833
834 page->num_lines++;
835 a_List_add (page->lines, page->num_lines, page->num_lines_max);
836 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
837
838 DEBUG_MSG (DEBUG_REWRAP_LEVEL, "--- new line %d in %p, with word %d of %d"
839 "\n", page->num_lines - 1, page, word_ind, page->num_words);
840
841 last_line = &page->lines[page->num_lines - 1];
842
843 if (page->num_lines == 1)
844 plast_line = NULL;
845 else
846 plast_line = &page->lines[page->num_lines - 2];
847
848 if (plast_line) {
849 /* second or more lines: copy values of last line */
850 last_line->top =
851 plast_line->top + plast_line->ascent +
852 plast_line->descent + plast_line->break_space;
853 last_line->max_line_width = plast_line->max_line_width;
854 last_line->max_word_min = plast_line->max_word_min;
855 last_line->max_par_max = plast_line->max_par_max;
856 last_line->par_min = plast_line->par_min;
857 last_line->par_max = plast_line->par_max;
858 } else {
859 /* first line: initialize values */
860 last_line->top = 0;
861 last_line->max_line_width = page->line1_offset_eff;
862 last_line->max_word_min = 0;
863 last_line->max_par_max = 0;
864 last_line->par_min = page->line1_offset_eff;
865 last_line->par_max = page->line1_offset_eff;
866 }
867
868 DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1,
869 last_line->top);
870 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_line_width", page->num_lines - 1,
871 last_line->max_line_width);
872 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1,
873 last_line->max_word_min);
874 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_par_max", page->num_lines - 1,
875 last_line->max_par_max);
876 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
877 last_line->par_min);
878 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
879 last_line->par_max);
880
881 last_line->first_word = word_ind;
882 last_line->ascent = 0;
883 last_line->descent = 0;
884 last_line->margin_descent = 0;
885 last_line->break_space = 0;
886 last_line->left_offset = 0;
887
888 DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
889 last_line->ascent);
890 DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
891 last_line->descent);
892
893 /* update values in line */
894 last_line->max_line_width =
895 MAX (last_line->max_line_width, page->last_line_width);
896
897 if (page->num_lines > 1)
898 page->last_line_width = 0;
899 else
900 page->last_line_width = page->line1_offset_eff;
901
902 if (new_par) {
903 last_line->max_par_max =
904 MAX (last_line->max_par_max, page->last_line_par_max);
905 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_par_max", page->num_lines - 1,
906 last_line->max_par_max);
907
908 if (page->num_lines > 1) {
909 last_line->par_min = 0;
910 last_line->par_max = 0;
911 } else {
912 last_line->par_min = page->line1_offset_eff;
913 last_line->par_max = page->line1_offset_eff;
914 }
915 page->last_line_par_min = 0;
916 page->last_line_par_max = 0;
917
918 DBG_OBJ_SET_NUM(page, "last_line_par_min", page->last_line_par_min);
919 DBG_OBJ_SET_NUM(page, "last_line_par_max", page->last_line_par_max);
920 }
921
922 last_line->par_min = page->last_line_par_min;
923 last_line->par_max = page->last_line_par_max;
924
925 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
926 last_line->par_min);
927 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
928 last_line->par_max);
929
930 DBG_MSG_END (page);
931 }
932
933
934 /*
935 * This function is called in two cases: (i) when a word is added (by
936 * Dw_page_add_word), and (ii) when a page has to be (partially)
937 * rewrapped. It does word wrap, and adds new lines, if necesary.
938 */
939 static void Dw_page_real_word_wrap (DwPage *page, gint word_ind)
940 {
941 DwPageLine *last_line;
942 DwPageWord *word, *prev_word;
943 gint32 avail_width, last_space, left_offset;
944 gboolean new_line = FALSE, new_par = FALSE;
945 DwWidget *widget = DW_WIDGET (page);
946 DwExtremes word_extremes;
947
948 DBG_MSGF (page, "wrap", 0, "Dw_page_real_word_wrap (%d): %s, width = %d",
949 word_ind, a_Dw_content_html (&page->words[word_ind].content),
950 page->words[word_ind].size.width);
951 DBG_MSG_START (page);
952
953 avail_width =
954 page->avail_width - p_Dw_style_box_diff_width (widget->style)
955 - page->inner_padding;
956 if (prefs.limit_text_width &&
957 avail_width > widget->viewport->allocation.width - 10)
958 avail_width = widget->viewport->allocation.width - 10;
959
960 word = &page->words[word_ind];
961
962 if (page->num_lines == 0) {
963 DBG_MSG (page, "wrap", 0, "first line");
964 new_line = TRUE;
965 new_par = TRUE;
966 last_line = NULL;
967 } else {
968 last_line = &page->lines[page->num_lines - 1];
969
970 if (page->num_words > 0) {
971 prev_word = &page->words[word_ind - 1];
972 if (prev_word->content.type == DW_CONTENT_BREAK) {
973 DBG_MSG (page, "wrap", 0, "after a break");
974 /* previous word is a break */
975 new_line = TRUE;
976 new_par = TRUE;
977 } else if (word->style->white_space != DW_STYLE_WHITE_SPACE_NORMAL) {
978 DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)",
979 word->style->white_space);
980 new_line = FALSE;
981 new_par = FALSE;
982 } else {
983 if (last_line->first_word != word_ind) {
984 /* Does new word fit into the last line? */
985 DBG_MSGF (page, "wrap", 0,
986 "word %d (%s) fits? (%d + %d + %d &lt;= %d)...",
987 word_ind, a_Dw_content_html (&word->content),
988 page->last_line_width, prev_word->orig_space,
989 word->size.width, avail_width);
990 new_line = (page->last_line_width + prev_word->orig_space
991 + word->size.width > avail_width);
992 DBG_MSGF (page, "wrap", 0, "... %s.",
993 new_line ? "No" : "Yes");
994 }
995 }
996 }
997 }
998
999 /* Has sometimes the wrong value. */
1000 word->eff_space = word->orig_space;
1001 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", word_ind, word->eff_space);
1002
1003 /* Test, whether line1_offset can be used. */
1004 if (word_ind == 0) {
1005 if (page->ignore_line1_offset_sometimes) {
1006 if (page->line1_offset + word->size.width > avail_width)
1007 page->line1_offset_eff = 0;
1008 else
1009 page->line1_offset_eff = page->line1_offset;
1010 } else
1011 page->line1_offset_eff = page->line1_offset;
1012 }
1013
1014 if (last_line != NULL && new_line && !new_par &&
1015 word->style->text_align == DW_STYLE_TEXT_ALIGN_JUSTIFY)
1016 Dw_page_justify_line (page, last_line, avail_width);
1017
1018 if (new_line) {
1019 Dw_page_add_line (page, word_ind, new_par);
1020 last_line = &page->lines[page->num_lines - 1];
1021 }
1022
1023 last_line->last_word = word_ind + 1;
1024 last_line->ascent = MAX (last_line->ascent, word->size.ascent);
1025 last_line->descent = MAX (last_line->descent, word->size.descent);
1026
1027 DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
1028 last_line->ascent);
1029 DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
1030 last_line->descent);
1031
1032 if (word->content.type == DW_CONTENT_WIDGET) {
1033 last_line->margin_descent =
1034 MAX (last_line->margin_descent,
1035 word->size.descent +
1036 word->content.data.widget->style->margin.bottom);
1037
1038 DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
1039 last_line->descent);
1040
1041 /* If the widget is not in the first line of the paragraph, its top
1042 * margin may make the line higher.
1043 */
1044 if (page->num_lines > 1) {
1045 /* Here, we know already what the break and the bottom margin
1046 * contributed to the space before this line.
1047 */
1048 last_line->ascent =
1049 MAX (last_line->ascent,
1050 word->size.ascent
1051 + word->content.data.widget->style->margin.top);
1052
1053 DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
1054 last_line->ascent);
1055 }
1056 } else
1057 last_line->margin_descent =
1058 MAX (last_line->margin_descent, last_line->descent);
1059
1060 Dw_page_get_word_extremes (word, &word_extremes);
1061 last_space = (word_ind > 0) ? page->words[word_ind - 1].orig_space : 0;
1062
1063 if (word->content.type == DW_CONTENT_BREAK)
1064 last_line->break_space =
1065 MAX3 (word->content.data.break_space,
1066 last_line->margin_descent - last_line->descent,
1067 last_line->break_space);
1068
1069 page->last_line_width += word->size.width;
1070 if (!new_line)
1071 page->last_line_width += last_space;
1072
1073 page->last_line_par_min += word_extremes.max_width;
1074 page->last_line_par_max += word_extremes.max_width;
1075 if (!new_par) {
1076 page->last_line_par_min += last_space;
1077 page->last_line_par_max += last_space;
1078 }
1079
1080 if (word->style->white_space != DW_STYLE_WHITE_SPACE_NORMAL) {
1081 last_line->par_min += word_extremes.min_width + last_space;
1082 /* This may also increase the accumulated minimum word width. */
1083 last_line->max_word_min =
1084 MAX (last_line->max_word_min, last_line->par_min);
1085 /* NOTE: Most code relies on that all values of nowrap are equal for all
1086 * words within one line. */
1087 } else
1088 /* Simple case. */
1089 last_line->max_word_min =
1090 MAX (last_line->max_word_min, word_extremes.min_width);
1091
1092 DBG_OBJ_SET_NUM(page, "last_line_par_min", page->last_line_par_min);
1093 DBG_OBJ_SET_NUM(page, "last_line_par_max", page->last_line_par_max);
1094 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
1095 last_line->par_min);
1096 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
1097 last_line->par_max);
1098 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1,
1099 last_line->max_word_min);
1100
1101 /* Finally, justify the line. Breaks are ignored, since the HTML
1102 * parser sometimes assignes the wrong style to them. (todo: ) */
1103 if (word->content.type != DW_CONTENT_BREAK) {
1104 switch (word->style->text_align) {
1105 case DW_STYLE_TEXT_ALIGN_LEFT:
1106 case DW_STYLE_TEXT_ALIGN_JUSTIFY: /* see some lines above */
1107 case DW_STYLE_TEXT_ALIGN_STRING: /* handled elsewhere (in future) */
1108 left_offset = 0;
1109 break;
1110
1111 case DW_STYLE_TEXT_ALIGN_RIGHT:
1112 left_offset = avail_width - page->last_line_width;
1113 break;
1114
1115 case DW_STYLE_TEXT_ALIGN_CENTER:
1116 left_offset = (avail_width - page->last_line_width) / 2;
1117 break;
1118
1119 default:
1120 /* compiler happiness */
1121 left_offset = 0;
1122 }
1123
1124 /* For large lines (images etc), which do not fit into the viewport: */
1125 if (left_offset < 0)
1126 left_offset = 0;
1127
1128 if (page->list_item && last_line == page->lines) {
1129 /* List item markers are always on the left. */
1130 last_line->left_offset = 0;
1131 page->words[0].eff_space = page->words[0].orig_space + left_offset;
1132 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0,
1133 page->words[0].eff_space);
1134 } else
1135 last_line->left_offset = left_offset;
1136 }
1137
1138 page->must_queue_resize = TRUE;
1139
1140 DBG_MSG_END (page);
1141 }
1142
1143
1144 /*
1145 * Calculate the size of a widget within the page.
1146 * (Subject of change in the near future!)
1147 */
1148 static void Dw_page_calc_widget_size (DwPage *page,
1149 DwWidget *widget,
1150 DwRequisition *size)
1151 {
1152 DwRequisition requisition;
1153 gint32 avail_width, avail_ascent, avail_descent;
1154
1155 /* We ignore line1_offset[_eff]. */
1156 avail_width =
1157 page->avail_width - p_Dw_style_box_diff_width (DW_WIDGET(page)->style)
1158 - page->inner_padding;
1159 avail_ascent =
1160 page->avail_ascent - p_Dw_style_box_diff_height (DW_WIDGET(page)->style);
1161 avail_descent = page->avail_descent;
1162
1163 if (DW_WIDGET_USES_HINTS (widget)) {
1164 p_Dw_widget_set_width (widget, avail_width);
1165 p_Dw_widget_set_ascent (widget, avail_ascent);
1166 p_Dw_widget_set_descent (widget, avail_descent);
1167 p_Dw_widget_size_request (widget, size);
1168 size->ascent -= widget->style->margin.top;
1169 size->descent -= widget->style->margin.bottom;
1170 } else {
1171 /* TODO: Use margin.{top|bottom} here, like above.
1172 * (No harm for the next future.) */
1173 if (widget->style->width == DW_STYLE_LENGTH_AUTO ||
1174 widget->style->height == DW_STYLE_LENGTH_AUTO)
1175 p_Dw_widget_size_request (widget, &requisition);
1176
1177 if (widget->style->width == DW_STYLE_LENGTH_AUTO)
1178 size->width = requisition.width;
1179 else if (DW_STYLE_IS_ABS_LENGTH (widget->style->width))
1180 /* Fixed lengths are only applied to the content, so we have to
1181 * add padding, border and margin. */
1182 size->width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width)
1183 + p_Dw_style_box_diff_width (widget->style);
1184 else
1185 size->width =
1186 DW_STYLE_PER_LENGTH_VAL (widget->style->width) * avail_width;
1187
1188 if (widget->style->height == DW_STYLE_LENGTH_AUTO) {
1189 size->ascent = requisition.ascent;
1190 size->descent = requisition.descent;
1191 } else if (DW_STYLE_IS_ABS_LENGTH (widget->style->height)) {
1192 /* Fixed lengths are only applied to the content, so we have to
1193 * add padding, border and margin. */
1194 size->ascent =
1195 DW_STYLE_ABS_LENGTH_VAL (widget->style->height)
1196 + p_Dw_style_box_diff_height (widget->style);
1197 size->descent = 0;
1198 } else {
1199 size->ascent =
1200 DW_STYLE_PER_LENGTH_VAL (widget->style->height) * avail_ascent;
1201 size->descent =
1202 DW_STYLE_PER_LENGTH_VAL (widget->style->height) * avail_descent;
1203 }
1204 }
1205 }
1206
1207
1208 /*
1209 * Rewrap the page from the line from which this is necessary.
1210 * There are basically two times we'll want to do this:
1211 * either when the viewport is resized, or when the size changes on one
1212 * of the child widgets.
1213 */
1214 static void Dw_page_rewrap (DwPage *page)
1215 {
1216 DwWidget *widget;
1217 gint i, word_index;
1218 DwPageWord *word;
1219 DwPageLine *last_line;
1220
1221 if (page->wrap_ref == -1)
1222 /* page does not have to be rewrapped */
1223 return;
1224
1225 widget = DW_WIDGET (page);
1226
1227 DBG_MSGF (page, "wrap", 0,
1228 "Dw_page_rewrap: page->wrap_ref = %d, in page with %d word(s)",
1229 page->wrap_ref, page->num_words);
1230 DBG_MSG_START (page);
1231
1232 /* All lines up from page->wrap_ref will be rebuild from the word list,
1233 * the line list up from this position is rebuild. */
1234 page->num_lines = page->wrap_ref;
1235 page->last_line_width = 0;
1236 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
1237 DBG_OBJ_SET_NUM(page, "last_line_width", page->last_line_width);
1238
1239 /* In the word list, we start at the last word, plus one (see definition
1240 * of last_word), in the line before. */
1241 if (page->wrap_ref > 0) {
1242 /* Note: In this case, Dw_page_real_word_wrap will immediately find
1243 * the need to rewrap the line, since we start with the last one (plus
1244 * one). This is also the reason, why page->last_line_width is set
1245 * to the length of the line. */
1246 last_line = &page->lines[page->num_lines - 1];
1247
1248 page->last_line_par_min = last_line->par_min;
1249 page->last_line_par_max = last_line->par_max;
1250
1251 word_index = last_line->last_word;
1252 for (i = last_line->first_word; i < last_line->last_word - 1; i++)
1253 page->last_line_width += (page->words[i].size.width +
1254 page->words[i].orig_space);
1255 page->last_line_width +=
1256 page->words[last_line->last_word - 1].size.width;
1257 } else {
1258 page->last_line_par_min = 0;
1259 page->last_line_par_max = 0;
1260
1261 word_index = 0;
1262 }
1263
1264 for (; word_index < page->num_words; word_index++) {
1265 word = &page->words[word_index];
1266
1267 if (word->content.type == DW_CONTENT_WIDGET)
1268 Dw_page_calc_widget_size (page, word->content.data.widget,
1269 &word->size);
1270 Dw_page_word_wrap (page, word_index);
1271
1272 if (word->content.type == DW_CONTENT_WIDGET) {
1273 word->content.data.widget->parent_ref = page->num_lines - 1;
1274 DBG_OBJ_SET_NUM (word->content.data.widget, "parent_ref",
1275 word->content.data.widget->parent_ref);
1276 }
1277
1278 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
1279 "Assigning parent_ref = %d to rewrapped word %d, "
1280 "in page with %d word(s)\n",
1281 page->num_lines - 1, word_index, page->num_words);
1282
1283 if ( word->content.type == DW_CONTENT_ANCHOR )
1284 p_Dw_gtk_viewport_change_anchor
1285 (widget, word->content.data.anchor,
1286 Dw_page_line_total_y_offset (page,
1287 &page->lines[page->num_lines - 1]));
1288 }
1289
1290 /* Next time, the page will not have to be rewrapped. */
1291 page->wrap_ref = -1;
1292
1293 DBG_MSG_END (page);
1294 }
1295
1296 /*
1297 * Paint a line
1298 * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!)
1299 * - area is used always (ev. set it to event->area)
1300 * - event is only used when is_expose
1301 */
1302 static void Dw_page_draw_line (DwPage *page,
1303 DwPageLine *line,
1304 DwRectangle *area,
1305 GdkEventExpose *event)
1306 {
1307 DwWidget *widget;
1308 DwPageWord *word;
1309 gint word_index;
1310 gint32 x_widget, y_widget, x_viewport, y_viewport, y_viewport_base;
1311 gint32 start_hl, width_hl;
1312 gint32 diff, word_len, eff_hl_end, layer;
1313 DwWidget *child;
1314 DwRectangle child_area;
1315 GdkWindow *window;
1316 GdkGC *gc, *hl_gc;
1317 DwStyleColor *page_bg_color, *word_bg_color;
1318
1319 /* Here's an idea on how to optimize this routine to minimize the number
1320 * of calls to gdk_draw_string:
1321 *
1322 * Copy the text from the words into a buffer, adding a new word
1323 * only if: the attributes match, and the spacing is either zero or
1324 * equal to the width of ' '. In the latter case, copy a " " into
1325 * the buffer. Then draw the buffer. */
1326
1327 widget = DW_WIDGET (page);
1328 window = DW_WIDGET_WINDOW (widget);
1329 page_bg_color = p_Dw_widget_get_bg_color (widget);
1330
1331 x_widget = Dw_page_line_total_x_offset(page,line);
1332 x_viewport =
1333 p_Dw_widget_x_world_to_viewport (widget,
1334 widget->allocation.x + x_widget);
1335 y_widget = Dw_page_line_total_y_offset(page,line);
1336 y_viewport =
1337 p_Dw_widget_y_world_to_viewport (widget,
1338 widget->allocation.y + y_widget);
1339 y_viewport_base = y_viewport + line->ascent;
1340
1341 for (word_index = line->first_word; word_index < line->last_word;
1342 word_index++) {
1343 word = &page->words[word_index];
1344 diff = 0;
1345 gc = word->style->color->gc;
1346
1347
1348 DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.x", word_index,
1349 x_widget);
1350 DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.y", word_index,
1351 y_widget);
1352
1353 switch (word->content.type) {
1354 case DW_CONTENT_TEXT:
1355 if (word->style->background_color)
1356 word_bg_color = word->style->background_color;
1357 else
1358 word_bg_color = page_bg_color;
1359
1360 /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
1361 if (word->style->valign == DW_STYLE_VALIGN_SUB)
1362 diff = word->size.ascent / 2;
1363 else if (word->style->valign == DW_STYLE_VALIGN_SUPER)
1364 diff -= word->size.ascent / 3;
1365
1366 /* Draw background (color, image), when given. */
1367 /* todo: Test (word->style->background_color) is incomplete, and
1368 * should in future include background images. */
1369 if (word->style->background_color &&
1370 word->size.width > 0)
1371 p_Dw_widget_draw_box (widget, word->style, area,
1372 x_widget,
1373 y_widget + line->ascent - word->size.ascent,
1374 word->size.width,
1375 word->size.ascent + word->size.descent,
1376 FALSE);
1377
1378 /* Draw space background (color, image), when given. */
1379 /* todo: Test (word->space_style->background_color) is incomplete, and
1380 * should in future include background images. */
1381 if (word->space_style->background_color &&
1382 word->eff_space > 0)
1383 p_Dw_widget_draw_box (widget, word->space_style, area,
1384 x_widget + word->size.width,
1385 y_widget + line->ascent - word->size.ascent,
1386 word->eff_space,
1387 word->size.ascent + word->size.descent,
1388 FALSE);
1389
1390 gdk_draw_string (window, word->style->font->font, gc,
1391 x_viewport, y_viewport_base + diff,
1392 word->content.data.text);
1393
1394 /* underline */
1395 if (word->style->text_decoration & DW_STYLE_TEXT_DECORATION_UNDERLINE)
1396 gdk_draw_line (window, gc,
1397 x_viewport, y_viewport_base + 1 + diff,
1398 x_viewport + word->size.width - 1,
1399 y_viewport_base + 1 + diff);
1400 if (word_index + 1 < line->last_word &&
1401 (word->space_style->text_decoration
1402 & DW_STYLE_TEXT_DECORATION_UNDERLINE))
1403 gdk_draw_line (window, gc,
1404 x_viewport + word->size.width,
1405 y_viewport_base + 1 + diff,
1406 x_viewport + word->size.width + word->eff_space - 1,
1407 y_viewport_base + 1 + diff);
1408
1409 /* strike-through */
1410 if (word->style->text_decoration
1411 & DW_STYLE_TEXT_DECORATION_LINE_THROUGH)
1412 gdk_draw_line (window, gc,
1413 x_viewport,
1414 y_viewport_base - word->size.ascent / 2 + diff,
1415 x_viewport + word->size.width - 1,
1416 y_viewport_base - word->size.ascent / 2 + diff);
1417 if (word_index + 1 < line->last_word &&
1418 (word->space_style->text_decoration
1419 & DW_STYLE_TEXT_DECORATION_LINE_THROUGH))
1420 gdk_draw_line (window, gc,
1421 x_viewport + word->size.width,
1422 y_viewport_base - word->size.ascent / 2 + diff,
1423 x_viewport + word->size.width + word->eff_space - 1,
1424 y_viewport_base - word->size.ascent / 2 + diff);
1425
1426 for (layer = 0; layer < DW_HIGHLIGHT_NUM_LAYERS; layer++) {
1427 if (word->hl_start[layer] != -1) {
1428 word_len = strlen (word->content.data.text);
1429 eff_hl_end = MIN (word_len, word->hl_end[layer]);
1430 start_hl = x_viewport +
1431 gdk_text_width (word->style->font->font,
1432 word->content.data.text,
1433 word->hl_start[layer]);
1434 width_hl =
1435 gdk_text_width (word->style->font->font,
1436 word->content.data.text
1437 + word->hl_start[layer],
1438 eff_hl_end - word->hl_start[layer]);
1439
1440 /* If the space after this word highlighted, and this word
1441 * is not the last one in this line, highlight also the
1442 * space. */
1443 /* todo: This should also be done with spaces after non-text
1444 * words, but this is not yet defined very well. */
1445 if (word->hl_end[layer] > eff_hl_end &&
1446 word_index < page->num_words &&
1447 word_index != line->last_word - 1)
1448 width_hl += word->eff_space;
1449
1450
1451 if (width_hl != 0) {
1452 /* Draw background for highlighted text. */
1453 gdk_draw_rectangle (window, word_bg_color->inverse_gc,
1454 TRUE, start_hl,
1455 y_viewport_base - word->size.ascent,
1456 width_hl,
1457 word->size.ascent + word->size.descent);
1458
1459 /* Highlight the text. */
1460 hl_gc = word->style->color->inverse_gc;
1461 gdk_draw_text (window, word->style->font->font, hl_gc,
1462 start_hl, y_viewport_base + diff,
1463 word->content.data.text
1464 + word->hl_start[layer],
1465 eff_hl_end - word->hl_start[layer]);
1466
1467 /* underline and strike-through */
1468 if (word->style->text_decoration
1469 & DW_STYLE_TEXT_DECORATION_UNDERLINE)
1470 gdk_draw_line (window, hl_gc,
1471 start_hl, y_viewport_base + 1 + diff,
1472 start_hl + width_hl - 1,
1473 y_viewport_base + 1 + diff);
1474 if (word->style->text_decoration
1475 & DW_STYLE_TEXT_DECORATION_LINE_THROUGH)
1476 gdk_draw_line (window, hl_gc,
1477 start_hl,
1478 y_viewport_base
1479 - word->size.ascent / 2 + diff,
1480 start_hl + width_hl - 1,
1481 y_viewport_base
1482 - word->size.ascent / 2 + diff);
1483 }
1484 }
1485 }
1486 break;
1487
1488 case DW_CONTENT_WIDGET:
1489 child = word->content.data.widget;
1490 if (p_Dw_widget_intersect (child, area, &child_area))
1491 p_Dw_widget_draw (child, &child_area, event);
1492 break;
1493
1494 case DW_CONTENT_ANCHOR: case DW_CONTENT_BREAK:
1495 /* nothing - an anchor/break isn't seen */
1496 /*
1497 * Historical note:
1498 * > BUG: sometimes anchors have x_space;
1499 * > we subtract that just in case --EG
1500 * This is inconsistent with other parts of the code, so it should
1501 * be tried to prevent this earlier.--SG
1502 */
1503 /*
1504 * x_viewport -= word->size.width + word->eff_space;
1505 * x_widget -= word->size.width + word->eff_space;
1506 */
1507 #if 0
1508 /* Useful for testing: draw breaks. */
1509 if (word->content.type == DW_CONTENT_BREAK)
1510 gdk_draw_rectangle (window, gc, TRUE,
1511 p_Dw_widget_x_world_to_viewport (widget,
1512 widget->allocation.x +
1513 Dw_page_line_total_x_offset(page, line)),
1514 y_viewport_base + line->descent,
1515 DW_WIDGET_CONTENT_WIDTH(widget),
1516 word->content.data.break_space);
1517 #endif
1518 break;
1519
1520 default:
1521 g_warning ("BUG!!! at (%d, %d).", x_viewport, y_viewport_base + diff);
1522 break;
1523 }
1524
1525 x_viewport += word->size.width + word->eff_space;
1526 x_widget += word->size.width + word->eff_space;
1527 }
1528 }
1529
1530 /*
1531 * Find the first line index that includes y, relative to top of widget.
1532 */
1533 static gint Dw_page_find_line_index (DwPage *page, gint y)
1534 {
1535 gint max_index = page->num_lines - 1;
1536 gint step, index, low = 0;
1537
1538 step = (page->num_lines + 1) >> 1;
1539 while ( step > 1 ) {
1540 index = low + step;
1541 if (index <= max_index &&
1542 Dw_page_line_total_y_offset_i (page, index) < y)
1543 low = index;
1544 step = (step + 1) >> 1;
1545 }
1546
1547 if (low < max_index && Dw_page_line_total_y_offset_i (page, low + 1) < y)
1548 low++;
1549
1550 /*
1551 * This new routine returns the line number between (top) and
1552 * (top + size.ascent + size.descent + break_space): the space
1553 * _below_ the line is considered part of the line. Old routine
1554 * returned line number between (top - previous_line->break_space)
1555 * and (top + size.ascent + size.descent): the space _above_ the
1556 * line was considered part of the line. This is important for
1557 * Dw_page_find_link() --EG
1558 * That function has now been inlined into Dw_page_motion_notify() --JV
1559 */
1560 return low;
1561 }
1562
1563 /*
1564 * Find the line of word <word_index>.
1565 */
1566 static gint Dw_page_find_line_of_word (DwPage *page, gint word_index)
1567 {
1568 gint high = page->num_lines - 1, index, low = 0;
1569
1570 g_return_val_if_fail (word_index >= 0, -1);
1571 g_return_val_if_fail (word_index < page->num_words, -1);
1572
1573 while (TRUE) {
1574 index = (low + high) / 2;
1575 if (word_index >= page->lines[index].first_word) {
1576 if (word_index < page->lines[index].last_word)
1577 return index;
1578 else
1579 low = index + 1;
1580 } else
1581 high = index - 1;
1582 }
1583 }
1584
1585 /*
1586 * Draw the actual lines, starting at (x, y) in toplevel Dw coords.
1587 * (former Dw_page_expose_lines)
1588 */
1589 static void Dw_page_draw (DwWidget *widget,
1590 DwRectangle *area,
1591 GdkEventExpose *event)
1592 {
1593 DwPage *page;
1594 gint line_index;
1595 DwPageLine *line;
1596
1597 p_Dw_widget_draw_widget_box (widget, area, FALSE);
1598
1599 page = DW_PAGE (widget);
1600 line_index = Dw_page_find_line_index (page, area->y);
1601
1602 for (; line_index < page->num_lines; line_index++) {
1603 line = &(page->lines[line_index]);
1604 if (Dw_page_line_total_y_offset(page, line) >= area->y + area->height)
1605 break;
1606
1607 Dw_page_draw_line (page, line, area, event);
1608 }
1609 }
1610
1611
1612 /*
1613 * Find the index of the word, or -1.
1614 */
1615 static gint Dw_page_find_word (DwPage *page, gint x, gint y)
1616 {
1617 gint line_index, word_index;
1618 gint x_cursor, last_x_cursor;
1619 DwPageLine *line;
1620 DwPageWord *word;
1621
1622 if ( (line_index = Dw_page_find_line_index (page, y)) >= page->num_lines )
1623 return -1;
1624 line = &page->lines[line_index];
1625 if (Dw_page_line_total_y_offset(page, line) + line->ascent + line->descent
1626 <= y)
1627 return -1;
1628
1629 x_cursor = Dw_page_line_total_x_offset (page, line);
1630 for (word_index = line->first_word; word_index < line->last_word;
1631 word_index++) {
1632 word = &page->words[word_index];
1633 last_x_cursor = x_cursor;
1634 x_cursor += word->size.width + word->eff_space;
1635 if (last_x_cursor <= x && x_cursor > x)
1636 return word_index;
1637 }
1638 return -1;
1639 }
1640
1641 /*
1642 * Construct an iterator for a word.
1643 */
1644 static DwIterator *Dw_page_construct_iterator (DwPage *page, gint word_index)
1645 {
1646 DwIterator *it;
1647
1648 it = Dw_page_iterator (DW_WIDGET (page), DW_CONTENT_ALL, FALSE);
1649 /* DW_CONTENT_TEXT | DW_CONTENT_WIDGET, FALSE); */
1650 ((DwIteratorInt*)it)->pos = word_index;
1651 it->content = page->words[word_index].content;
1652 return it;
1653 }
1654
1655 /*
1656 * Send event to selection.
1657 */
1658 static gboolean Dw_page_send_selection_event (DwPage *page,
1659 gint (*fn) (Selection*,
1660 DwIterator *,
1661 gint, gint,
1662 GdkEventButton*,
1663 gboolean
1664 within_content),
1665 gint32 x, gint32 y,
1666 GdkEventButton *event)
1667 {
1668 DwIterator *it;
1669 DwPageLine *line, *last_line;
1670 gint32 next_word_start_x, word_start_x, word_x, next_word_x, yfirst, ylast;
1671 gint char_pos = 0, word_index, line_index, link;
1672 DwPageWord *word;
1673 gboolean found;
1674 gboolean within_content;
1675
1676 DEBUG_MSG (DEBUG_EVENT_LEVEL,
1677 "Dw_page_send_selection_event: x = %d, y = %d\n", x, y);
1678
1679 if (page->num_words == 0) {
1680 DEBUG_MSG (DEBUG_EVENT_LEVEL, " no words\n");
1681 return FALSE;
1682 }
1683
1684 /* In most cases true, so set here: */
1685 link = -1;
1686 within_content = TRUE;
1687
1688 last_line = &page->lines[page->num_lines - 1];
1689 yfirst = Dw_page_line_total_y_offset_i (page, 0);
1690 ylast =
1691 Dw_page_line_total_y_offset (page, last_line) +
1692 last_line->ascent + last_line->descent;
1693 if (y < yfirst) {
1694 /* Above the first line: take the first word. */
1695 DEBUG_MSG (DEBUG_EVENT_LEVEL, " above first line (at %d)\n", yfirst);
1696 within_content = FALSE;
1697 word_index = 0;
1698 char_pos = 0;
1699 } else if (y >= ylast) {
1700 /* Below the last line: take the last word. */
1701 DEBUG_MSG (DEBUG_EVENT_LEVEL, " below last line (at %d)\n", ylast);
1702 within_content = FALSE;
1703 word_index = page->num_words - 1;
1704 word = &page->words[word_index];
1705 char_pos = word->content.type == DW_CONTENT_TEXT ?
1706 strlen (word->content.data.text) : 0;
1707 } else {
1708 line_index = Dw_page_find_line_index (page, y);
1709 line = &page->lines[line_index];
1710 DEBUG_MSG (DEBUG_EVENT_LEVEL, " in line %d\n", line_index);
1711
1712 /* Pointer within the break space? */
1713 if (y > (Dw_page_line_total_y_offset(page, line) + line->ascent +
1714 line->descent)) {
1715 /* Choose this break. */
1716 DEBUG_MSG (DEBUG_EVENT_LEVEL, " break\n");
1717 within_content = FALSE;
1718 word_index = line->last_word - 1;
1719 char_pos = 0;
1720 } else if (x < Dw_page_line_total_x_offset (page, line)) {
1721 /* Left of the first word in the line. */
1722 DEBUG_MSG (DEBUG_EVENT_LEVEL, " left of this line\n");
1723 word_index = line->first_word;
1724 within_content = FALSE;
1725 char_pos = 0;
1726 } else {
1727 next_word_start_x = Dw_page_line_total_x_offset (page, line);
1728 found = FALSE;
1729 for (word_index = line->first_word;
1730 !found && word_index < line->last_word;
1731 word_index++) {
1732 word = &page->words[word_index];
1733 word_start_x = next_word_start_x;
1734 next_word_start_x += word->size.width + word->eff_space;
1735 DEBUG_MSG (DEBUG_EVENT_LEVEL,
1736 " word %d (%s) from %d to %d, delta = %d + %d\n",
1737 word_index, a_Dw_content_text (&word->content),
1738 word_start_x, next_word_start_x,
1739 word->size.width, word->eff_space);
1740 if (x >= word_start_x && x < next_word_start_x) {
1741 DEBUG_MSG (DEBUG_EVENT_LEVEL,
1742 " found word %d (%s)\n",
1743 word_index, a_Dw_content_text (&word->content));
1744
1745 /* We have found the word. */
1746 if (word->content.type == DW_CONTENT_TEXT) {
1747 /* Search the character the mouse pointer is in.
1748 * next_word_x is the right side of this character. */
1749 char_pos = 0;
1750 while ((next_word_x = word_start_x +
1751 gdk_text_width(word->style->font->font,
1752 word->content.data.text,
1753 char_pos))
1754 <= x)
1755 char_pos++;
1756
1757 /* The left side of this character. */
1758 word_x =
1759 word_start_x + gdk_text_width (word->style->font->font,
1760 word->content.data.text,
1761 char_pos - 1);
1762
1763 /* If the mouse pointer is left from the middle, use the left
1764 * position, otherwise, use the right one. */
1765 if (x <= (word_x + next_word_x) / 2)
1766 char_pos--;
1767 } else {
1768 /* Depends on whether the pointer is within the left or
1769 * right half of the (non-text) word. */
1770 if (x >= (word_start_x + next_word_start_x) / 2)
1771 char_pos = SELECTION_EOW;
1772 else
1773 char_pos = 0;
1774 }
1775
1776 found = TRUE;
1777 link = word->style ? word->style->x_link : -1;
1778 break;
1779 }
1780 }
1781
1782 if (!found) {
1783 /* No word found in this line (i.e. we are on the right side),
1784 * take the last of this line. */
1785 DEBUG_MSG (DEBUG_EVENT_LEVEL, " not found\n");
1786 within_content = FALSE;
1787 word_index = line->last_word - 1;
1788 if (word_index >= page->num_words)
1789 word_index--;
1790 word = &page->words[word_index];
1791 char_pos = word->content.type == DW_CONTENT_TEXT ?
1792 strlen (word->content.data.text) : SELECTION_EOW;
1793 }
1794 }
1795 }
1796
1797 word = &page->words[word_index];
1798 it = Dw_page_construct_iterator (page, word_index);
1799 DEBUG_MSG (DEBUG_EVENT_LEVEL, "-> word %d (of %d), char %d, link %d: %s\n",
1800 word_index, page->num_words, char_pos, link,
1801 a_Dw_iterator_text (it));
1802 return fn (GTK_DW_VIEWPORT(DW_WIDGET(page)->viewport)->selection,
1803 it, char_pos, link, event, within_content);
1804 }
1805
1806
1807 /*
1808 * Standard Dw function.
1809 */
1810 static gboolean Dw_page_button_press (DwWidget *widget,
1811 gint32 x,
1812 gint32 y,
1813 GdkEventButton *event)
1814 {
1815 return Dw_page_send_selection_event (DW_PAGE (widget),
1816 a_Selection_button_press, x, y,
1817 event);
1818 }
1819
1820
1821 /*
1822 * Standard Dw function.
1823 */
1824 static gboolean Dw_page_button_release (DwWidget *widget,
1825 gint32 x,
1826 gint32 y,
1827 GdkEventButton *event)
1828 {
1829 return Dw_page_send_selection_event (DW_PAGE (widget),
1830 a_Selection_button_release, x, y,
1831 event);
1832 }
1833
1834
1835 /*
1836 * Standard Dw function.
1837 */
1838 static gboolean Dw_page_motion_notify (DwWidget *widget,
1839 gint32 x,
1840 gint32 y,
1841 GdkEventMotion *event)
1842 {
1843 DwPage *page = DW_PAGE (widget);
1844 gint link_old, word_index;
1845 DwTooltip *tooltip_old;
1846 gboolean return_val = FALSE;
1847
1848 if (event && (event->state & GDK_BUTTON1_MASK))
1849 return Dw_page_send_selection_event (page,
1850 a_Selection_button_motion, x, y,
1851 NULL);
1852 else {
1853 /*DEBUG_MSG (DEBUG_EVENT_LEVEL, "Dw_page_motion_notify\n");*/
1854 word_index = Dw_page_find_word (page, x, y);
1855
1856 link_old = page->hover_link;
1857 tooltip_old = page->hover_tooltip;
1858
1859 if (word_index == -1) {
1860 page->hover_link = -1;
1861 page->hover_tooltip = NULL;
1862 } else {
1863 page->hover_link = page->words[word_index].style->x_link;
1864 page->hover_tooltip = page->words[word_index].style->x_tooltip;
1865 }
1866
1867 /* Show/hide tooltip */
1868 if (tooltip_old != page->hover_tooltip) {
1869 if (tooltip_old)
1870 a_Dw_tooltip_on_leave (tooltip_old);
1871 if (page->hover_tooltip)
1872 a_Dw_tooltip_on_enter(page->hover_tooltip);
1873 } else if (page->hover_tooltip)
1874 a_Dw_tooltip_on_motion(page->hover_tooltip);
1875
1876 if (page->hover_link != link_old) {
1877 gtk_signal_emit (GTK_OBJECT (widget), page_signals[LINK_ENTERED],
1878 page->hover_link, -1, -1, &return_val);
1879 return return_val;
1880 } else
1881 return (page->hover_link != -1);
1882 }
1883 }
1884
1885
1886 /*
1887 * Standard Dw function.
1888 */
1889 static gboolean Dw_page_leave_notify (DwWidget *widget,
1890 DwWidget *next_widget,
1891 GdkEventMotion *event)
1892 {
1893 DwPage *page = DW_PAGE (widget);
1894 gboolean return_val = FALSE;
1895
1896 if (page->hover_tooltip) {
1897 a_Dw_tooltip_on_leave(page->hover_tooltip);
1898 page->hover_tooltip = NULL;
1899 }
1900
1901 if (page->hover_link != -1) {
1902 page->hover_link = -1;
1903 gtk_signal_emit (GTK_OBJECT (widget), page_signals[LINK_ENTERED],
1904 -1, -1, -1, &return_val);
1905 return return_val;
1906 }
1907
1908 return FALSE;
1909 }
1910
1911
1912 /*
1913 * Add a new word (text, widget etc.) to a page.
1914 */
1915 static DwPageWord *Dw_page_add_word (DwPage *page,
1916 gint width,
1917 gint ascent,
1918 gint descent,
1919 DwStyle *style)
1920 {
1921 DwPageWord *word;
1922 int i;
1923
1924 page->num_words++;
1925 a_List_add (page->words, page->num_words, page->num_words_max);
1926
1927 word = &page->words[page->num_words - 1];
1928 word->size.width = width;
1929 word->size.ascent = ascent;
1930 word->size.descent = descent;
1931 word->orig_space = 0;
1932 word->eff_space = 0;
1933 word->content.space = FALSE;
1934
1935 DBG_OBJ_ARRSET_NUM (page, "words.%d.size.width", page->num_words - 1,
1936 word->size.width);
1937 DBG_OBJ_ARRSET_NUM (page, "words.%d.size.descent", page->num_words - 1,
1938 word->size.descent);
1939 DBG_OBJ_ARRSET_NUM (page, "words.%d.size.ascent", page->num_words - 1,
1940 word->size.ascent);
1941 DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", page->num_words - 1,
1942 word->orig_space);
1943 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", page->num_words - 1,
1944 word->eff_space);
1945 DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", page->num_words - 1,
1946 word->content.space);
1947
1948 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++) {
1949 word->hl_start[i] = -1;
1950 word->hl_end[i] = -1;
1951 }
1952
1953 word->style = style;
1954 word->space_style = style;
1955 a_Dw_style_ref (style);
1956 a_Dw_style_ref (style);
1957
1958 return word;
1959 }
1960
1961 /*
1962 * Calculate the size of a text word.
1963 */
1964 static void Dw_page_calc_text_size (DwPage *page, char *text, DwStyle *style,
1965 DwRequisition *size)
1966 {
1967 size->width = gdk_string_width (style->font->font, text);
1968 size->ascent = style->font->font->ascent;
1969 size->descent = style->font->font->descent;
1970
1971 /* In case of a sub or super script we increase the word's height and
1972 * potentially the line's height.
1973 */
1974 if (style->valign == DW_STYLE_VALIGN_SUB)
1975 size->descent += (size->ascent / 2);
1976 else if (style->valign == DW_STYLE_VALIGN_SUPER)
1977 size->ascent += (size->ascent / 3);
1978 }
1979
1980
1981 /*
1982 * Add a word to the page structure. Stashes the argument pointer in
1983 * the page data structure so that it will be deallocated on destroy.
1984 */
1985 void a_Dw_page_add_text (DwPage *page, char *text, DwStyle *style)
1986 {
1987 DwPageWord *word;
1988 DwRequisition size;
1989
1990 Dw_page_calc_text_size (page, text, style, &size);
1991 word = Dw_page_add_word (page, size.width, size.ascent, size.descent,
1992 style);
1993 word->content.type = DW_CONTENT_TEXT;
1994 word->content.data.text = text;
1995
1996 DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1,
1997 word->content.data.text);
1998
1999 Dw_page_word_wrap (page, page->num_words - 1);
2000 }
2001
2002 /*
2003 * Add a widget (word type) to the page.
2004 */
2005 void a_Dw_page_add_widget (DwPage *page,
2006 DwWidget *widget,
2007 DwStyle *style)
2008 {
2009 DwPageWord *word;
2010 DwRequisition size;
2011
2012 /* We first assign -1 as parent_ref, since the call of widget->size_request
2013 * will otherwise let this DwPage be rewrapped from the beginning.
2014 * (parent_ref is actually undefined, but likely has the value 0.) At the,
2015 * end of this function, the correct value is assigned. */
2016 widget->parent_ref = -1;
2017
2018 p_Dw_widget_set_parent (widget, DW_WIDGET (page));
2019 a_Dw_widget_set_style (widget, style);
2020
2021 Dw_page_calc_widget_size (page, widget, &size);
2022 word =
2023 Dw_page_add_word (page, size.width, size.ascent, size.descent, style);
2024
2025 word->content.type = DW_CONTENT_WIDGET;
2026 word->content.data.widget = widget;
2027
2028 DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", page->num_words - 1,
2029 word->content.data.widget);
2030
2031 Dw_page_word_wrap (page, page->num_words - 1);
2032 word->content.data.widget->parent_ref = page->num_lines - 1;
2033 DBG_OBJ_SET_NUM (word->content.data.widget, "parent_ref",
2034 word->content.data.widget->parent_ref);
2035
2036 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
2037 "Assigning parent_ref = %d to added word %d, "
2038 "in page with %d word(s)\n",
2039 page->num_lines - 1, page->num_words - 1, page->num_words);
2040 }
2041
2042
2043 /*
2044 * Add an anchor to the page. "name" is copied, so no strdup is neccessary for
2045 * the caller.
2046 *
2047 * Return TRUE on success, and FALSE, when this anchor had already been
2048 * added to the widget tree.
2049 */
2050 gboolean a_Dw_page_add_anchor (DwPage *page, const gchar *name, DwStyle *style)
2051 {
2052 DwPageWord *word;
2053 char *copy;
2054 gint32 y;
2055
2056 if (page->num_lines == 0)
2057 y = 0;
2058 else
2059 y = Dw_page_line_total_y_offset_i (page, page->num_lines - 1);
2060
2061 /*
2062 * Since an anchor does not take any space, it is safe to call
2063 * p_Dw_gtk_viewport_add_anchor already here.
2064 */
2065 if ((copy = p_Dw_gtk_viewport_add_anchor(DW_WIDGET(page), name, y)) == NULL)
2066 return FALSE;
2067 else {
2068 word = Dw_page_add_word (page, 0, 0, 0, style);
2069 word->content.type = DW_CONTENT_ANCHOR;
2070 word->content.data.anchor = copy;
2071 Dw_page_word_wrap (page, page->num_words - 1);
2072
2073 DBG_OBJ_ARRSET_STR (page, "words.%d.content.anchor", page->num_words - 1,
2074 word->content.data.anchor);
2075
2076 return TRUE;
2077 }
2078 }
2079
2080
2081 /*
2082 * ?
2083 */
2084 void a_Dw_page_add_space (DwPage *page, DwStyle *style)
2085 {
2086 gint nl, nw;
2087 gint space;
2088
2089 nl = page->num_lines - 1;
2090 if (nl >= 0) {
2091 nw = page->num_words - 1;
2092 if (nw >= 0) {
2093 /* todo: remove this test case */
2094 if (page->words[nw].orig_space != 0) {
2095 _MSG(" a_Dw_page_add_space:: already existing space!!!\n");
2096 }
2097 space = style->font->space_width;
2098 page->words[nw].orig_space = space;
2099 page->words[nw].eff_space = space;
2100 page->words[nw].content.space = TRUE;
2101
2102 DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw,
2103 page->words[nw].orig_space);
2104 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", nw,
2105 page->words[nw].eff_space);
2106 DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw,
2107 page->words[nw].content.space);
2108
2109 a_Dw_style_unref (page->words[nw].space_style);
2110 page->words[nw].space_style = style;
2111 a_Dw_style_ref (style);
2112 }
2113 }
2114 }
2115
2116
2117 /*
2118 * Cause a paragraph break
2119 */
2120 void a_Dw_page_add_parbreak (DwPage *page, gint space, DwStyle *style)
2121 {
2122 DwPageWord *word, *word2;
2123 DwWidget *widget;
2124 DwPage *page2;
2125 gboolean isfirst;
2126 gint lineno;
2127
2128 /* A break may not be the first word of a page, or directly after
2129 the bullet/number (which is the first word) in a list item. (See
2130 also comment in Dw_page_size_request.) */
2131 if (page->num_words == 0 ||
2132 (page->list_item && page->num_words == 1)) {
2133 /* This is a bit hackish: If a break is added as the
2134 first/second word of a page, and the parent widget is also a
2135 DwPage, and there is a break before -- this is the case when
2136 a widget is used as a text box (lists, blockquotes, list
2137 items etc) -- then we simply adjust the break before, in a
2138 way that the space is in any case visible. */
2139
2140 /* Find the widget where to adjust the break_space. */
2141 for (widget = DW_WIDGET (page);
2142 widget->parent && DW_IS_PAGE (widget->parent);
2143 widget = widget->parent) {
2144 page2 = DW_PAGE (widget->parent);
2145 if (page2->list_item)
2146 isfirst = (page2->words[1].content.type == DW_CONTENT_WIDGET
2147 && page2->words[1].content.data.widget == widget);
2148 else
2149 isfirst = (page2->words[0].content.type == DW_CONTENT_WIDGET
2150 && page2->words[0].content.data.widget == widget);
2151 if (!isfirst) {
2152 /* The page we searched for has been found. */
2153 lineno = widget->parent_ref;
2154 if (lineno > 0 &&
2155 (word2 = &page2->words[page2->lines[lineno - 1].first_word]) &&
2156 word2->content.type == DW_CONTENT_BREAK) {
2157 if (word2->content.data.break_space < space) {
2158 word2->content.data.break_space = space;
2159 p_Dw_widget_queue_resize (DW_WIDGET (page2), lineno, FALSE);
2160 page2->must_queue_resize = FALSE;
2161 }
2162 }
2163 return;
2164 }
2165 /* Otherwise continue to examine parents. */
2166 }
2167 /* Return in any case. */
2168 return;
2169 }
2170
2171 /* Another break before? */
2172 if ((word = &page->words[page->num_words - 1]) &&
2173 word->content.type == DW_CONTENT_BREAK) {
2174 word->content.data.break_space =
2175 MAX (word->content.data.break_space, space);
2176 return;
2177 }
2178
2179 word = Dw_page_add_word (page, 0, 0, 0, style);
2180 word->content.type = DW_CONTENT_BREAK;
2181 word->content.data.break_space = space;
2182 Dw_page_word_wrap (page, page->num_words - 1);
2183 }
2184
2185 /*
2186 * Cause a line break.
2187 */
2188 void a_Dw_page_add_linebreak (DwPage *page, DwStyle *style)
2189 {
2190 DwPageWord *word;
2191
2192 if (page->num_words == 0 ||
2193 page->words[page->num_words - 1].content.type == DW_CONTENT_BREAK)
2194 /* An <BR> in an empty line gets the height of the current font
2195 * (why would someone else place it here?), ... */
2196 word = Dw_page_add_word (page, 0, style->font->font->ascent,
2197 style->font->font->descent, style);
2198 else
2199 /* ... otherwise, it has no size (and does not enlarge the line). */
2200 word = Dw_page_add_word (page, 0, 0, 0, style);
2201 word->content.type = DW_CONTENT_BREAK;
2202 word->content.data.break_space = 0;
2203 word->style = style;
2204 Dw_page_word_wrap (page, page->num_words - 1);
2205 }
2206
2207 /*
2208 * This function "hands" the last break of a page "over" to a parent
2209 * page. This is used for "collapsing spaces".
2210 */
2211 void a_Dw_page_hand_over_break (DwPage *page, DwStyle *style)
2212 {
2213 DwPageLine *last_line;
2214 DwWidget *parent;
2215
2216 if (page->num_lines == 0)
2217 return;
2218
2219 last_line = &page->lines[page->num_lines - 1];
2220 if (last_line->break_space != 0 &&
2221 (parent = DW_WIDGET(page)->parent) && DW_IS_PAGE (parent))
2222 a_Dw_page_add_parbreak (DW_PAGE (parent), last_line->break_space, style);
2223 }
2224
2225 /*
2226 * Any words added by a_Dw_page_add_... are not immediately (queued to
2227 * be) drawn, instead, this function must be called. This saves some
2228 * calls to p_Dw_widget_queue_resize.
2229 *
2230 */
2231 void a_Dw_page_flush (DwPage *page)
2232 {
2233 if (page->must_queue_resize) {
2234 p_Dw_widget_queue_resize (DW_WIDGET (page), -1, TRUE);
2235 page->must_queue_resize = FALSE;
2236 }
2237 }
2238
2239 /*
2240 * Change the color of all words with a specific link. Used to
2241 * visualize links opened in a new window.
2242 */
2243 void a_Dw_page_change_link_color (DwPage *page,
2244 gint link,
2245 gint32 new_color)
2246 {
2247 DwStyle *old_style, style_attrs;
2248 gint i, j, changed;
2249 DwWidget *widget = DW_WIDGET (page);
2250
2251 for (i = 0; i < page->num_lines; i++) {
2252 changed = FALSE;
2253
2254 for (j = page->lines[i].first_word; j < page->lines[i].last_word; j++)
2255 if (page->words[j].style->x_link == link) {
2256 old_style = page->words[j].style;
2257
2258 style_attrs = *old_style;
2259 style_attrs.color =
2260 a_Dw_style_color_new (new_color, widget->viewport->window);
2261 page->words[j].style =
2262 a_Dw_style_new (&style_attrs, widget->viewport->window);
2263 /* unref'ing it before may crash dillo! */
2264 a_Dw_style_unref (old_style);
2265 changed = TRUE;
2266 }
2267
2268 if (changed)
2269 p_Dw_widget_queue_draw_area (widget, 0,
2270 Dw_page_line_total_y_offset_i (page, i),
2271 widget->allocation.width,
2272 page->lines[i].ascent
2273 + page->lines[i].descent);
2274 }
2275 }
2276
2277 /*
2278 * Assign a new style to some text words. Other words than text are not
2279 * affected.
2280 */
2281 void a_Dw_page_change_word_style (DwPage *page, gint from, gint to,
2282 DwStyle *style, gboolean include_first_space,
2283 gboolean include_last_space)
2284 {
2285 int i;
2286 DwStyle *old_style = NULL, *old_space_style;
2287 gint line, break_line = DW_INFINITY;
2288 DwWidget *widget = DW_WIDGET (page);
2289 gboolean diffs = FALSE, must_queue_resize = FALSE;
2290 DwPageWord *word;
2291 DwRequisition size;
2292
2293 if (from == to)
2294 return;
2295
2296 line = Dw_page_find_line_of_word (page, MAX (from - 1, 0));
2297
2298 /* We also have to include the space before the first word. */
2299 for (i = MAX (from - 1, 0); i < to; i++) {
2300 word = &page->words[i];
2301
2302 if (word->content.type == DW_CONTENT_TEXT) {
2303 if (i >= from) {
2304 old_style = word->style;
2305 word->style = style;
2306 a_Dw_style_ref (style);
2307
2308 if ((diffs = a_Dw_style_size_diffs (old_style, style))) {
2309 must_queue_resize = TRUE;
2310 break_line = MIN (break_line, line);
2311 Dw_page_calc_text_size (page, word->content.data.text,
2312 style, &size);
2313 word->size = size;
2314 }
2315 }
2316
2317
2318 if ((i >= from || include_first_space) &&
2319 (i < to -1 || include_last_space)) {
2320 old_space_style = word->space_style;
2321 word->space_style = style;
2322 a_Dw_style_ref (style);
2323
2324 /* In most cases, old_space_style will be old_style, so
2325 * that a_Dw_style_size_diffs does not have to be called.
2326 * Note: For the space before the first word, old_style is
2327 * NULL.
2328 */
2329 if (old_space_style == old_style ?
2330 diffs : a_Dw_style_size_diffs (old_space_style, style)) {
2331 must_queue_resize = TRUE;
2332 break_line = MIN (break_line, line);
2333 if (word->content.space)
2334 word->orig_space = style->font->space_width;
2335 }
2336
2337 a_Dw_style_unref (old_space_style);
2338 }
2339
2340 if (old_style)
2341 a_Dw_style_unref (old_style);
2342 }
2343
2344 /* Resizing will also imply redrawing, so if we resize anyway,
2345 * redrawing can be skipped. Otherwise, whole lines are redrawn,
2346 * i.e. when we have completed the whole task, or if this word
2347 * is the last one in the line.
2348 */
2349 if (!must_queue_resize &&
2350 (i == to - 1 || i == page->lines[line].last_word - 1)) {
2351 p_Dw_widget_queue_draw_area
2352 (widget, 0, Dw_page_line_total_y_offset_i (page, line),
2353 widget->allocation.width,
2354 page->lines[line].ascent + page->lines[line].descent);
2355
2356 if (i < to - 1)
2357 line = Dw_page_find_line_of_word (page, i + 1);
2358 }
2359 }
2360
2361 if (must_queue_resize)
2362 p_Dw_widget_queue_resize (widget, break_line, TRUE);
2363 }
2364
2365
2366 /*
2367 * Standard Dw function.
2368 */
2369 static DwIterator *Dw_page_iterator (DwWidget *widget,
2370 gint mask,
2371 gboolean at_end)
2372 {
2373 DwIteratorInt *it = g_new (DwIteratorInt, 1);
2374 it->it.widget = widget;
2375 it->it.mask = mask;
2376 it->it.next = Dw_page_iterator_next;
2377 it->it.prev = Dw_page_iterator_prev;
2378 it->it.clone = p_Dw_iterator_clone_std_int;
2379 it->it.compare = p_Dw_iterator_compare_std_int;
2380 it->it.free = p_Dw_iterator_free_std;
2381 it->it.highlight = Dw_page_iterator_highlight;
2382 it->it.get_allocation = Dw_page_iterator_get_allocation;
2383
2384 if (at_end) {
2385 it->it.content.type = DW_CONTENT_END;
2386 it->pos = DW_PAGE(widget)->num_words;
2387 } else {
2388 it->it.content.type = DW_CONTENT_START;
2389 it->pos = -1;
2390 }
2391
2392 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_page_iterator -> %p, at %s\n",
2393 it, at_end ? "end" : "start");
2394
2395 return (DwIterator*)it;
2396 }
2397
2398 /*
2399 * Standard Dw function.
2400 */
2401 static gboolean Dw_page_iterator_next (DwIterator *it)
2402 {
2403 DwPage *page = DW_PAGE (it->widget);
2404 DwIteratorInt *ii = (DwIteratorInt*)it;
2405
2406 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_page_iterator_next (%p): %d of %d\n",
2407 it, ii->pos, page->num_words);
2408
2409 if (it->content.type == DW_CONTENT_END)
2410 return FALSE;
2411
2412 do {
2413 ii->pos++;
2414 if (ii->pos >= page->num_words) {
2415 it->content.type = DW_CONTENT_END;
2416 return FALSE;
2417 }
2418 } while ((page->words[ii->pos].content.type & it->mask) == 0);
2419
2420 it->content = page->words[ii->pos].content;
2421 return TRUE;
2422 }
2423
2424 /*
2425 * Standard Dw function.
2426 */
2427 static gboolean Dw_page_iterator_prev (DwIterator *it)
2428 {
2429 DwPage *page = DW_PAGE (it->widget);
2430 DwIteratorInt *ii = (DwIteratorInt*)it;
2431
2432 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_page_iterator_prev (%p): %d of %d\n",
2433 it, ii->pos, page->num_words);
2434
2435 if (it->content.type == DW_CONTENT_START)
2436 return FALSE;
2437
2438 do {
2439 ii->pos--;
2440 if (ii->pos < 0) {
2441 it->content.type = DW_CONTENT_START;
2442 return FALSE;
2443 }
2444 } while ((page->words[ii->pos].content.type & it->mask) == 0);
2445
2446 it->content = page->words[ii->pos].content;
2447 return TRUE;
2448 }
2449
2450 /*
2451 * Standard Dw function.
2452 */
2453 void Dw_page_iterator_highlight (DwIterator *it,
2454 gint start,
2455 gint end,
2456 DwHighlightLayer layer)
2457 {
2458 DwIteratorInt *ii = (DwIteratorInt*)it;
2459 DwPage *page = DW_PAGE(it->widget);
2460 gint line;
2461
2462 page->words[ii->pos].hl_start[layer] = start;
2463 page->words[ii->pos].hl_end[layer] = end;
2464
2465 line = Dw_page_find_line_of_word (page, ii->pos);
2466 p_Dw_widget_queue_draw_area (it->widget, 0,
2467 Dw_page_line_total_y_offset_i (page, line),
2468 it->widget->allocation.width,
2469 page->lines[line].ascent
2470 + page->lines[line].descent);
2471 }
2472
2473 /*
2474 * Standard Dw function.
2475 */
2476 static void Dw_page_iterator_get_allocation (DwIterator *it,
2477 gint start,
2478 gint end,
2479 DwAllocation *allocation)
2480 {
2481 DwIteratorInt *ii = (DwIteratorInt*)it;
2482 DwPage *page = DW_PAGE(it->widget);
2483 DwPageWord *word;
2484 DwPageLine *line;
2485 gint wi, l;
2486 gint32 diff;
2487
2488 line = &page->lines[Dw_page_find_line_of_word (page, ii->pos)];
2489 word = &page->words[ii->pos];
2490
2491 /* the difference at the start */
2492 if (start == 0)
2493 diff = 0;
2494 else {
2495 if (word->content.type == DW_CONTENT_TEXT)
2496 diff = gdk_text_width (word->style->font->font,
2497 word->content.data.text, start);
2498 else
2499 diff = word->size.width;
2500 }
2501
2502 allocation->x =
2503 Dw_page_line_total_x_offset (page, line) + it->widget->allocation.x
2504 + diff;
2505 wi = line->first_word;
2506 while (wi < ii->pos) {
2507 word = &page->words[wi];
2508 allocation->x += word->size.width + word->eff_space;
2509 wi++;
2510 }
2511
2512 allocation->y =
2513 Dw_page_line_total_y_offset (page, line) + it->widget->allocation.y;
2514
2515 word = &page->words[wi];
2516 if (word->content.type == DW_CONTENT_TEXT) {
2517 l = strlen (word->content.data.text);
2518 if (end == l)
2519 allocation->width = word->size.width;
2520 else if (end >= l)
2521 allocation->width = word->size.width + word->eff_space;
2522 else
2523 allocation->width = gdk_text_width (word->style->font->font,
2524 word->content.data.text, end);
2525 } else {
2526 if (end == 1)
2527 allocation->width = word->size.width;
2528 else if (end >= 1)
2529 allocation->width = word->size.width + word->eff_space;
2530 else
2531 allocation->width = 0;
2532 }
2533
2534 allocation->width -= diff;
2535
2536 allocation->ascent = page->words[wi].size.ascent;
2537 allocation->descent = page->words[wi].size.descent;
2538
2539 DBG_MSGF (page, "scrolling", 0,
2540 "Dw_page_iterator_get_allocation: (%d, %d), (%d x %d x %d)",
2541 allocation->x, allocation->y,
2542 allocation->width, allocation->ascent, allocation->descent);
2543 }
0 /* This module contains the dw_page widget, which is the "back end" to
1 Web text widgets including html. */
2
3 #ifndef __DW_PAGE_H__
4 #define __DW_PAGE_H__
5
6 #include <gdk/gdk.h>
7 #include "dw_container.h"
8 #include "url.h"
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif /* __cplusplus */
13
14 #define DW_TYPE_PAGE (a_Dw_page_get_type ())
15 #define DW_PAGE(obj) GTK_CHECK_CAST ((obj), DW_TYPE_PAGE, DwPage)
16 #define DW_PAGE_CLASS(klass) GTK_CHECK_CLASS_CAST ((klass), DW_TYPE_PAGE, \
17 DwPageClass)
18 #define DW_IS_PAGE(obj) GTK_CHECK_TYPE ((obj), DW_TYPE_PAGE)
19 #define DW_IS_PAGE_CLASS(klass) GTK_CHECK_CLASS_TYPE ((klass), DW_TYPE_PAGE)
20
21 typedef struct _DwPage DwPage;
22 typedef struct _DwPageClass DwPageClass;
23
24 /* Internal data structures (maybe shouldn't be in public .h file? */
25 typedef struct _DwPageLine DwPageLine;
26 typedef struct _DwPageWord DwPageWord;
27
28 struct _DwPageLine
29 {
30 gint first_word; /* first-word's position in DwPageWord [0 based] */
31 gint last_word; /* last-word's position in DwPageWord [1 based] */
32
33 /* "top" is always relative to the top of the first line, i.e.
34 * page->lines[0].top is always 0. */
35 gint32 top, ascent, descent, break_space, left_offset;
36
37 /* This is similar to descent, but includes the bottom margins of the
38 * widgets within this line. */
39 gint32 margin_descent;
40
41 /* The following members contain accumulated values, from the top
42 * down to the line before. */
43 gint32 max_line_width; /* maximum of all line widths */
44 gint32 max_word_min; /* maximum of all word minima */
45 gint32 max_par_max; /* maximum of all paragraph maxima */
46 gint32 par_min; /* the minimal total width down from the last
47 paragraph start, to the *beginning* of the
48 line */
49 gint32 par_max; /* the maximal total width down from the last
50 paragraph start, to the *beginning* of the
51 line */
52 };
53
54 struct _DwPageWord {
55 /* todo: perhaps add a x_left? */
56 DwRequisition size;
57 /* Space after the word, only if it's not a break: */
58 gint32 orig_space; /* from font, set by a_Dw_page_add_space */
59 gint32 eff_space; /* effective space, set by Dw_page_word_wrap,
60 used for drawing etc. */
61 gint hl_start[DW_HIGHLIGHT_NUM_LAYERS], hl_end[DW_HIGHLIGHT_NUM_LAYERS];
62
63 DwContent content;
64
65 DwStyle *style;
66 DwStyle *space_style; /* initially the same as of the word, later
67 set by a_Dw_page_add_space */
68 };
69
70
71 struct _DwPage
72 {
73 DwContainer container;
74
75 /* These fields provide some ad-hoc-functionality, used by sub-classes. */
76 gboolean list_item; /* If TRUE, the first word of the page is treated
77 specially (search in source). */
78 gint32 inner_padding; /* This is an additional padding on the left side
79 (used by DwListItem). */
80 gint32 line1_offset; /* This is an additional offset of the first line.
81 May be negative (shift to left) or positive
82 (shift to right). */
83 gint32 line1_offset_eff; /* The "effective" value of line1_offset, may
84 differ from line1_offset when
85 ignore_line1_offset_sometimes is set to TRUE */
86
87 /* The following is really hackish: It is used for DwTableCell (see
88 * comment in dw_table_cell.c), to avoid too wide table columns. If
89 * set to TRUE, it has following effects:
90 *
91 * (i) line1_offset is ignored in calculating the minimal width
92 * (which is used by DwTable!), and
93 * (ii) line1_offset is ignored (line1_offset_eff is set to 0),
94 * when line1_offset plus the width of the first word is
95 * greater than the the available witdh.
96 *
97 * todo: Eliminate all these ad-hoc features by a new, simpler and
98 * more elegant design. ;-)
99 */
100 gboolean ignore_line1_offset_sometimes;
101
102 gboolean must_queue_resize;
103
104 /* These values are set by set_... */
105 gint32 avail_width, avail_ascent, avail_descent;
106
107 gint32 last_line_width;
108 gint32 last_line_par_min;
109 gint32 last_line_par_max;
110 gint wrap_ref; /* [0 based] */
111
112 DwPageLine *lines;
113 gint num_lines;
114 gint num_lines_max; /* number allocated */
115
116 DwPageWord *words;
117 gint num_words;
118 gint num_words_max; /* number allocated */
119
120 /* The word index of the link under a button press, and the char
121 * position */
122 gint link_pressed_index;
123 gint link_pressed_char_pos;
124
125 gint hover_link; /* The link under the button. */
126 DwTooltip *hover_tooltip; /* The tooltip under the button. No ref hold. */
127 };
128
129
130 struct _DwPageClass
131 {
132 DwContainerClass parent_class;
133
134 gboolean (*link_entered) (DwPage *page,
135 gint link, gint x, gint y);
136 gboolean (*link_pressed) (DwPage *page,
137 gint link, gint x, gint y,
138 GdkEventButton *event);
139 gboolean (*link_released) (DwPage *page,
140 gint link, gint x, gint y,
141 GdkEventButton *event);
142 gboolean (*link_clicked) (DwPage *page,
143 gint link, gint x, gint y,
144 GdkEventButton *event);
145
146 void (*word_wrap) (DwPage *page,
147 gint word_ind);
148 };
149
150
151 DwWidget* a_Dw_page_new (void);
152 GtkType a_Dw_page_get_type (void);
153
154 void a_Dw_page_flush (DwPage *page);
155 void a_Dw_page_set_voffset (DwPage *page, gint32 voffset);
156 void a_Dw_page_add_text (DwPage *page, char *text, DwStyle *style);
157 void a_Dw_page_add_widget (DwPage *page,
158 DwWidget *widget,
159 DwStyle *style);
160 gboolean a_Dw_page_add_anchor (DwPage *page, const gchar *name, DwStyle *style);
161 void a_Dw_page_add_space(DwPage *page, DwStyle *style);
162 void a_Dw_page_add_parbreak (DwPage *page, gint space, DwStyle *style);
163 void a_Dw_page_add_linebreak (DwPage *page, DwStyle *style);
164 void a_Dw_page_hand_over_break (DwPage *page, DwStyle *style);
165 void a_Dw_page_change_link_color (DwPage *page, gint link, gint32 new_color);
166 void a_Dw_page_change_word_style (DwPage *page, gint from, gint to,
167 DwStyle *style, gboolean include_first_space,
168 gboolean include_last_space);
169
170
171 #ifdef __cplusplus
172 }
173 #endif /* __cplusplus */
174
175
176 #endif /* __DW_PAGE_H__ */
0 /*
1 * File: dw_style.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "dw_style.h"
12 #include "dw_widget.h"
13
14 #include <gdk/gdk.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 /*#define DEBUG_LEVEL 1*/
20 #include "debug.h"
21
22 #define EQUIV(a, b) (((a) && (b)) || (!(a) && !(b)))
23
24 #define Dw_style_font_ref(font) ((font)->ref_count++)
25 #define Dw_style_font_unref(font) if (--((font)->ref_count) == 0) \
26 Dw_style_font_remove (font)
27
28 #define Dw_style_color_ref(color) ((color)->ref_count++)
29 #define Dw_style_color_unref(color) if (--((color)->ref_count) == 0) \
30 Dw_style_color_remove (color)
31
32 #define Dw_style_shaded_color_ref(color) ((color)->ref_count++)
33 #define Dw_style_shaded_color_unref(color) if (--((color)->ref_count) == 0) \
34 Dw_style_shaded_color_remove \
35 (color) \
36
37 static GHashTable *fonts_table;
38 static GHashTable *colors_table;
39 static GHashTable *shaded_colors_table;
40
41 static gint styles_num = 0;
42
43 /* Used by a_Dw_style_numtostr(). */
44 static const char
45 *roman_I0[] ={"","I","II","III","IV","V","VI","VII","VIII","IX"},
46 *roman_I1[] ={"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"},
47 *roman_I2[] ={"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"},
48 *roman_I3[] ={"","M","MM","MMM","MMMM"};
49
50
51 static gint Dw_style_font_equal (gconstpointer v1, gconstpointer v2);
52 static guint Dw_style_font_hash (gconstpointer key);
53 static void Dw_style_font_remove (DwStyleFont *font);
54
55 static void Dw_style_color_remove (DwStyleColor *color);
56 static void Dw_style_shaded_color_remove (DwStyleShadedColor *color);
57
58
59 /* ----------------------------------------------------------------------
60 *
61 * Initialization / Cleaning up
62 *
63 * ----------------------------------------------------------------------
64 */
65
66 /*
67 * Initialize the DwStyle submodule.
68 */
69 void a_Dw_style_init (void)
70 {
71 fonts_table = g_hash_table_new (Dw_style_font_hash, Dw_style_font_equal);
72 colors_table = g_hash_table_new (g_direct_hash, g_direct_equal);
73 shaded_colors_table = g_hash_table_new (g_direct_hash, g_direct_equal);
74 }
75
76
77 /*
78 * Called by a_Dw_style_freeall.
79 */
80 static void Dw_style_count_fonts (gpointer key,
81 gpointer value,
82 gpointer user_data)
83 {
84 DwStyleFont *font = (DwStyleFont*) key;
85 gint *count = (int*)user_data;
86
87 count[0]++;
88 count[1] += font->ref_count;
89 }
90
91 /*
92 * Called by a_Dw_style_freeall.
93 */
94 static void Dw_style_count_colors (gpointer key,
95 gpointer value,
96 gpointer user_data)
97 {
98 DwStyleColor *color = (DwStyleColor*) value;
99 gint *count = (int*)user_data;
100
101 count[0]++;
102 count[1] += color->ref_count;
103 }
104
105 /*
106 * Called by a_Dw_style_freeall.
107 */
108 static void Dw_style_count_shaded_colors (gpointer key,
109 gpointer value,
110 gpointer user_data)
111 {
112 DwStyleShadedColor *color = (DwStyleShadedColor*) value;
113 gint *count = (int*)user_data;
114
115 count[0]++;
116 count[1] += color->ref_count;
117 }
118
119
120 /*
121 * Free variables used by DwStyle, and do a check whether memory
122 * management works properly.
123 */
124 void a_Dw_style_freeall (void)
125 {
126 gint count[2];
127
128 if (styles_num)
129 g_warning ("%d styles left", styles_num);
130
131 count[0] = count[1] = 0;
132 g_hash_table_foreach (fonts_table, Dw_style_count_fonts, count);
133 if (count[0] || count[1])
134 g_warning ("%d fonts (%d references) left", count[0], count[1]);
135
136 count[0] = count[1] = 0;
137 g_hash_table_foreach (colors_table, Dw_style_count_colors, count);
138 if (count[0] || count[1])
139 g_warning ("%d colors (%d references) left", count[0], count[1]);
140
141 count[0] = count[1] = 0;
142 g_hash_table_foreach (shaded_colors_table, Dw_style_count_shaded_colors,
143 count);
144 if (count[0] || count[1])
145 g_warning ("%d shaded colors (%d references) left", count[0], count[1]);
146
147 g_hash_table_destroy (fonts_table);
148 g_hash_table_destroy (colors_table);
149 }
150
151
152 /* ----------------------------------------------------------------------
153 *
154 * Styles
155 *
156 * ----------------------------------------------------------------------
157 */
158
159
160 /*
161 * Set all style fields except font and color to reasonable defaults.
162 */
163 void a_Dw_style_init_values (DwStyle *style_attrs,
164 GdkWindow *window)
165 {
166 style_attrs->x_link = -1;
167 style_attrs->x_tooltip = NULL;
168 style_attrs->text_decoration = 0;
169 style_attrs->text_align = DW_STYLE_TEXT_ALIGN_LEFT;
170 style_attrs->list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
171 style_attrs->valign = DW_STYLE_VALIGN_MIDDLE;
172 style_attrs->background_color = NULL;
173 style_attrs->width = DW_STYLE_LENGTH_AUTO;
174 style_attrs->height = DW_STYLE_LENGTH_AUTO;
175
176 a_Dw_style_box_set_val (&style_attrs->margin, 0);
177 a_Dw_style_box_set_val (&style_attrs->border_width, 0);
178 a_Dw_style_box_set_val (&style_attrs->padding, 0);
179 a_Dw_style_box_set_border_color (style_attrs, NULL);
180 a_Dw_style_box_set_border_style (style_attrs, DW_STYLE_BORDER_NONE);
181 style_attrs->border_spacing = 0;
182
183 style_attrs->display = DW_STYLE_DISPLAY_INLINE;
184 style_attrs->white_space = DW_STYLE_WHITE_SPACE_NORMAL;
185 }
186
187
188 /*
189 * Reset those style attributes to their standard values, which are
190 * not inherited, according to CSS.
191 */
192 void a_Dw_style_reset_values (DwStyle *style_attrs,
193 GdkWindow *window)
194 {
195 style_attrs->x_link = -1;
196 style_attrs->background_color = NULL;
197 style_attrs->x_tooltip = NULL;
198
199 style_attrs->text_align = DW_STYLE_TEXT_ALIGN_LEFT; /* ??? */
200 style_attrs->valign = DW_STYLE_VALIGN_MIDDLE;
201 style_attrs->text_align_char = '.';
202 style_attrs->background_color = NULL;
203 style_attrs->width = DW_STYLE_LENGTH_AUTO;
204 style_attrs->height = DW_STYLE_LENGTH_AUTO;
205
206 a_Dw_style_box_set_val (&style_attrs->margin, 0);
207 a_Dw_style_box_set_val (&style_attrs->border_width, 0);
208 a_Dw_style_box_set_val (&style_attrs->padding, 0);
209 a_Dw_style_box_set_border_color (style_attrs, NULL);
210 a_Dw_style_box_set_border_style (style_attrs, DW_STYLE_BORDER_NONE);
211 style_attrs->border_spacing = 0;
212
213 style_attrs->display = DW_STYLE_DISPLAY_INLINE;
214 style_attrs->white_space = DW_STYLE_WHITE_SPACE_NORMAL;
215 }
216
217 /*
218 * Return a new DwStyle, with increased reference pointer.
219 */
220 DwStyle* a_Dw_style_new (DwStyle *style_attrs,
221 GdkWindow *window)
222 {
223 DwStyle *style;
224
225 style = g_new (DwStyle, 1);
226 *style = *style_attrs;
227 style->ref_count = 1;
228 Dw_style_font_ref (style->font);
229 Dw_style_color_ref (style->color);
230 if (style->background_color)
231 Dw_style_color_ref (style->background_color);
232 if (style->border_color.top)
233 Dw_style_shaded_color_ref (style->border_color.top);
234 if (style->border_color.right)
235 Dw_style_shaded_color_ref (style->border_color.right);
236 if (style->border_color.bottom)
237 Dw_style_shaded_color_ref (style->border_color.bottom);
238 if (style->border_color.left)
239 Dw_style_shaded_color_ref (style->border_color.left);
240 if (style->x_tooltip)
241 a_Dw_tooltip_ref (style->x_tooltip);
242
243 styles_num++;
244 return style;
245 }
246
247 /*
248 * Remove a style (called when ref_count == 0).
249 */
250 void Dw_style_remove (DwStyle *style)
251 {
252 Dw_style_font_unref (style->font);
253 Dw_style_color_unref (style->color);
254 if (style->background_color)
255 Dw_style_color_unref (style->background_color);
256 if (style->border_color.top)
257 Dw_style_shaded_color_unref (style->border_color.top);
258 if (style->border_color.right)
259 Dw_style_shaded_color_unref (style->border_color.right);
260 if (style->border_color.bottom)
261 Dw_style_shaded_color_unref (style->border_color.bottom);
262 if (style->border_color.left)
263 Dw_style_shaded_color_unref (style->border_color.left);
264 if (style->x_tooltip)
265 a_Dw_tooltip_unref (style->x_tooltip);
266
267 g_free (style);
268 styles_num--;
269 }
270
271
272
273 /* ----------------------------------------------------------------------
274 *
275 * Fonts
276 *
277 * ----------------------------------------------------------------------
278 */
279
280 /*
281 * Create the GdkFont. font->name contains one name. If try_all is
282 * TRUE, try also standard fonts, if anything else fails.
283 */
284 static void Dw_style_font_realize (DwStyleFont *font, gboolean try_all)
285 {
286 char fontname[256], *style_char_1 = NULL, *style_char_2 = NULL;
287
288 switch (font->style) {
289 case DW_STYLE_FONT_STYLE_NORMAL:
290 style_char_1 = style_char_2 = "r";
291 break;
292 case DW_STYLE_FONT_STYLE_ITALIC:
293 style_char_1 = "i";
294 style_char_2 = "o";
295 break;
296 case DW_STYLE_FONT_STYLE_OBLIQUE:
297 style_char_1 = "o";
298 style_char_2 = "i";
299 break;
300 }
301
302 sprintf (fontname, "-*-%s-%s-%s-*-*-%d-*-75-75-*-*-iso8859-1",
303 font->name,
304 (font->weight >= 500) ? "bold" : "medium",
305 style_char_1, font->size);
306 font->font = gdk_font_load (fontname);
307
308 if (font->font == NULL && font->style != DW_STYLE_FONT_STYLE_NORMAL) {
309 sprintf (fontname, "-*-%s-%s-%s-*-*-%d-*-75-75-*-*-iso8859-1",
310 font->name,
311 (font->weight >= 500) ? "bold" : "medium",
312 style_char_2, font->size);
313 font->font = gdk_font_load (fontname);
314 }
315
316 if (try_all) {
317 if (font->font == NULL) {
318 /* Can't load the font - substitute the default instead. */
319 font->font =
320 gdk_font_load
321 ("-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-iso8859-1");
322 }
323
324 if (font->font == NULL) {
325 /* Try another platform-font that should be available. (iPaq) */
326 font->font =
327 gdk_font_load
328 ("-misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1");
329 }
330
331 if (font->font == NULL) {
332 /* Can't load any suitable font! */
333 g_warning ("Can't load any ISO8859-1 font!?! :(");
334 font->font =
335 gdk_font_load ("-adobe-helvetica-*-*-*--*-*-*-*-*-*-*-*");
336 }
337 }
338
339 if (font->font) {
340 font->space_width = gdk_char_width (font->font, ' ');
341 font->x_height = gdk_char_height (font->font, 'x');
342 }
343 }
344
345
346 /*
347 * Used for the font_table hash table.
348 */
349 static gint Dw_style_font_equal (gconstpointer v1, gconstpointer v2)
350 {
351 const DwStyleFont *font1 = (DwStyleFont*) v1, *font2 = (DwStyleFont*) v2;
352
353 return (font1->size == font2->size &&
354 font1->weight == font2->weight &&
355 font1->style == font2->style &&
356 strcmp (font1->name, font2->name) == 0);
357 }
358
359
360 /*
361 * Used for the font_table hash table.
362 */
363 static guint Dw_style_font_hash (gconstpointer key)
364 {
365 const DwStyleFont *font = (DwStyleFont*) key;
366 guint h;
367
368 h = g_str_hash (font->name);
369 h = (h << 5) - h + font->size;
370 h = (h << 5) - h + font->weight;
371 h = (h << 5) - h + font->style;
372 return h;
373 }
374
375
376 /*
377 * Returns a new or already existing font. This function is only used
378 * internally, and called by a_Dw_style_font_new and
379 * a_Dw_style_font_new_from_list.
380 *
381 * Note that the reference counter is not increased/set to zero, it
382 * will be increased by a_Dw_style_new. If 'try_all' is TRUE, try also
383 * standard fonts, if anything else fails.
384 */
385 static DwStyleFont* Dw_style_font_new_internal (DwStyleFont *font_attrs,
386 gboolean try_all)
387 {
388 DwStyleFont *font;
389
390 g_return_val_if_fail (font_attrs->name != NULL, NULL);
391
392 if ((font = g_hash_table_lookup (fonts_table, font_attrs))) {
393 return font;
394 } else {
395 font = g_new (DwStyleFont, 1);
396 font->size = font_attrs->size;
397 font->weight = font_attrs->weight;
398 font->style = font_attrs->style;
399 font->name = g_strdup (font_attrs->name);
400 font->ref_count = 0;
401
402 Dw_style_font_realize (font, try_all);
403 if (font->font) {
404 g_hash_table_insert (fonts_table, font, font);
405 return font;
406 } else {
407 g_free (font->name);
408 g_free (font);
409 return NULL;
410 }
411 }
412 }
413
414
415 /*
416 * Return a new or already existing font, with one name in
417 * font_attrs->name. See also Dw_style_font_new_internal.
418 */
419 DwStyleFont* a_Dw_style_font_new (DwStyleFont *font_attrs)
420 {
421 DwStyleFont *font;
422
423 font = Dw_style_font_new_internal (font_attrs, TRUE);
424 if (font == NULL)
425 g_error ("Could not find any font.");
426
427 return font;
428 }
429
430
431 /*
432 * Return a new or already existing font, with font_attrs->name
433 * containing a comma separated list of names. See also
434 * Dw_style_font_new_internal.
435 */
436 DwStyleFont* a_Dw_style_font_new_from_list (DwStyleFont *font_attrs,
437 gchar *default_family)
438 {
439 DwStyleFont *font = NULL, font_attrs2;
440 char *comma, *list, *current;
441
442 font_attrs2 = *font_attrs;
443 current = list = g_strdup (font_attrs->name);
444
445 while (current && (font == NULL)) {
446 comma = strchr (current, ',');
447 if (comma) *comma = 0;
448
449 font_attrs2.name = current;
450 font = Dw_style_font_new_internal (&font_attrs2, FALSE);
451 if (font)
452 break;
453
454 if (comma) {
455 current = comma + 1;
456 while (isspace (*current)) current++;
457 } else
458 current = NULL;
459 }
460
461 g_free (list);
462
463 if (font == NULL) {
464 font_attrs2.name = default_family;
465 font = Dw_style_font_new_internal (&font_attrs2, TRUE);
466 }
467
468 if (font == NULL)
469 g_error ("Could not find any font.");
470
471 return font;
472 }
473
474
475 /*
476 * Remove a font (called when ref_count == 0).
477 */
478 static void Dw_style_font_remove (DwStyleFont *font)
479 {
480 g_hash_table_remove (fonts_table, font);
481 g_free (font->name);
482 gdk_font_unref (font->font);
483 g_free (font);
484 }
485
486
487 /* ----------------------------------------------------------------------
488 *
489 * Colors
490 *
491 * ----------------------------------------------------------------------
492 */
493
494 /*
495 * Copied from gtkstyle.c.
496 * Convert RGB into HLS.
497 */
498 static void Dw_style_rgb_to_hls (gdouble *r,
499 gdouble *g,
500 gdouble *b)
501 {
502 gdouble min;
503 gdouble max;
504 gdouble red;
505 gdouble green;
506 gdouble blue;
507 gdouble h, l, s;
508 gdouble delta;
509
510 red = *r;
511 green = *g;
512 blue = *b;
513
514 if (red > green) {
515 if (red > blue)
516 max = red;
517 else
518 max = blue;
519
520 if (green < blue)
521 min = green;
522 else
523 min = blue;
524 } else {
525 if (green > blue)
526 max = green;
527 else
528 max = blue;
529
530 if (red < blue)
531 min = red;
532 else
533 min = blue;
534 }
535
536 l = (max + min) / 2;
537 s = 0;
538 h = 0;
539
540 if (max != min) {
541 if (l <= 0.5)
542 s = (max - min) / (max + min);
543 else
544 s = (max - min) / (2 - max - min);
545
546 delta = max -min;
547 if (red == max)
548 h = (green - blue) / delta;
549 else if (green == max)
550 h = 2 + (blue - red) / delta;
551 else if (blue == max)
552 h = 4 + (red - green) / delta;
553
554 h *= 60;
555 if (h < 0.0)
556 h += 360;
557 }
558
559 *r = h;
560 *g = l;
561 *b = s;
562 }
563
564 /*
565 * Copied from gtkstyle.c.
566 * Convert HLS into RGB.
567 */
568 static void Dw_style_hls_to_rgb (gdouble *h,
569 gdouble *l,
570 gdouble *s)
571 {
572 gdouble hue;
573 gdouble lightness;
574 gdouble saturation;
575 gdouble m1, m2;
576 gdouble r, g, b;
577
578 lightness = *l;
579 saturation = *s;
580
581 if (lightness <= 0.5)
582 m2 = lightness * (1 + saturation);
583 else
584 m2 = lightness + saturation - lightness * saturation;
585 m1 = 2 * lightness - m2;
586
587 if (saturation == 0) {
588 *h = lightness;
589 *l = lightness;
590 *s = lightness;
591 } else {
592 hue = *h + 120;
593 while (hue > 360)
594 hue -= 360;
595 while (hue < 0)
596 hue += 360;
597
598 if (hue < 60)
599 r = m1 + (m2 - m1) * hue / 60;
600 else if (hue < 180)
601 r = m2;
602 else if (hue < 240)
603 r = m1 + (m2 - m1) * (240 - hue) / 60;
604 else
605 r = m1;
606
607 hue = *h;
608 while (hue > 360)
609 hue -= 360;
610 while (hue < 0)
611 hue += 360;
612
613 if (hue < 60)
614 g = m1 + (m2 - m1) * hue / 60;
615 else if (hue < 180)
616 g = m2;
617 else if (hue < 240)
618 g = m1 + (m2 - m1) * (240 - hue) / 60;
619 else
620 g = m1;
621
622 hue = *h - 120;
623 while (hue > 360)
624 hue -= 360;
625 while (hue < 0)
626 hue += 360;
627
628 if (hue < 60)
629 b = m1 + (m2 - m1) * hue / 60;
630 else if (hue < 180)
631 b = m2;
632 else if (hue < 240)
633 b = m1 + (m2 - m1) * (240 - hue) / 60;
634 else
635 b = m1;
636
637 *h = r;
638 *l = g;
639 *s = b;
640 }
641 }
642
643
644 /*
645 * Create the necessary recources (GdkColor, GdkGC).
646 * k is a factor the color is multiplied with before, this is needed
647 * for shaded colors.
648 */
649 static void Dw_style_color_create (gint color_val,
650 GdkWindow *window,
651 GdkColor *color,
652 GdkGC **gc,
653 gint d)
654 {
655 gint red, green, blue;
656 gdouble hue, lightness, saturation;
657
658 red = (color_val >> 16) & 255;
659 green = (color_val >> 8) & 255;
660 blue = color_val & 255;
661
662 if (d) {
663 hue = (gdouble)red / 255;
664 lightness = (gdouble)green / 255;
665 saturation = (gdouble)blue / 255;
666
667 DEBUG_MSG (1, "Shaded by %1.3g: (%1.3g, %1.3g, %1.3g) -> ",
668 k, hue, lightness, saturation);
669 Dw_style_rgb_to_hls (&hue, &lightness, &saturation);
670
671 if (lightness > 0.8) {
672 if (d > 0)
673 lightness -= 0.2;
674 else
675 lightness -= 0.4;
676 } else if (lightness < 0.2) {
677 if (d > 0)
678 lightness += 0.4;
679 else
680 lightness += 0.2;
681 } else
682 lightness += d * 0.2;
683
684 Dw_style_hls_to_rgb (&hue, &lightness, &saturation);
685 DEBUG_MSG (1, "(%1.3g, %1.3g, %1.3g)\n", hue, lightness, saturation);
686
687 red = hue * 255;
688 green = lightness * 255;
689 blue = saturation * 255;
690 }
691
692 blue |= blue << 8;
693 red |= red << 8;
694 green |= green << 8;
695
696 color->red = red;
697 color->green = green;
698 color->blue = blue;
699 gdk_color_alloc (gdk_window_get_colormap (window), color);
700
701 *gc = gdk_gc_new (window);
702 gdk_gc_set_foreground (*gc, color);
703 }
704
705 /*
706 Return a new or already existing color. color_val has the form
707 0xrrggbb.
708 */
709 DwStyleColor* a_Dw_style_color_new (gint color_val,
710 GdkWindow *window)
711 {
712 DwStyleColor *color;
713
714 color = g_hash_table_lookup (colors_table, GINT_TO_POINTER (color_val));
715 if (color == NULL) {
716 color = g_new (DwStyleColor, 1);
717 color->ref_count = 0;
718 color->color_val = color_val;
719
720 Dw_style_color_create (color_val, window,
721 &color->color, &color->gc, 0);
722 Dw_style_color_create (color_val ^ 0xffffff, window,
723 &color->inverse_color, &color->inverse_gc, 0);
724 g_hash_table_insert (colors_table, GINT_TO_POINTER (color_val), color);
725 g_hash_table_insert (colors_table, GINT_TO_POINTER (color_val), color);
726 }
727
728 return color;
729 }
730
731 /*
732 * Remove a color (called when ref_count == 0).
733 */
734 static void Dw_style_color_remove (DwStyleColor *color)
735 {
736 g_hash_table_remove (colors_table, GINT_TO_POINTER (color->color_val));
737 gdk_gc_destroy (color->gc);
738 gdk_gc_destroy (color->inverse_gc);
739 g_free (color);
740 }
741
742
743 /*
744 * Return a new or already existing shaded color. color_val has the
745 * form 0xrrggbb.
746 */
747 DwStyleShadedColor* a_Dw_style_shaded_color_new (gint color_val,
748 GdkWindow *window)
749 {
750 DwStyleShadedColor *color;
751
752 color =
753 g_hash_table_lookup (shaded_colors_table, GINT_TO_POINTER (color_val));
754 if (color == NULL) {
755 color = g_new (DwStyleShadedColor, 1);
756 color->ref_count = 0;
757 color->color_val = color_val;
758
759 Dw_style_color_create (color_val, window,
760 &color->color, &color->gc, 0);
761 Dw_style_color_create (color_val ^ 0xffffff, window,
762 &color->inverse_color, &color->inverse_gc, 0);
763 Dw_style_color_create (color_val, window,
764 &color->color_dark, &color->gc_dark, -1);
765 Dw_style_color_create (color_val, window,
766 &color->color_light, &color->gc_light, +1);
767 g_hash_table_insert (shaded_colors_table,
768 GINT_TO_POINTER (color_val), color);
769 }
770
771 return color;
772 }
773
774 /*
775 * Remove a shaded color (called when ref_count == 0).
776 */
777 static void Dw_style_shaded_color_remove (DwStyleShadedColor *color)
778 {
779 g_hash_table_remove (shaded_colors_table,
780 GINT_TO_POINTER (color->color_val));
781 gdk_gc_destroy (color->gc);
782 gdk_gc_destroy (color->gc_dark);
783 gdk_gc_destroy (color->gc_light);
784 g_free (color);
785 }
786
787 /*
788 * This function returns whether something may change its size, when
789 * its style changes from style1 to style2. It is mainly for
790 * optimizing style changes where only colors etc change (where FALSE
791 * would be returned), in some cases it may return TRUE, although a
792 * size change does not actually happen (e.g. when in a certain
793 * context a particular attribute is ignored).
794 */
795 gboolean a_Dw_style_size_diffs (DwStyle *style1,
796 DwStyle *style2)
797 {
798 /* todo: Should for CSS implemented properly. Currently, size
799 * changes are not needed, so always FALSE is returned. See also
800 * a_Dw_widget_set_style(). */
801 return FALSE;
802 }
803
804
805 /* ----------------------------------------------------------------------
806 *
807 * Drawing functions
808 *
809 * ----------------------------------------------------------------------
810 */
811
812 /*
813 * Draw a part of a border.
814 */
815 static void Dw_style_draw_polygon (GdkDrawable *drawable,
816 GdkGC *gc,
817 gint32 x1, gint32 y1, gint32 x2, gint32 y2,
818 gint32 width, gint32 w1, gint32 w2)
819 {
820 GdkPoint points[4];
821
822 if (width != 0) {
823 if (width == 1) {
824 if (x1 == x2)
825 gdk_draw_line (drawable, gc, x1, y1, x2, y2 - 1);
826 else
827 gdk_draw_line (drawable, gc, x1, y1, x2 - 1, y2);
828 } else if (width == -1) {
829 if (x1 == x2)
830 gdk_draw_line (drawable, gc, x1 - 1, y1, x2 - 1, y2 - 1);
831 else
832 gdk_draw_line (drawable, gc, x1, y1 - 1, x2 - 1, y2 - 1);
833 } else {
834 points[0].x = x1;
835 points[0].y = y1;
836 points[1].x = x2;
837 points[1].y = y2;
838
839 if (x1 == x2) {
840 points[2].x = x1 + width;
841 points[2].y = y2 + w2;
842 points[3].x = x1 + width;
843 points[3].y = y1 + w1;
844 } else {
845 points[2].x = x2 + w2;
846 points[2].y = y1 + width;
847 points[3].x = x1 + w1;
848 points[3].y = y1 + width;
849 }
850
851 gdk_draw_polygon (drawable, gc, TRUE, points, 4);
852 }
853 }
854 }
855
856
857 /*
858 * Draw the border of a region in window, according to style. Used by
859 * Dw_widget_draw_box and Dw_widget_draw_widget_box.
860 */
861
862 #define LIMIT(v) if ((v) < -30000) v = -30000; \
863 if ((v) > 30000) v = 30000
864
865 void p_Dw_style_draw_border (GdkWindow *window,
866 GdkRectangle *area,
867 gint32 vx,
868 gint32 vy,
869 gint32 x,
870 gint32 y,
871 gint32 width,
872 gint32 height,
873 DwStyle *style,
874 gboolean inverse)
875 {
876 /* todo: a lot! */
877 GdkGC *dark_gc, *light_gc, *normal_gc;
878 GdkGC *top_gc, *right_gc, *bottom_gc, *left_gc;
879 gint32 xb1, yb1, xb2, yb2, xp1, yp1, xp2, yp2;
880
881 if (style->border_style.top == DW_STYLE_BORDER_NONE)
882 return;
883
884 xb1 = x + style->margin.left - vx;
885 yb1 = y + style->margin.top - vy;
886 xb2 = xb1 + width - style->margin.left - style->margin.right;
887 yb2 = yb1 + height - style->margin.top - style->margin.bottom;
888
889 xp1 = xb1 + style->border_width.top;
890 yp1 = yb1 + style->border_width.left;
891 xp2 = xb2 + style->border_width.bottom;
892 yp2 = yb2 + style->border_width.right;
893
894 /* Make sure that we pass 16-bit values to Gdk functions. */
895 LIMIT (xb1);
896 LIMIT (xb2);
897 LIMIT (yb1);
898 LIMIT (yb2);
899
900 light_gc = inverse ? style->border_color.top->gc_dark :
901 style->border_color.top->gc_light;
902 dark_gc = inverse ? style->border_color.top->gc_light :
903 style->border_color.top->gc_dark;
904 normal_gc = inverse ? style->border_color.top->inverse_gc :
905 style->border_color.top->gc;
906
907 switch (style->border_style.top) {
908 case DW_STYLE_BORDER_INSET:
909 top_gc = left_gc = dark_gc;
910 right_gc = bottom_gc = light_gc;
911 break;
912
913 case DW_STYLE_BORDER_OUTSET:
914 top_gc = left_gc = light_gc;
915 right_gc = bottom_gc = dark_gc;
916 break;
917
918 default:
919 top_gc = right_gc = bottom_gc = left_gc = normal_gc;
920 break;
921 }
922
923 Dw_style_draw_polygon (window, top_gc, xb1, yb1, xb2, yb1,
924 style->border_width.top,
925 style->border_width.left,
926 - style->border_width.right);
927 Dw_style_draw_polygon (window, right_gc, xb2, yb1, xb2, yb2,
928 - style->border_width.right,
929 style->border_width.top,
930 - style->border_width.bottom);
931 Dw_style_draw_polygon (window, bottom_gc, xb1, yb2, xb2, yb2,
932 - style->border_width.bottom,
933 style->border_width.left,
934 - style->border_width.right);
935 Dw_style_draw_polygon (window, left_gc, xb1, yb1, xb1, yb2,
936 style->border_width.left,
937 style->border_width.top,
938 - style->border_width.bottom);
939 }
940
941
942 /*
943 * Draw the background (content plus padding) of a region in window,
944 * according to style. Used by Dw_widget_draw_box and
945 * Dw_widget_draw_widget_box.
946 */
947 void p_Dw_style_draw_background (GdkWindow *window,
948 GdkRectangle *area,
949 gint32 vx,
950 gint32 vy,
951 gint32 x,
952 gint32 y,
953 gint32 width,
954 gint32 height,
955 DwStyle *style,
956 gboolean inverse)
957 {
958 DwRectangle dw_area, bg_area, intersection;
959
960 if (style->background_color) {
961 dw_area.x = area->x + vx;
962 dw_area.y = area->y + vy;
963 dw_area.width = area->width;
964 dw_area.height = area->height;
965
966 bg_area.x = x + style->margin.left + style->border_width.left;
967 bg_area.y = y + style->margin.top + style->border_width.top;
968 bg_area.width =
969 width - style->margin.left - style->border_width.left -
970 style->margin.right - style->border_width.right;
971 bg_area.height =
972 height - style->margin.top - style->border_width.top -
973 style->margin.bottom - style->border_width.bottom;
974
975 if (p_Dw_rectangle_intersect (&dw_area, &bg_area, &intersection))
976 gdk_draw_rectangle (window,
977 inverse ? style->background_color->inverse_gc :
978 style->background_color->gc,
979 TRUE, intersection.x - vx, intersection.y - vy,
980 intersection.width, intersection.height);
981 }
982 }
983
984
985 /*
986 * Convert a number into a string, in a given style. Used for ordered lists.
987 */
988 void a_Dw_style_numtostr (gint num,
989 gchar *buf,
990 gint buflen,
991 DwStyleListStyleType list_style_tyle)
992 {
993 int i3, i2, i1, i0;
994 gboolean low = FALSE;
995 int start_ch = 'A';
996
997 switch(list_style_tyle){
998 case DW_STYLE_LIST_STYLE_TYPE_LOWER_ALPHA:
999 start_ch = 'a';
1000 case DW_STYLE_LIST_STYLE_TYPE_UPPER_ALPHA:
1001 i0 = num - 1;
1002 i1 = i0/26 - 1; i2 = i1/26 - 1;
1003 if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */
1004 sprintf(buf, "****.");
1005 else
1006 sprintf(buf, "%c%c%c.",
1007 i2<0 ? ' ' : start_ch + i2%26,
1008 i1<0 ? ' ' : start_ch + i1%26,
1009 i0<0 ? ' ' : start_ch + i0%26);
1010 break;
1011 case DW_STYLE_LIST_STYLE_TYPE_LOWER_ROMAN:
1012 low = TRUE;
1013 case DW_STYLE_LIST_STYLE_TYPE_UPPER_ROMAN:
1014 i0 = num;
1015 i1 = i0/10; i2 = i1/10; i3 = i2/10;
1016 i0 %= 10; i1 %= 10; i2 %= 10;
1017 if (num < 0 || i3 > 4) /* more than 4999 elements ? */
1018 sprintf(buf, "****.");
1019 else
1020 g_snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2],
1021 roman_I1[i1], roman_I0[i0]);
1022 if (low)
1023 g_strdown(buf);
1024 break;
1025 case DW_STYLE_LIST_STYLE_TYPE_DECIMAL:
1026 default:
1027 g_snprintf(buf, buflen, "%d.", num);
1028 break;
1029 }
1030 }
0 #ifndef __DW_STYLE_H__
1 #define __DW_STYLE_H__
2
3 #include <gdk/gdktypes.h>
4 #include "dw_tooltip.h"
5
6 #define DW_STYLE_ALIGN_LEFT 1
7 #define DW_STYLE_ALIGN_RIGHT 2
8
9 typedef enum {
10 DW_STYLE_BORDER_NONE,
11 DW_STYLE_BORDER_HIDDEN,
12 DW_STYLE_BORDER_DOTTED,
13 DW_STYLE_BORDER_DASHED,
14 DW_STYLE_BORDER_SOLID,
15 DW_STYLE_BORDER_DOUBLE,
16 DW_STYLE_BORDER_GROOVE,
17 DW_STYLE_BORDER_RIDGE,
18 DW_STYLE_BORDER_INSET,
19 DW_STYLE_BORDER_OUTSET
20 } DwStyleBorderStyle;
21
22 typedef enum {
23 DW_STYLE_TEXT_ALIGN_LEFT,
24 DW_STYLE_TEXT_ALIGN_RIGHT,
25 DW_STYLE_TEXT_ALIGN_CENTER,
26 DW_STYLE_TEXT_ALIGN_JUSTIFY,
27 DW_STYLE_TEXT_ALIGN_STRING
28 } DwStyleTextAlignType;
29
30 typedef enum {
31 DW_STYLE_VALIGN_TOP,
32 DW_STYLE_VALIGN_BOTTOM,
33 DW_STYLE_VALIGN_MIDDLE,
34 DW_STYLE_VALIGN_BASELINE,
35 DW_STYLE_VALIGN_SUB,
36 DW_STYLE_VALIGN_SUPER
37 } DwStyleVAlignType;
38
39 typedef enum {
40 /* todo: incomplete */
41 DW_STYLE_DISPLAY_BLOCK,
42 DW_STYLE_DISPLAY_INLINE,
43 DW_STYLE_DISPLAY_LIST_ITEM,
44 DW_STYLE_DISPLAY_TABLE,
45 DW_STYLE_DISPLAY_TABLE_ROW_GROUP,
46 DW_STYLE_DISPLAY_TABLE_HEADER_GROUP,
47 DW_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
48 DW_STYLE_DISPLAY_TABLE_ROW,
49 DW_STYLE_DISPLAY_TABLE_CELL,
50 DW_STYLE_DISPLAY_LAST
51 } DwStyleDisplayType;
52
53
54 typedef enum {
55 DW_STYLE_LIST_STYLE_TYPE_DISC,
56 DW_STYLE_LIST_STYLE_TYPE_CIRCLE,
57 DW_STYLE_LIST_STYLE_TYPE_SQUARE,
58 DW_STYLE_LIST_STYLE_TYPE_DECIMAL,
59 DW_STYLE_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO,
60 DW_STYLE_LIST_STYLE_TYPE_LOWER_ROMAN,
61 DW_STYLE_LIST_STYLE_TYPE_UPPER_ROMAN,
62 DW_STYLE_LIST_STYLE_TYPE_LOWER_GREEK,
63 DW_STYLE_LIST_STYLE_TYPE_LOWER_ALPHA,
64 DW_STYLE_LIST_STYLE_TYPE_LOWER_LATIN,
65 DW_STYLE_LIST_STYLE_TYPE_UPPER_ALPHA,
66 DW_STYLE_LIST_STYLE_TYPE_UPPER_LATIN,
67 DW_STYLE_LIST_STYLE_TYPE_HEBREW,
68 DW_STYLE_LIST_STYLE_TYPE_ARMENIAN,
69 DW_STYLE_LIST_STYLE_TYPE_GEORGIAN,
70 DW_STYLE_LIST_STYLE_TYPE_CJK_IDEOGRAPHIC,
71 DW_STYLE_LIST_STYLE_TYPE_HIRAGANA,
72 DW_STYLE_LIST_STYLE_TYPE_KATAKANA,
73 DW_STYLE_LIST_STYLE_TYPE_HIRAGANA_IROHA,
74 DW_STYLE_LIST_STYLE_TYPE_KATAKANA_IROHA,
75 DW_STYLE_LIST_STYLE_TYPE_NONE
76 } DwStyleListStyleType;
77
78 typedef enum {
79 DW_STYLE_FONT_STYLE_NORMAL,
80 DW_STYLE_FONT_STYLE_ITALIC,
81 DW_STYLE_FONT_STYLE_OBLIQUE
82 } DwStyleFontStyle;
83
84 typedef enum {
85 DW_STYLE_TEXT_DECORATION_UNDERLINE = 1 << 0,
86 DW_STYLE_TEXT_DECORATION_OVERLINE = 1 << 1,
87 DW_STYLE_TEXT_DECORATION_LINE_THROUGH = 1 << 2,
88 DW_STYLE_TEXT_DECORATION_BLINK = 1 << 3
89 } DwStyleTextDecoration;
90
91 typedef enum {
92 DW_STYLE_WHITE_SPACE_NORMAL,
93 DW_STYLE_WHITE_SPACE_PRE,
94 DW_STYLE_WHITE_SPACE_NOWRAP
95 } DwStyleWhiteSpace;
96
97 typedef struct _DwStyle DwStyle;
98 typedef struct _DwStyleFont DwStyleFont;
99 typedef struct _DwStyleColor DwStyleColor;
100 typedef struct _DwStyleShadedColor DwStyleShadedColor;
101 typedef struct _DwStyleBox DwStyleBox;
102
103
104 /*
105 * Lengths are gint's. Absolute lengths are represented in the following way:
106 *
107 * +---+ - - - - - - - - - -+---+---+
108 * | pixel value (integer) | 0 | 1 |
109 * +---+ - - - - - - - - - -+---+---+
110 *
111 * Percentages:
112 *
113 * +---+ - - - +---+---+- - - - - -+---+---+---+
114 * | integer part | decimal fraction | 1 | 0 |
115 * +---+ - - - +---+---+- - - - - -+---+---+---+
116 * n-1 18 17 2 1 0
117 *
118 * | <------ fixed point value ------> |
119 *
120 * Relative lengths (only used in HTML):
121 *
122 * +---+ - - - +---+---+- - - - - -+---+---+---+
123 * | integer part | decimal fraction | 1 | 1 |
124 * +---+ - - - +---+---+- - - - - -+---+---+---+
125 * n-1 18 17 2 1 0
126 *
127 * | <------ fixed point value ------> |
128 *
129 * "auto" lenghths are represented as DW_STYLE_LENGTH_AUTO.
130 */
131
132 typedef gint DwStyleLength;
133
134 #define DW_STYLE_CREATE_ABS_LENGTH(n) (((n) << 2) | 1)
135 #define DW_STYLE_CREATE_PER_LENGTH(v) ( ( (gint)((v) * (1 << 18)) & ~3 ) | 2 )
136 #define DW_STYLE_CREATE_REL_LENGTH(v) ( ( (gint)((v) * (1 << 18)) & ~3 ) | 3 )
137
138 #define DW_STYLE_IS_ABS_LENGTH(l) (((l) & 3) == 1)
139 #define DW_STYLE_IS_PER_LENGTH(l) (((l) & 3) == 2)
140 #define DW_STYLE_IS_REL_LENGTH(l) (((l) & 3) == 3)
141
142 #define DW_STYLE_ABS_LENGTH_VAL(l) ((l) >> 2)
143 #define DW_STYLE_PER_LENGTH_VAL(l) ( ( (gfloat)((l) & ~3) ) / (1 << 18) )
144 #define DW_STYLE_REL_LENGTH_VAL(l) ( ( (gfloat)((l) & ~3) ) / (1 << 18) )
145
146 #define DW_STYLE_LENGTH_AUTO 0
147
148
149 struct _DwStyleBox
150 {
151 /* in future also percentages */
152 gint32 top, right, bottom, left;
153 };
154
155 struct _DwStyle
156 {
157 gint ref_count;
158
159 DwStyleFont *font;
160 DwStyleTextDecoration text_decoration;
161 DwStyleColor *color, *background_color;
162
163 DwStyleTextAlignType text_align;
164 DwStyleVAlignType valign;
165 gchar text_align_char; /* In future, strings will be supported. */
166
167 gint32 border_spacing;
168 DwStyleLength width, height;
169
170 DwStyleBox margin, border_width, padding;
171 struct { DwStyleShadedColor *top, *right, *bottom, *left; } border_color;
172 struct { DwStyleBorderStyle top, right, bottom, left; } border_style;
173
174 DwStyleDisplayType display;
175 DwStyleWhiteSpace white_space;
176 DwStyleListStyleType list_style_type;
177
178 gint x_link;
179 DwTooltip *x_tooltip;
180 };
181
182 struct _DwStyleFont
183 {
184 gint ref_count;
185
186 char *name;
187 gint32 size;
188 gint weight;
189 DwStyleFontStyle style;
190
191 #ifdef USE_TYPE1
192 gint t1fontid;
193 #else
194 GdkFont *font;
195 #endif
196 gint space_width;
197 gint x_height;
198 };
199
200
201 struct _DwStyleColor
202 {
203 gint ref_count;
204 gint color_val;
205 GdkColor color, inverse_color;
206 GdkGC *gc, *inverse_gc;
207 };
208
209
210 struct _DwStyleShadedColor
211 {
212 gint ref_count;
213 gint color_val;
214 GdkColor color, inverse_color, color_dark, color_light;
215 GdkGC *gc, *inverse_gc, *gc_dark, *gc_light;
216 };
217
218
219 void a_Dw_style_init (void);
220 void a_Dw_style_freeall (void);
221
222 void a_Dw_style_init_values (DwStyle *style_attrs,
223 GdkWindow *window);
224 void a_Dw_style_reset_values (DwStyle *style_attrs,
225 GdkWindow *window);
226
227 DwStyle* a_Dw_style_new (DwStyle *style_attrs,
228 GdkWindow *window);
229 DwStyleFont* a_Dw_style_font_new (DwStyleFont *font_attrs);
230 DwStyleFont* a_Dw_style_font_new_from_list (DwStyleFont *font_attrs,
231 gchar *default_family);
232 DwStyleColor* a_Dw_style_color_new (gint color_val,
233 GdkWindow *window);
234 DwStyleShadedColor* a_Dw_style_shaded_color_new (gint color_val,
235 GdkWindow *window);
236
237 gboolean a_Dw_style_size_diffs (DwStyle *style1,
238 DwStyle *style2);
239
240 #define a_Dw_style_ref(style) ((style)->ref_count++)
241 #define a_Dw_style_unref(style) if (--((style)->ref_count) == 0) \
242 Dw_style_remove (style)
243
244
245 /* Don't use this function directly! */
246 void Dw_style_remove (DwStyle *style);
247
248 #define a_Dw_style_box_set_val(box, val) \
249 ((box)->top = (box)->right = (box)->bottom = (box)->left = (val))
250 #define a_Dw_style_box_set_border_color(style, val) \
251 ((style)->border_color.top = (style)->border_color.right = \
252 (style)->border_color.bottom = (style)->border_color.left = (val))
253 #define a_Dw_style_box_set_border_style(style, val) \
254 ((style)->border_style.top = (style)->border_style.right = \
255 (style)->border_style.bottom = (style)->border_style.left = (val))
256
257 /* For use of widgets */
258 #define DW_INFINITY 1000000 /* random */
259
260 void p_Dw_style_draw_border (GdkWindow *window,
261 GdkRectangle *area,
262 gint32 vx,
263 gint32 vy,
264 gint32 x,
265 gint32 y,
266 gint32 width,
267 gint32 height,
268 DwStyle *style,
269 gboolean inverse);
270 void p_Dw_style_draw_background (GdkWindow *window,
271 GdkRectangle *area,
272 gint32 vx,
273 gint32 vy,
274 gint32 x,
275 gint32 y,
276 gint32 width,
277 gint32 height,
278 DwStyle *style,
279 gboolean inverse);
280
281 void a_Dw_style_numtostr (gint num,
282 gchar *buf,
283 gint buflen,
284 DwStyleListStyleType list_style_tyle);
285
286 #define p_Dw_style_box_offset_x(style) ((style)->margin.left + \
287 (style)->border_width.left + \
288 (style)->padding.left)
289 #define p_Dw_style_box_rest_width(style) ((style)->margin.right + \
290 (style)->border_width.right + \
291 (style)->padding.right)
292 #define p_Dw_style_box_diff_width(style) (p_Dw_style_box_offset_x(style) + \
293 p_Dw_style_box_rest_width(style))
294
295 #define p_Dw_style_box_offset_y(style) ((style)->margin.top + \
296 (style)->border_width.top + \
297 (style)->padding.top)
298 #define p_Dw_style_box_rest_height(style) ((style)->margin.bottom + \
299 (style)->border_width.bottom + \
300 (style)->padding.bottom)
301 #define p_Dw_style_box_diff_height(style) (p_Dw_style_box_offset_y(style) + \
302 p_Dw_style_box_rest_height(style))
303
304 #endif /* __DW_STYLE_H__ */
0 /*
1 * File: dw_table.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <sgeerken@users.sourceforge.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <string.h>
12
13 #include "dw_table.h"
14 #include "list.h"
15 #include "prefs.h"
16 #include "dw_gtk_viewport.h"
17
18 #define DEBUG_CALC_LEVEL 10
19 #define DEBUG_EXTR_LEVEL 10
20 #define DEBUG_WIDTH_LEVEL 10
21 #define DEBUG_ITERATOR_LEVEL 0
22
23 /*#define DEBUG_LEVEL 10*/
24 #include "debug.h"
25
26 /*
27 * Some notes:
28 *
29 * - table->num_children always has the value table->num_cols *
30 * table->num_rows, and shows the minimal size of
31 * table->children, *not* the number of children.
32 *
33 * - table->children[i] may be NULL (unused cells), or a pointer to
34 * DwTableChild. A DwTableChild may be a span space (blocked by
35 * COLSPAN or ROWSPAN of another cell), or a cell starting at
36 * this position. In the last case, the macro CHILD_DEFINED
37 * returns TRUE.
38 */
39
40 /*#define BORDERS*/
41
42 #define CHILD_DEFINED(table, n) ((n) < (table)->num_children && \
43 (table)->children[(n)] != NULL && \
44 (table)->children[(n)]->type != \
45 DW_TABLE_SPAN_SPACE)
46
47
48 static void Dw_table_init (DwTable *table);
49 static void Dw_table_class_init (DwTableClass *klass);
50
51 static void Dw_table_destroy (GtkObject *object);
52
53 static void Dw_table_size_request (DwWidget *widget,
54 DwRequisition *requisition);
55 static void Dw_table_get_extremes (DwWidget *widget,
56 DwExtremes *extremes);
57 static void Dw_table_size_allocate (DwWidget *widget,
58 DwAllocation *allocation);
59 static void Dw_table_set_width (DwWidget *widget,
60 gint32 width);
61 static void Dw_table_set_ascent (DwWidget *widget,
62 gint32 ascent);
63 static void Dw_table_set_descent (DwWidget *widget,
64 gint32 descent);
65 static void Dw_table_draw (DwWidget *widget,
66 DwRectangle *area,
67 GdkEventExpose *event);
68 static gboolean Dw_table_button_press (DwWidget *widget,
69 gint32 x,
70 gint32 y,
71 GdkEventButton *event);
72 static gboolean Dw_table_button_release(DwWidget *widget,
73 gint32 x,
74 gint32 y,
75 GdkEventButton *event);
76 static gboolean Dw_table_motion_notify (DwWidget *widget,
77 gint32 x,
78 gint32 y,
79 GdkEventMotion *event);
80
81 static void Dw_table_add (DwContainer *container,
82 DwWidget *widget);
83 static void Dw_table_remove (DwContainer *container,
84 DwWidget *widget);
85 static void Dw_table_forall (DwContainer *container,
86 DwCallback callback,
87 gpointer callback_data);
88
89 static DwIterator* Dw_table_iterator (DwWidget *widget,
90 gint mask,
91 gboolean at_end);
92 static gboolean Dw_table_iterator_next (DwIterator *it);
93 static gboolean Dw_table_iterator_prev (DwIterator *it);
94
95
96 static void Dw_table_realloc_children (DwTable *table);
97
98 static void Dw_table_sub_create (DwTable *table);
99 static void Dw_table_sub_free (DwTableSub *sub);
100 static void Dw_table_sub_get_extremes (DwTableSub *sub);
101 static void Dw_table_sub_calc_col_widths (DwTableSub *sub,
102 gint32 width,
103 gint32 total_width);
104
105
106 static DwContainerClass *parent_class;
107
108 enum {
109 /* values of use_percentage */
110 USE_PERCENTAGE_NO, /* No percentage in subtable, and USE_PERCENTAGE_NO
111 * in all sub-subtables.
112 */
113 USE_PERCENTAGE_SOME, /* None of the other cases. This affects the minimal,
114 * but not the maximal width of the subtable.
115 */
116 USE_PERCENTAGE_ALL /* Percentage in subtable, or USE_PERCENTAGE_ALL in
117 * all sub-subtables. This affects the minimal and
118 * the maximal width of the subtable.
119 */
120 };
121
122 /*
123 * Standard Gtk+ function.
124 */
125 GtkType a_Dw_table_get_type (void)
126 {
127 static GtkType type = 0;
128
129 if (!type) {
130 GtkTypeInfo info = {
131 "DwTable",
132 sizeof (DwTable),
133 sizeof (DwTableClass),
134 (GtkClassInitFunc) Dw_table_class_init,
135 (GtkObjectInitFunc) Dw_table_init,
136 (GtkArgSetFunc) NULL,
137 (GtkArgGetFunc) NULL,
138 (GtkClassInitFunc) NULL
139 };
140
141 type = gtk_type_unique (DW_TYPE_CONTAINER, &info);
142 }
143
144 return type;
145 }
146
147
148 /*
149 * Standard Gtk+ function.
150 */
151 DwWidget* a_Dw_table_new (void)
152 {
153 GtkObject *object;
154
155 object = gtk_object_new (DW_TYPE_TABLE, NULL);
156 DBG_OBJ_CREATE (object, "DwTable");
157 return DW_WIDGET (object);
158 }
159
160
161 /*
162 * Standard Gtk+ function.
163 */
164 static void Dw_table_init (DwTable *table)
165 {
166 DW_WIDGET_SET_FLAGS (table, DW_USES_HINTS);
167
168 /* random value */
169 table->avail_width = 100;
170
171 table->cur_row = -1;
172 table->cur_col = 0;
173
174 table->num_children_max = 1; /* 16 */
175 table->num_children = 0;
176 table->children = NULL;
177
178 table->num_col_width_max = 1; /* 8 */
179 table->num_cols = 0;
180 table->col_width = NULL;
181
182 table->cum_height_max = 1; /* row cumulative height array */
183 table->num_rows = 0; /* num_cum_height is (num_rows + 1) */
184 table->cum_height = g_new(gint32, table->cum_height_max);
185
186 table->row_style_max = 1;
187 table->row_style = g_new(DwStyle*, table->row_style_max);
188
189 table->baseline_max = 1;
190 table->baseline = g_new(gint32, table->baseline_max);
191
192 table->sub = NULL;
193 }
194
195
196 /*
197 * Standard Gtk+ function.
198 */
199 static void Dw_table_class_init (DwTableClass *klass)
200 {
201 GtkObjectClass *object_class;
202 DwWidgetClass *widget_class;
203 DwContainerClass *container_class;
204
205 object_class = (GtkObjectClass*) klass;
206 widget_class = (DwWidgetClass*) klass;
207 container_class = (DwContainerClass*) klass;
208
209 parent_class = gtk_type_class (DW_TYPE_CONTAINER);
210
211 object_class->destroy = Dw_table_destroy;
212
213 widget_class->size_request = Dw_table_size_request;
214 widget_class->get_extremes = Dw_table_get_extremes;
215 widget_class->size_allocate = Dw_table_size_allocate;
216 widget_class->set_width = Dw_table_set_width;
217 widget_class->set_ascent = Dw_table_set_ascent;
218 widget_class->set_descent = Dw_table_set_descent;
219 widget_class->draw = Dw_table_draw;
220 widget_class->button_press_event = Dw_table_button_press;
221 widget_class->button_release_event = Dw_table_button_release;
222 widget_class->motion_notify_event = Dw_table_motion_notify;
223 widget_class->iterator = Dw_table_iterator;
224
225 container_class->add = Dw_table_add;
226 container_class->remove = Dw_table_remove;
227 container_class->forall = Dw_table_forall;
228 }
229
230
231 /*
232 * Standard Gtk+ function.
233 */
234 static void Dw_table_destroy (GtkObject *object)
235 {
236 DwTable *table = DW_TABLE (object);
237 gint i;
238
239 for (i = 0; i < table->num_children; i++)
240 if (table->children[i]) {
241 if (table->children[i]->type == DW_TABLE_CELL)
242 gtk_object_unref(GTK_OBJECT(table->children[i]->data.cell.widget));
243 g_free (table->children[i]);
244 }
245
246 g_free (table->children);
247 table->num_children = 0;
248 g_free (table->col_width);
249 g_free (table->cum_height);
250
251 for (i = 0; i < table->num_rows; i++)
252 if (table->row_style[i])
253 a_Dw_style_unref (table->row_style[i]);
254 g_free (table->row_style);
255 g_free (table->baseline);
256
257 if (table->sub)
258 Dw_table_sub_free (table->sub);
259
260 GTK_OBJECT_CLASS(parent_class)->destroy (object);
261 }
262
263 /*
264 * Standard Dw function.
265 * Furthermore, table->width and table->cum_height are filled.
266 */
267 static void Dw_table_size_request (DwWidget *widget,
268 DwRequisition *requisition)
269 {
270 DwExtremes extremes;
271 gint32 avail_width;
272 DwTable *table = DW_TABLE (widget);
273 gint row, row2, col, col2, n;
274 gint32 child_height, set_width;
275 DwWidget *child;
276 DwRequisition child_requisition;
277
278 /* Calculate the widths of the columns, and store it in table->col_width. */
279 if (table->num_cols > 0) {
280 p_Dw_widget_get_extremes (DW_WIDGET (table), &extremes);
281
282 if (DW_STYLE_IS_ABS_LENGTH (widget->style->width))
283 avail_width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width);
284 else if (DW_STYLE_IS_PER_LENGTH (widget->style->width))
285 /*
286 * If the width is > 100%, we use 100%, this prevents ugly
287 * results. (May be changed in future, when a more powerful
288 * rendering is implemented, to handle fixed positions etc.,
289 * as defined by CSS2.)
290 */
291 avail_width = table->avail_width
292 * MIN (DW_STYLE_PER_LENGTH_VAL (widget->style->width), 1.0);
293 else
294 avail_width = table->avail_width;
295
296 if (avail_width < extremes.min_width)
297 avail_width = extremes.min_width;
298 if (widget->style->width == DW_STYLE_LENGTH_AUTO &&
299 avail_width > extremes.max_width)
300 avail_width = extremes.max_width;
301
302 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3,
303 "--> Calculating column widths for (%d x %d) table.\n",
304 table->num_cols, table->num_rows);
305 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3,
306 " [ %d --(%d, %d)--> %d ]\n",
307 table->avail_width, extremes.min_width, extremes.max_width,
308 avail_width);
309
310 avail_width -= (2 * widget->style->border_spacing +
311 p_Dw_style_box_diff_width (widget->style));
312 Dw_table_sub_calc_col_widths
313 (table->sub, avail_width,
314 avail_width - (table->num_cols - 1) * widget->style->border_spacing);
315
316 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "<-- END\n");
317 }
318
319 /* Calculate the total width. */
320 requisition->width = p_Dw_style_box_diff_width (widget->style) +
321 (table->num_cols + 1) * widget->style->border_spacing;
322 for (col = 0; col < table->num_cols; col++)
323 requisition->width += table->col_width[col];
324
325 /* Calculate the height. */
326 for (row = 0; row <= table->num_rows; row++)
327 table->cum_height[row] = widget->style->border_spacing;
328
329 for (row = 0; row < table->num_rows; row++) {
330 /* Calculate the baselines, for cells only with valign=baseline. */
331 table->baseline[row] = 0;
332 for (col = 0; col < table->num_cols; col++) {
333 n = col + row * table->num_cols;
334 if (CHILD_DEFINED (table, n)) {
335 child = table->children[n]->data.cell.widget;
336 if (child->style->valign == DW_STYLE_VALIGN_BASELINE) {
337 p_Dw_widget_size_request (child, &child_requisition);
338 if (child_requisition.ascent > table->baseline[row])
339 table->baseline[row] = child_requisition.ascent;
340 }
341 }
342 }
343
344 for (col = 0; col < table->num_cols; col++) {
345 n = col + row * table->num_cols;
346 if (CHILD_DEFINED (table, n)) {
347 child = table->children[n]->data.cell.widget;
348
349 set_width = 0;
350 for (col2 = col;
351 col2 < col + table->children[n]->data.cell.colspan;
352 col2++)
353 set_width += table->col_width[col2];
354
355 p_Dw_widget_set_width (child, set_width);
356 p_Dw_widget_set_ascent (child, table->avail_ascent);
357 p_Dw_widget_set_descent (child, table->avail_descent);
358
359 p_Dw_widget_size_request (child, &child_requisition);
360 /* Cells with valign=baseline have a height which may
361 * be greater than the height returned by size_request. */
362 if (child->style->valign == DW_STYLE_VALIGN_BASELINE)
363 child_height =
364 table->baseline[row] + child_requisition.descent;
365 else
366 child_height =
367 child_requisition.ascent + child_requisition.descent;
368
369 row2 = row + table->children[n]->data.cell.rowspan;
370
371 /* row2 is the row directly forced downward by the child */
372 if (table->cum_height[row2] <
373 table->cum_height[row] + child_height
374 + widget->style->border_spacing ) {
375 table->cum_height[row2] =
376 table->cum_height[row] + child_height
377 + widget->style->border_spacing;
378 DEBUG_MSG (1, "Row %d starts at %d.\n",
379 row2, table->cum_height[row + table->children[n]
380 ->data.cell.rowspan]);
381 /* todo: column widths should be adjusted a bit. */
382 }
383 }
384 }
385 /* if we've hit a row that contains no defined children, we need
386 to force cumulative heights to trickle down to lower rows */
387
388 if (table->cum_height[row + 1] < table->cum_height[row])
389 table->cum_height[row + 1] = table->cum_height[row];
390 }
391 requisition->ascent = table->cum_height[table->num_rows] +
392 p_Dw_style_box_diff_height (widget->style);
393 requisition->descent = 0;
394
395 DEBUG_MSG (2, "Dw_table_size_request: %d x %d x %d\n",
396 requisition->width, requisition->ascent, requisition->descent);
397 }
398
399
400 /*
401 * Standard Dw function.
402 */
403 static void Dw_table_get_extremes (DwWidget *widget,
404 DwExtremes *extremes)
405 {
406 DwTable *table = DW_TABLE (widget);
407 gint32 diff;
408
409 if (table->num_cols > 0) {
410 if (table->sub == NULL)
411 Dw_table_sub_create (table);
412
413 DEBUG_MSG (DEBUG_EXTR_LEVEL + 3,
414 "--> [%p] Dw_table_get_extremes for (%d x %d) table.\n",
415 table, table->num_cols, table->num_rows);
416
417 Dw_table_sub_get_extremes (table->sub);
418 diff = 2 * widget->style->border_spacing +
419 p_Dw_style_box_diff_width (widget->style);
420 extremes->min_width = table->sub->total_extremes.min_width + diff;
421 extremes->max_width = table->sub->total_extremes.max_width + diff;
422
423 DEBUG_MSG (DEBUG_EXTR_LEVEL + 2, "<-- [%p] END\n", table);
424 } else {
425 extremes->min_width = widget->style->border_spacing
426 + p_Dw_style_box_diff_width (widget->style);
427 extremes->max_width = widget->style->border_spacing
428 + p_Dw_style_box_diff_width (widget->style);
429 }
430 }
431
432
433 /*
434 * Standard Dw function.
435 */
436 static void Dw_table_size_allocate (DwWidget *widget,
437 DwAllocation *allocation)
438 {
439 DwTable *table = DW_TABLE (widget);
440 gint row, col, col2, n;
441 gint32 col_offset, cell_width, cell_height, height_diff, vdiff;
442 DwWidget *child;
443 DwAllocation child_allocation;
444 DwRequisition child_requisition;
445
446 /*
447 * todo: This implementation depends on that size_request has been
448 * called before. Can this be assumed?
449 */
450
451 DEBUG_MSG (2, "Dw_table_size_allocate: %d x %d x %d\n",
452 allocation->width, allocation->ascent, allocation->descent);
453
454 vdiff = allocation->y + p_Dw_style_box_offset_y (widget->style);
455
456 for (row = 0; row < table->num_rows; row++) {
457 col_offset = allocation->x + widget->style->border_spacing
458 + p_Dw_style_box_offset_x (widget->style);
459
460 for (col = 0; col < table->num_cols; col++) {
461 n = col + row * table->num_cols;
462 if (CHILD_DEFINED (table, n)) {
463 cell_width = (table->children[n]->data.cell.colspan - 1)
464 * widget->style->border_spacing;
465 for (col2 = col;
466 col2 < col + table->children[n]->data.cell.colspan;
467 col2++)
468 cell_width += table->col_width[col2];
469
470 child = table->children[n]->data.cell.widget;
471 cell_height =
472 table->cum_height[row + table->children[n]->data.cell.rowspan] -
473 table->cum_height[row] - widget->style->border_spacing;
474 child_allocation.x = col_offset;
475 child_allocation.y = vdiff + table->cum_height[row];
476 child_allocation.width = cell_width;
477
478 /* Vertical alignment is done via allocation. */
479 p_Dw_widget_size_request (child, &child_requisition);
480 switch (child->style->valign) {
481 case DW_STYLE_VALIGN_TOP:
482 child_allocation.ascent = child_requisition.ascent;
483 child_allocation.descent =
484 cell_height - child_requisition.ascent;
485 break;
486 case DW_STYLE_VALIGN_BOTTOM:
487 child_allocation.ascent =
488 cell_height - child_requisition.descent;
489 child_allocation.descent = child_requisition.descent;
490 break;
491 case DW_STYLE_VALIGN_MIDDLE:
492 height_diff = cell_height - (child_requisition.ascent +
493 child_requisition.descent);
494 child_allocation.ascent =
495 child_requisition.ascent + height_diff / 2;
496 child_allocation.descent =
497 child_requisition.descent + (height_diff + 1) / 2;
498 /* The "+ 1" is to avoid rounding errors. */
499 break;
500 case DW_STYLE_VALIGN_BASELINE:
501 child_allocation.ascent = table->baseline[row];
502 child_allocation.descent =
503 cell_height - table->baseline[row];
504 break;
505 default:
506 g_assert_not_reached();
507 break;
508 }
509
510 p_Dw_widget_size_allocate (child, &child_allocation);
511 }
512
513 col_offset += table->col_width[col] + widget->style->border_spacing;
514 }
515 }
516 }
517
518
519 /*
520 * Standard Dw function.
521 */
522 static void Dw_table_set_width (DwWidget *widget,
523 gint32 width)
524 {
525 DwTable *table;
526
527 table = DW_TABLE (widget);
528 /* If limit_text_width is set to YES, a queue_resize may also be
529 necessary. */
530 if (table->avail_width != width || prefs.limit_text_width) {
531 table->avail_width = width;
532 p_Dw_widget_queue_resize (widget, 0, FALSE);
533 }
534 }
535
536
537 /*
538 * Standard Dw function.
539 */
540 static void Dw_table_set_ascent (DwWidget *widget,
541 gint32 ascent)
542 {
543 DwTable *table;
544
545 table = DW_TABLE (widget);
546 if (table->avail_ascent != ascent) {
547 table->avail_ascent = ascent;
548 p_Dw_widget_queue_resize (widget, 0, FALSE);
549 }
550 }
551
552
553 /*
554 * Standard Dw function.
555 */
556 static void Dw_table_set_descent (DwWidget *widget,
557 gint32 descent)
558 {
559 DwTable *table;
560
561 table = DW_TABLE (widget);
562 if (table->avail_descent != descent) {
563 table->avail_descent = descent;
564 p_Dw_widget_queue_resize (widget, 0, FALSE);
565 }
566 }
567
568 /*
569 * Standard Dw function.
570 */
571 static void Dw_table_draw (DwWidget *widget,
572 DwRectangle *area,
573 GdkEventExpose *event)
574 {
575 /* can be optimized, by iterating on the lines in area */
576 DwTable *table = DW_TABLE (widget);
577 DwRectangle child_area;
578 DwWidget *child;
579 gint i;
580 gint32 offx, offy, width;
581
582 p_Dw_widget_draw_widget_box (widget, area, FALSE);
583
584 offx = p_Dw_style_box_offset_x (widget->style);
585 offy = p_Dw_style_box_offset_y (widget->style);
586 width = DW_WIDGET_CONTENT_WIDTH(widget);
587
588 for (i = 0; i < table->num_rows; i++)
589 if (table->row_style[i])
590 p_Dw_widget_draw_box (widget, table->row_style[i], area,
591 offx, offy + table->cum_height[i],
592 width,
593 table->cum_height[i + 1] - table->cum_height[i]
594 - widget->style->border_spacing, FALSE);
595
596 for (i = 0; i < table->num_children; i++)
597 if (CHILD_DEFINED (table, i)) {
598 child = table->children[i]->data.cell.widget;
599 if (p_Dw_widget_intersect (child, area, &child_area)) {
600 #ifdef BORDERS
601 gdk_draw_rectangle
602 (DW_WIDGET_WINDOW (child),
603 widget->viewport->style->dark_gc[widget->viewport->state],
604 FALSE,
605 Dw_widget_x_world_to_viewport (child, child->allocation.x) - 1,
606 Dw_widget_y_world_to_viewport (child, child->allocation.y) - 1,
607 child->allocation.width + 2,
608 DW_WIDGET_HEIGHT(child) + 2);
609 #endif
610 p_Dw_widget_draw (child, &child_area, event);
611 }
612 }
613
614 #ifdef BORDERS
615 gdk_draw_rectangle (DW_WIDGET_WINDOW (widget),
616 widget->style->color->gc, FALSE,
617 Dw_widget_x_world_to_viewport (widget,
618 widget->allocation.x),
619 Dw_widget_y_world_to_viewport (widget,
620 widget->allocation.y),
621 widget->allocation.width,
622 DW_WIDGET_HEIGHT(widget));
623 #endif
624 }
625
626
627 /*
628 * Construct an iterator for a cell.
629 */
630 static DwIterator *Dw_table_construct_iterator (DwTable *table,
631 gint cell_index)
632 {
633 DwIterator *it;
634
635 it = Dw_table_iterator (DW_WIDGET (table), DW_CONTENT_ALL, FALSE);
636 ((DwIteratorInt*)it)->pos = cell_index;
637 it->content.type = DW_CONTENT_WIDGET;
638 it->content.data.widget = table->children[cell_index]->data.cell.widget;
639 return it;
640 }
641
642 /*
643 * Construct an iterator pointing to the end of the table.
644 */
645 static DwIterator *Dw_table_construct_last_iterator (DwTable *table,
646 int *char_pos)
647 {
648 int n;
649 DwIterator *it = NULL;
650 DwTableChild *child = NULL;
651
652 *char_pos = 0;
653 n = table->num_children -1;
654 while (n >= 0 && ((child = table->children[n]) == NULL ||
655 child->type != DW_TABLE_CELL))
656 n--;
657 if (n >= 0) {
658 /* Try to construct an iterator for the last child of the table. */
659 it = a_Dw_widget_iterator (child->data.cell.widget,
660 DW_CONTENT_ALL, TRUE);
661 if (it != NULL && a_Dw_iterator_prev (it)) {
662 if (it->content.type == DW_CONTENT_TEXT)
663 *char_pos = strlen (it->content.data.text);
664 } else {
665 a_Dw_iterator_free (it);
666 it = NULL;
667 }
668
669 if (it == NULL)
670 /* This widget has no content, construct an iterator for the table. */
671 it = Dw_table_construct_iterator (table, n);
672 }
673
674 return it;
675 }
676
677 /*
678 * Send event to selection.
679 */
680 static gboolean Dw_table_send_selection_event (DwTable *table,
681 gint (*fn) (Selection*,
682 DwIterator *,
683 gint, gint,
684 GdkEventButton*,
685 gboolean
686 within_content),
687 gint32 x, gint32 y,
688 GdkEventButton *event)
689 {
690 int row, col, n, char_pos;
691 DwIterator *it;
692 DwTableChild *child;
693 gint32 offy, cellspacing, cum_width;
694
695 it = NULL;
696 char_pos = 0;
697 cellspacing = DW_WIDGET(table)->style->border_spacing;
698
699 /* Search row.
700 * Just a note: Improving the speed of this does not give very much,
701 * since events (in dw_widget.c) are always much slower. */
702 offy = p_Dw_style_box_offset_y (DW_WIDGET(table)->style);
703 row = 0;
704 while (row < table->num_rows &&
705 y > table->cum_height[row + 1] + offy - cellspacing)
706 row++;
707
708 if (row == table->num_rows)
709 /* Below the last row: We create an iterator of the last child,
710 * starting at the end (if possible). */
711 it = Dw_table_construct_last_iterator (table, &char_pos);
712 else {
713 if (y < table->cum_height[row] + offy) {
714 /* Before the row: Start at first column. */
715 col = 0;
716 } else {
717 /* Within the row: Search column.
718 * NOTE: col may be table->num_cols, this is intended, see below */
719 cum_width =
720 p_Dw_style_box_offset_x (DW_WIDGET(table)->style) +
721 cellspacing + table->col_width[0];
722 col = 0;
723 while (col < table->num_cols && x > cum_width) {
724 col++;
725 cum_width += table->col_width[col] + cellspacing;
726 }
727
728 /* Already right of the middle of the next column?
729 * (Actually, this feature is only needed for cases where widgets
730 * are added to a table, which do not process mouse events. This
731 * is never the case in dillo. But it works.) */
732 if (col < table->num_cols &&
733 x >= cum_width - table->col_width[col] / 2)
734 col++;
735 }
736
737 n = col + row * table->num_cols;
738 /* If col == table->num_cols, this may be happen: */
739 if (n == table->num_children)
740 it = Dw_table_construct_last_iterator (table, &char_pos);
741 else {
742 /* some corrections */
743 for (; n < table->num_children; n++) {
744 if ((child = table->children[n]) &&
745 child->type == DW_TABLE_CELL) {
746 it = Dw_table_construct_iterator (table, n);
747 break;
748 }
749 }
750 }
751 }
752
753 if (it) {
754 return fn (GTK_DW_VIEWPORT(DW_WIDGET(table)->viewport)->selection,
755 it, char_pos, -1, event, FALSE);
756 } else
757 return FALSE;
758 }
759
760
761 /*
762 * Standard Dw function.
763 */
764 static gboolean Dw_table_button_press (DwWidget *widget,
765 gint32 x,
766 gint32 y,
767 GdkEventButton *event)
768 {
769 return Dw_table_send_selection_event (DW_TABLE (widget),
770 a_Selection_button_press, x, y,
771 event);
772 }
773
774 /*
775 * Standard Dw function.
776 */
777 static gboolean Dw_table_button_release (DwWidget *widget,
778 gint32 x,
779 gint32 y,
780 GdkEventButton *event)
781 {
782 return Dw_table_send_selection_event (DW_TABLE (widget),
783 a_Selection_button_release, x, y,
784 event);
785 }
786
787
788 /*
789 * Standard Dw function.
790 */
791 static gboolean Dw_table_motion_notify (DwWidget *widget,
792 gint32 x,
793 gint32 y,
794 GdkEventMotion *event)
795 {
796 if (event && (event->state & GDK_BUTTON1_MASK))
797 return Dw_table_send_selection_event (DW_TABLE (widget),
798 a_Selection_button_motion, x, y,
799 NULL);
800 else
801 return FALSE;
802 }
803
804
805 /*
806 * Standard Dw function.
807 */
808 static void Dw_table_add (DwContainer *container,
809 DwWidget *widget)
810 {
811 a_Dw_table_add_cell (DW_TABLE (container), widget, 1, 1);
812 }
813
814
815 /*
816 * Standard Dw function.
817 */
818 static void Dw_table_remove (DwContainer *container,
819 DwWidget *widget)
820 {
821 /* used? */
822 }
823
824
825 /*
826 * Standard Dw function.
827 */
828 static void Dw_table_forall (DwContainer *container,
829 DwCallback callback,
830 gpointer callback_data)
831 {
832 gint i;
833 DwTable *table = DW_TABLE (container);
834
835 for (i = 0; i < table->num_children; i++)
836 if (CHILD_DEFINED (table, i))
837 callback (table->children[i]->data.cell.widget, callback_data);
838 }
839
840
841 /*
842 * Make sure that there is enough space for table->children,
843 * table->cum_height etc., and fill the rest of table->children with
844 * NULL.
845 */
846 static void Dw_table_realloc_children (DwTable *table)
847 {
848 gint old_num_children, i;
849
850 table->num_rows++;
851 a_List_resize (table->cum_height, table->num_rows, table->cum_height_max);
852 table->num_rows--;
853 a_List_resize (table->row_style, table->num_rows, table->row_style_max);
854 a_List_resize (table->baseline, table->num_rows, table->baseline_max);
855
856 old_num_children = table->num_children;
857 table->num_children = table->num_cols * table->num_rows;
858 a_List_resize (table->children, table->num_children,
859 table->num_children_max);
860
861 for (i = old_num_children; i < table->num_children; i++)
862 table->children[i] = NULL;
863 }
864
865
866 /*
867 * Add widget as a cell to table. colspan and rowspan have the same
868 * meaning as the attributes of <td> and <th>.
869 */
870 void a_Dw_table_add_cell (DwTable *table,
871 DwWidget *widget,
872 gint colspan,
873 gint rowspan)
874 {
875 gint col, row, old_num_cols, old_num_rows;
876 DwTableChild *child;
877
878 /* We limit the values for colspan and rowspan to 50, to avoid
879 * attacks by malicious web pages.
880 * todo: Some warnings would be nice. */
881 if (colspan > 50)
882 colspan = 50;
883 if (rowspan > 50)
884 rowspan = 50;
885
886 if (table->num_rows == 0)
887 /* to prevent a crash */
888 a_Dw_table_add_row (table, NULL);
889
890 /* todo: {col|row}span = 0 is not supported yet: */
891 if (colspan < 1)
892 colspan = 1;
893 if (rowspan < 1)
894 rowspan = 1;
895
896 while (table->cur_col < table->num_cols &&
897 (child = table->children[table->cur_row * table->num_cols
898 + table->cur_col]) != NULL &&
899 child->type == DW_TABLE_SPAN_SPACE)
900 table->cur_col++;
901
902 if (table->cur_row + rowspan > table->num_rows) {
903 old_num_rows = table->num_rows;
904 table->num_rows = table->cur_row + rowspan;
905 Dw_table_realloc_children (table);
906 for (row = old_num_rows; row < table->num_rows; row++)
907 table->row_style[row] = NULL;
908 }
909
910 if (table->cur_col + colspan > table->num_cols) {
911 old_num_cols = table->num_cols;
912 table->num_cols = table->cur_col + colspan;
913 a_List_resize (table->col_width, table->num_cols,
914 table->num_col_width_max);
915
916 table->num_children = table->num_cols * table->num_rows;
917 a_List_resize (table->children, table->num_children,
918 table->num_children_max);
919
920 for (row = table->num_rows - 1; row >= 0; row--) {
921 for (col = old_num_cols - 1; col >= 0; col--) {
922 table->children[row * table->num_cols + col] =
923 table->children[row * old_num_cols + col];
924
925 }
926
927 for (col = old_num_cols; col < table->num_cols; col++)
928 table->children[row * table->num_cols + col] = NULL;
929 }
930 }
931
932 for (col = 0; col < colspan; col++)
933 for (row = 0; row < rowspan; row++)
934 if (!(col == 0 && row == 0)) {
935 child = g_new (DwTableChild, 1);
936 child->type = DW_TABLE_SPAN_SPACE;
937 child->data.span_space.start_col = table->cur_col;
938 child->data.span_space.start_row = table->cur_row;
939 table->children[(table->cur_row + row) * table->num_cols
940 + (table->cur_col + col)] = child;
941 }
942
943 child = g_new (DwTableChild, 1);
944 child->type = DW_TABLE_CELL;
945 child->data.cell.widget = widget;
946 child->data.cell.colspan = colspan;
947 child->data.cell.rowspan = rowspan;
948 table->children[table->cur_row * table->num_cols + table->cur_col] = child;
949 table->cur_col += colspan;
950
951 p_Dw_widget_set_parent (widget, DW_WIDGET (table));
952 if (table->row_style[table->cur_row] != NULL)
953 p_Dw_widget_set_bg_color
954 (widget, table->row_style[table->cur_row]->background_color);
955 p_Dw_widget_queue_resize (widget, 0, TRUE);
956
957 /* The cell structure has changed, so the subtables have to be rebuilt. */
958 if (table->sub) {
959 Dw_table_sub_free (table->sub);
960 table->sub = NULL;
961 }
962 }
963
964
965 /*
966 * Add a new row to the table, and start adding cells from the
967 * left-most column.
968 */
969 void a_Dw_table_add_row (DwTable *table,
970 DwStyle *style)
971 {
972 gint row, old_num_rows;
973
974 table->cur_row++;
975
976 if (table->cur_row >= table->num_rows) {
977 old_num_rows = table->num_rows;
978 table->num_rows = table->cur_row + 1;
979 Dw_table_realloc_children (table);
980 for (row = old_num_rows; row < table->num_rows; row++)
981 table->row_style[row] = NULL;
982 }
983
984 if (table->row_style[table->cur_row])
985 a_Dw_style_unref (table->row_style[table->cur_row]);
986 table->row_style[table->cur_row] = style;
987 if (style)
988 a_Dw_style_ref (style);
989
990 table->cur_col = 0;
991 }
992
993 /*
994 * Returns the DwTableCell to be passed to a_Dw_table_cell_new().
995 */
996 DwTableCell* a_Dw_table_get_cell_ref (DwTable *table)
997 {
998 gint row, n;
999 DwWidget *child;
1000
1001 for (row = 0; row <= table->num_rows; row++) {
1002 n = table->cur_col + row * table->num_cols;
1003 if (CHILD_DEFINED (table, n)) {
1004 child = table->children[n]->data.cell.widget;
1005 if (DW_IS_TABLE_CELL (child))
1006 return DW_TABLE_CELL (child);
1007 }
1008 }
1009
1010 return NULL;
1011 }
1012
1013
1014 /* ----------------------------------------------------------------------
1015 *
1016 * General Functions for Subtables
1017 *
1018 * ---------------------------------------------------------------------- */
1019
1020 static gboolean Dw_table_sub_spans_width (DwTableSub *sub,
1021 gint row)
1022 {
1023 gint n, start_col, start_row, start_n, colspan;
1024
1025 n = sub->start_col + sub->table->num_cols * row;
1026
1027 if (sub->table->children[n]) {
1028 if (sub->table->children[n]->type == DW_TABLE_CELL) {
1029 start_col = sub->start_col;
1030 start_row = row;
1031 } else {
1032 start_col = sub->table->children[n]->data.span_space.start_col;
1033 start_row = sub->table->children[n]->data.span_space.start_row;
1034 }
1035
1036 start_n = start_col + sub->table->num_cols * start_row;
1037 colspan = sub->table->children[start_n]->data.cell.colspan;
1038
1039 return (start_col <= sub->start_col &&
1040 start_col + colspan >= sub->end_col);
1041 } else
1042 return FALSE;
1043 }
1044
1045 /*
1046 * Fill the DwTableSub and create the sub DwTableSub's. Only for use
1047 * by Dw_table_get_sub.
1048 */
1049 static void Dw_table_sub_calc_subs (DwTableSub *sub)
1050 {
1051 gint col, row, n, start_n, colspan, most_right, start_col, start_row;
1052
1053 if (sub->start_col + 1 == sub->end_col) {
1054 DEBUG_MSG (DEBUG_CALC_LEVEL + 2, " single column at %d\n",
1055 sub->start_col);
1056 sub->num_subs = 0;
1057 sub->subs = NULL;
1058
1059 for (row = 0; row < sub->table->num_rows; row++)
1060 if (!a_Bitvec_get_bit (sub->removed_rows, row)) {
1061 a_Bitvec_set_bit (sub->spanning_rows, row);
1062 DEBUG_MSG (DEBUG_CALC_LEVEL + 1,
1063 " row %d spans width\n", row);
1064
1065 }
1066 } else {
1067 DEBUG_MSG (DEBUG_CALC_LEVEL + 2,
1068 " --> complex subtable from %d to %d\n",
1069 sub->start_col, sub->end_col);
1070
1071 for (row = 0; row < sub->table->num_rows; row++)
1072 if (!a_Bitvec_get_bit (sub->removed_rows, row) &&
1073 Dw_table_sub_spans_width (sub, row)) {
1074 a_Bitvec_set_bit (sub->spanning_rows, row);
1075 DEBUG_MSG (DEBUG_CALC_LEVEL + 1,
1076 " row %d spans width\n", row);
1077
1078 }
1079
1080 sub->num_subs = 0;
1081 sub->subs = g_new (DwTableSub, sub->end_col - sub->start_col);
1082
1083 col = sub->start_col;
1084 while (col < sub->end_col) {
1085 DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " column %d\n", col);
1086
1087 sub->subs[sub->num_subs].table = sub->table;
1088 sub->subs[sub->num_subs].start_col = col;
1089 sub->subs[sub->num_subs].removed_rows =
1090 a_Bitvec_new (sub->table->num_rows);
1091 sub->subs[sub->num_subs].spanning_rows =
1092 a_Bitvec_new (sub->table->num_rows);
1093
1094 /* Search for the greatest colspan value. */
1095 most_right = col + 1;
1096 for (row = 0; row < sub->table->num_rows; row++)
1097 if (a_Bitvec_get_bit (sub->removed_rows, row) ||
1098 a_Bitvec_get_bit (sub->spanning_rows, row))
1099 a_Bitvec_set_bit (sub->subs[sub->num_subs].removed_rows, row);
1100 else {
1101 n = col + sub->table->num_cols * row;
1102
1103 if (sub->table->children[n]) {
1104 if (sub->table->children[n]->type == DW_TABLE_CELL) {
1105 start_col = col;
1106 start_row = row;
1107 start_n = n;
1108 } else {
1109 start_col =
1110 sub->table->children[n]->data.span_space.start_col;
1111 start_row =
1112 sub->table->children[n]->data.span_space.start_row;
1113 start_n = start_col + sub->table->num_cols * start_row;
1114 }
1115
1116 if (row == start_row) {
1117 colspan =
1118 sub->table->children[start_n]->data.cell.colspan -
1119 (col - start_col);
1120
1121 if (start_col + colspan > most_right &&
1122 start_col + colspan <= sub->end_col)
1123 most_right = start_col + colspan;
1124 }
1125 }
1126 }
1127
1128 sub->subs[sub->num_subs].end_col = most_right;
1129 Dw_table_sub_calc_subs (&sub->subs[sub->num_subs]);
1130
1131 sub->num_subs++;
1132 col = most_right;
1133 }
1134
1135 DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " <-- END\n");
1136 }
1137 }
1138
1139 /*
1140 * Create a DwTableSub for a table.
1141 */
1142 static void Dw_table_sub_create (DwTable *table)
1143 {
1144 if (table->sub)
1145 Dw_table_sub_free (table->sub);
1146
1147 DEBUG_MSG (DEBUG_CALC_LEVEL + 3,
1148 "--> Dw_table_sub_create widths for (%d x %d) table.\n",
1149 table->num_cols, table->num_rows);
1150
1151 table->sub = g_new (DwTableSub, 1);
1152 table->sub->table = table;
1153 table->sub->start_col = 0;
1154 table->sub->end_col = table->num_cols;
1155 table->sub->removed_rows = a_Bitvec_new (table->num_rows);
1156 table->sub->spanning_rows = a_Bitvec_new (table->num_rows);
1157
1158 Dw_table_sub_calc_subs (table->sub);
1159
1160 DEBUG_MSG (DEBUG_CALC_LEVEL + 2, "<-- END\n");
1161 }
1162
1163 /*
1164 * Clean up a DwTableSub. Only for use by Dw_table_free_sub.
1165 */
1166 static void Dw_table_sub_clean_up (DwTableSub *sub)
1167 {
1168 gint i;
1169
1170 a_Bitvec_free (sub->removed_rows);
1171 a_Bitvec_free (sub->spanning_rows);
1172 if (sub->subs) {
1173 for (i = 0; i < sub->num_subs; i++)
1174 Dw_table_sub_clean_up (&sub->subs[i]);
1175 g_free (sub->subs);
1176 }
1177 }
1178
1179 /*
1180 * Free a DwTableSub.
1181 */
1182 static void Dw_table_sub_free (DwTableSub *sub)
1183 {
1184 Dw_table_sub_clean_up (sub);
1185 g_free (sub);
1186 }
1187
1188
1189 /* ----------------------------------------------------------------------
1190 *
1191 * Specific Functions for Subtables
1192 *
1193 * NOTE: The spaces left of the left most column and right of the
1194 * right most column do not belong to a subtable.
1195 *
1196 * ---------------------------------------------------------------------- */
1197
1198 /*
1199 * Calculate the extremes of a subtable.
1200 */
1201 static void Dw_table_sub_get_extremes (DwTableSub *sub)
1202 {
1203 gint i, row, n, start_col, start_row, start_n, num_cols, colspan;
1204 DwWidget *child, *widget;
1205 DwExtremes child_extremes, sum_extremes;
1206 gint32 col_width;
1207 gint32 border_spacing = DW_WIDGET(sub->table)->style->border_spacing;
1208 gfloat percentage;
1209 gfloat sum_percentage;
1210 gint use_percentage_map[3][3] = {
1211 { USE_PERCENTAGE_NO, USE_PERCENTAGE_SOME, -1 },
1212 { USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME },
1213 { USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME, USE_PERCENTAGE_ALL } };
1214
1215 num_cols = sub->end_col - sub->start_col;
1216 sub->use_percentage = USE_PERCENTAGE_NO;
1217 sub->fixed_width = FALSE;
1218 sub->percentage = 0;
1219
1220 DEBUG_MSG (DEBUG_EXTR_LEVEL + 2, " [%p] subtable from %d to %d\n",
1221 sub->table, sub->start_col, sub->end_col);
1222
1223 /* 1. cells spanning the whole width */
1224 sub->span_extremes.min_width = 0;
1225 sub->span_extremes.max_width = 0;
1226
1227 for (row = 0; row < sub->table->num_rows; row++) {
1228 if (!a_Bitvec_get_bit (sub->removed_rows, row) &&
1229 a_Bitvec_get_bit (sub->spanning_rows, row)) {
1230 n = sub->start_col + row * sub->table->num_cols;
1231 if (sub->table->children[n]) {
1232 if (sub->table->children[n]->type == DW_TABLE_CELL) {
1233 start_row = row;
1234 start_n = n;
1235 } else {
1236 start_col = sub->table->children[n]->data.span_space.start_col;
1237 start_row = sub->table->children[n]->data.span_space.start_row;
1238 start_n = start_col + sub->table->num_cols * start_row;
1239 }
1240
1241 if (row == start_row) {
1242 child = sub->table->children[start_n]->data.cell.widget;
1243 p_Dw_widget_get_extremes (child, &child_extremes);
1244 colspan = sub->table->children[start_n]->data.cell.colspan;
1245
1246 /* Adjust width argument of the cell */
1247 if (DW_STYLE_IS_ABS_LENGTH (child->style->width)) {
1248 col_width =
1249 DW_STYLE_ABS_LENGTH_VAL (child->style->width);
1250 if (col_width < child_extremes.min_width)
1251 col_width = child_extremes.min_width;
1252 child_extremes.min_width = col_width;
1253 child_extremes.max_width = col_width;
1254 sub->fixed_width = TRUE;
1255 DEBUG_MSG (DEBUG_EXTR_LEVEL, " [%p] following "
1256 "adjusted by %d pixels:\n",
1257 sub->table, col_width);
1258 } else if (DW_STYLE_IS_PER_LENGTH (child->style->width)) {
1259 percentage = DW_STYLE_PER_LENGTH_VAL (child->style->width);
1260 sub->percentage = MAX (sub->percentage, percentage);
1261 sub->use_percentage = USE_PERCENTAGE_ALL;
1262 DEBUG_MSG (DEBUG_EXTR_LEVEL,
1263 " [%p] following adjusted by %g %%\n",
1264 sub->table, 100 * sub->percentage);
1265 }
1266
1267 if (num_cols != colspan) {
1268 child_extremes.min_width =
1269 child_extremes.min_width * num_cols / colspan;
1270 child_extremes.max_width =
1271 child_extremes.max_width * num_cols / colspan;
1272 }
1273
1274 DEBUG_MSG (DEBUG_EXTR_LEVEL,
1275 " [%p], row %d: cell extremes: %d / %d "
1276 "[was multiplied by %d / %d]\n",
1277 sub->table, row, child_extremes.min_width,
1278 child_extremes.max_width,
1279 num_cols, colspan);
1280
1281 if (child_extremes.min_width > sub->span_extremes.min_width)
1282 sub->span_extremes.min_width = child_extremes.min_width;
1283 if (child_extremes.max_width > sub->span_extremes.max_width)
1284 sub->span_extremes.max_width = child_extremes.max_width;
1285 } else {
1286 DEBUG_MSG (DEBUG_EXTR_LEVEL,
1287 " [%p] row %d: omitted\n",
1288 sub->table, row);
1289 }
1290 }
1291 }
1292 }
1293
1294 DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] spanning subs: %d / %d\n",
1295 sub->table, sub->span_extremes.min_width,
1296 sub->span_extremes.max_width);
1297
1298 if (sub->table->sub == sub) {
1299 /* Adjust width argument of the table (fixed, percentages
1300 later in the code). */
1301 widget = DW_WIDGET (sub->table);
1302 if (DW_STYLE_IS_ABS_LENGTH (widget->style->width)) {
1303 col_width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width);
1304 if (col_width < sub->span_extremes.min_width)
1305 col_width = sub->span_extremes.min_width;
1306 sub->span_extremes.min_width = col_width;
1307 sub->span_extremes.max_width = col_width;
1308 }
1309 }
1310
1311 /* 2. subtables */
1312 sum_extremes.min_width = (sub->num_subs - 1) * border_spacing;
1313 sum_extremes.max_width = (sub->num_subs - 1) * border_spacing;
1314 sum_percentage = 0;
1315
1316 if (num_cols > 1)
1317 for (i = 0; i < sub->num_subs; i++) {
1318 Dw_table_sub_get_extremes (&sub->subs[i]);
1319 sum_extremes.min_width += sub->subs[i].total_extremes.min_width;
1320 sum_extremes.max_width += sub->subs[i].total_extremes.max_width;
1321 sum_percentage += sub->subs[i].percentage;
1322 if ((sub->use_percentage =
1323 use_percentage_map
1324 [sub->use_percentage][sub->subs[i].use_percentage]) == -1)
1325 sub->use_percentage =
1326 (i == 0) ? USE_PERCENTAGE_ALL : USE_PERCENTAGE_SOME;
1327 }
1328
1329 DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] sum of subsubs: %d / %d\n",
1330 sub->table, sum_extremes.min_width, sum_extremes.max_width);
1331
1332 if (sub->fixed_width) {
1333 sum_extremes.max_width = sum_extremes.min_width;
1334 sub->span_extremes.max_width = sub->span_extremes.min_width;
1335 }
1336
1337 sub->total_extremes.min_width = MAX (sum_extremes.min_width,
1338 sub->span_extremes.min_width);
1339 sub->total_extremes.max_width = MAX (sum_extremes.max_width,
1340 sub->span_extremes.max_width);
1341 sub->percentage = MAX (sub->percentage, sum_percentage);
1342
1343 DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] final: %d / %d\n",
1344 sub->table, sub->total_extremes.min_width,
1345 sub->total_extremes.max_width);
1346 }
1347
1348 /*
1349 * Corrent minima or maxima, used by Dw_table_sub_calc_col_widths.
1350 */
1351 #define EXTR_VALUE(e) (max ? (e).max_width : (e).min_width)
1352
1353 static void Dw_table_sub_adjust_col_widths (DwTableSub *sub,
1354 gboolean max,
1355 gint32 width,
1356 DwExtremes *sub_extremes,
1357 gint num_nf_subs,
1358 gint num_nf_cols,
1359 gint32 sum_nf_sub_widths)
1360 {
1361 gint i, cols_per_sub, rest_n, rest_w;
1362 gint32 sum_sub_widths, sum_orig_sub_widths, sub_extr_width, delta;
1363
1364 sum_sub_widths = 0;
1365 sum_orig_sub_widths = 0;
1366
1367 for (i = 0; i < sub->num_subs; i++) {
1368 sum_orig_sub_widths += EXTR_VALUE (sub->subs[i].total_extremes);
1369 sum_sub_widths += EXTR_VALUE (sub_extremes[i]);
1370 }
1371
1372 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1373 " Comparing %s: total: %d, sum: %d\n",
1374 (max ? "max" : "min"), width, sum_sub_widths);
1375
1376 if (num_nf_subs == -1) {
1377 /* If all subs are fixed, unfix them all. */
1378 sum_nf_sub_widths = sum_sub_widths;
1379 num_nf_cols = sub->end_col - sub->start_col;
1380 }
1381
1382 if (sum_sub_widths < width) {
1383 if (sum_nf_sub_widths == 0) {
1384 /* All non-fixed columns zero: Apportion the rest to the
1385 * non-fixed columns, according to the columns per subtable.
1386 */
1387 rest_w = width - sum_sub_widths;
1388 rest_n = num_nf_cols;
1389
1390 for (i = 0; i < sub->num_subs; i++) {
1391 if (num_nf_subs == -1 ||
1392 !(sub->subs[i].fixed_width ||
1393 sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) {
1394 cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col);
1395 sub_extr_width = rest_w * cols_per_sub / rest_n;
1396 rest_w -= sub_extr_width;
1397 rest_n -= cols_per_sub;
1398 if (max)
1399 sub_extremes[i].max_width = sub_extr_width;
1400 else
1401 sub_extremes[i].min_width = sub_extr_width;
1402 }
1403 }
1404 } else {
1405 /* Apportion the rest, according to current values. */
1406 rest_w = width - sum_sub_widths;
1407 rest_n = sum_nf_sub_widths;
1408
1409 for (i = 0; i < sub->num_subs; i++)
1410 if (num_nf_subs == -1 ||
1411 !(sub->subs[i].fixed_width ||
1412 sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) {
1413 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1414 " increasing sub %d: %d -> ",
1415 i, EXTR_VALUE (sub_extremes[i]));
1416 if (EXTR_VALUE (sub_extremes[i]) > 0) {
1417 delta = rest_w * EXTR_VALUE (sub_extremes[i]) / rest_n;
1418 rest_w -= delta;
1419 rest_n -= EXTR_VALUE (sub_extremes[i]);
1420 if (max)
1421 sub_extremes[i].max_width += delta;
1422 else
1423 sub_extremes[i].min_width += delta;
1424 }
1425 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d\n",
1426 EXTR_VALUE (sub_extremes[i]));
1427 }
1428 }
1429 }
1430 }
1431
1432
1433 /*
1434 * Calculate the column widths of a subtable, fill table->width.
1435 */
1436 static void Dw_table_sub_calc_col_widths (DwTableSub *sub,
1437 gint32 width,
1438 gint32 total_width)
1439 {
1440 enum { AT_MIN, AT_MAX, AT_NORM }; /* for sub_status */
1441 gint *sub_status;
1442 gint32 avail_width, sub_width, width_norm_cols, diff_spacing;
1443 gint32 col_width, sum_sub_min_widths, sum_orig_sub_min_widths;
1444 gint i, num_cols, cols_per_sub, num_norm_cols, num_nf_subs, num_nf_cols;
1445 gint rest_w, rest_n, delta;
1446 gboolean success;
1447 gint32 border_spacing, diff;
1448 DwExtremes extremes, *sub_extremes, sum_nf_sub_extremes;
1449
1450 num_cols = sub->end_col - sub->start_col;
1451
1452 if (num_cols == 1) {
1453 /* single column */
1454 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " single column at %d: width = %d\n",
1455 sub->start_col, width);
1456 sub->table->col_width[sub->start_col] = width;
1457 } else {
1458 /* complex subtable
1459 *
1460 * The comments "STEP <n>" refer to the documentation in
1461 * ../doc/DwTable.txt, "Calculating column widths". Read this
1462 * parallel.
1463 */
1464 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1465 " --> complex subtable from %d to %d, width = %d "
1466 "(total = %d)\n",
1467 sub->start_col, sub->end_col, width, total_width);
1468
1469 border_spacing = DW_WIDGET (sub->table)->style->border_spacing;
1470 diff = (sub->num_subs - 1) * border_spacing;
1471 avail_width = width - diff;
1472
1473 extremes = sub->total_extremes;
1474 if (width > extremes.max_width)
1475 extremes.max_width = width;
1476
1477 sub_extremes = g_new (DwExtremes, sub->num_subs);
1478 sum_sub_min_widths = 0;
1479
1480 /* ---- STEP 1: Calculate relative widths. ---- */
1481 for (i = 0; i < sub->num_subs; i++) {
1482 sub_extremes[i] = sub->subs[i].total_extremes;
1483
1484 if (sub->subs[i].use_percentage != USE_PERCENTAGE_NO) {
1485 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1486 " sub %d [%s] has width of %g%% "
1487 "-> adjusting: %d/%d -> ",
1488 i, sub->subs[i].fixed_width ? "fixed" : "variable",
1489 100 * sub->subs[i].percentage,
1490 sub_extremes[i].min_width, sub_extremes[i].max_width);
1491 col_width = sub->subs[i].percentage * total_width;
1492 if (col_width < sub_extremes[i].min_width)
1493 col_width = sub_extremes[i].min_width;
1494 sub_extremes[i].min_width = col_width;
1495 if (sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)
1496 sub_extremes[i].max_width = col_width;
1497
1498 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d/%d\n",
1499 sub_extremes[i].min_width, sub_extremes[i].max_width);
1500 } else {
1501 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1502 " sub %d [%s]: %d/%d\n", i,
1503 sub->subs[i].fixed_width ? "fixed" : "variable",
1504 sub_extremes[i].min_width, sub_extremes[i].max_width);
1505 }
1506
1507 sum_sub_min_widths += sub_extremes[i].min_width;
1508 }
1509
1510 /* ---- STEP 2: Eventually, decrease them again. ---- */
1511 if (sum_sub_min_widths > width) {
1512 sum_orig_sub_min_widths = 0;
1513 for (i = 0; i < sub->num_subs; i++)
1514 sum_orig_sub_min_widths += sub->subs[i].total_extremes.min_width;
1515
1516 rest_w = sum_sub_min_widths - (width -diff);
1517 rest_n = sum_sub_min_widths - sum_orig_sub_min_widths;
1518 for (i = 0; i < sub->num_subs; i++) {
1519 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1520 " decreasing sub %d: %d -> ",
1521 i, sub_extremes[i].min_width);
1522 if (sub_extremes[i].min_width !=
1523 sub->subs[i].total_extremes.min_width) {
1524 delta =
1525 rest_w * (sub_extremes[i].min_width
1526 - sub->subs[i].total_extremes.min_width) / rest_n;
1527 rest_w -= delta;
1528 rest_n -= (sub_extremes[i].min_width
1529 - sub->subs[i].total_extremes.min_width);
1530 } else
1531 delta = 0;
1532
1533 sub_width = sub_extremes[i].min_width - delta;
1534 Dw_table_sub_calc_col_widths (&sub->subs[i], sub_width,
1535 total_width);
1536 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d\n", sub_width);
1537 }
1538 } else {
1539 /* ---- STEP 3: If necessary, increase the sub-subtable ----
1540 * ---- extremes. ---- */
1541 num_nf_subs = 0;
1542 num_nf_cols = 0;
1543 sum_nf_sub_extremes.min_width = 0;
1544 sum_nf_sub_extremes.max_width = 0;
1545
1546 for (i = 0; i < sub->num_subs; i++)
1547 if (!(sub->subs[i].fixed_width ||
1548 sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) {
1549 num_nf_cols += (sub->subs[i].end_col - sub->subs[i].start_col);
1550 num_nf_subs++;
1551 sum_nf_sub_extremes.min_width += sub_extremes[i].min_width;
1552 sum_nf_sub_extremes.max_width += sub_extremes[i].max_width;
1553 }
1554
1555 /* If all subtables are fixed, unfix them all. */
1556 if (num_nf_subs == 0) {
1557 num_nf_subs = -1;
1558 num_nf_cols = num_cols;
1559 }
1560
1561 diff_spacing =
1562 (sub->end_col - sub->start_col - 1)
1563 * DW_WIDGET(sub->table)->style->border_spacing;
1564
1565 Dw_table_sub_adjust_col_widths
1566 (sub, FALSE, extremes.min_width - diff_spacing, sub_extremes,
1567 num_nf_subs, num_nf_cols, sum_nf_sub_extremes.min_width);
1568 Dw_table_sub_adjust_col_widths
1569 (sub, TRUE, extremes.max_width - diff_spacing, sub_extremes,
1570 num_nf_subs, num_nf_cols, sum_nf_sub_extremes.max_width);
1571
1572 /* ---- STEP 4: Finally, calculate the widths. ---- */
1573 sub_status = g_new (gint, sub->num_subs);
1574
1575 /* First, assume that all columns have the same width. */
1576 for (i = 0; i < sub->num_subs; i++)
1577 sub_status[i] = AT_NORM;
1578
1579 do {
1580 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, " columns: ");
1581
1582 /* Calculate the normal width, and the number of columns at this
1583 width. */
1584 width_norm_cols = avail_width;
1585 num_norm_cols = num_cols;
1586 for (i = 0; i < sub->num_subs; i++) {
1587 if (sub_status[i] != AT_NORM) {
1588 num_norm_cols -=
1589 (sub->subs[i].end_col - sub->subs[i].start_col);
1590 if (sub_status[i] == AT_MIN)
1591 width_norm_cols -= sub_extremes[i].min_width;
1592 else
1593 width_norm_cols -= sub_extremes[i].max_width;
1594 }
1595
1596 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "%c", "ian"[sub_status[i]]);
1597 }
1598
1599 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "\n");
1600 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " norm: %d, width = %d\n",
1601 num_norm_cols, width_norm_cols);
1602
1603 /*
1604 * Iteratively, test for minimum/maximum, and correct the
1605 * status. As soon as one test fails, the status is
1606 * changed, and the iteration starts again from the
1607 * beginning.
1608 */
1609 success = TRUE;
1610 for (i = 0; success && i < sub->num_subs; i++) {
1611 cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col);
1612
1613 switch (sub_status[i]) {
1614 case AT_NORM:
1615 /* Columns at normal width must between min and max. */
1616 if (width_norm_cols * cols_per_sub
1617 < num_norm_cols * sub_extremes[i].min_width) {
1618 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1619 " sub %d at min\n", i);
1620 sub_status[i] = AT_MIN;
1621 success = FALSE;
1622 } else if (width_norm_cols * cols_per_sub
1623 > num_norm_cols * sub_extremes[i].max_width) {
1624 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1625 " sub %d at max\n", i);
1626 sub_status[i] = AT_MAX;
1627 success = FALSE;
1628 }
1629 break;
1630
1631 case AT_MIN:
1632 /* If the column is at min, the the normal width (which has
1633 been changed), must tested against the min. */
1634 if (width_norm_cols * cols_per_sub
1635 > num_norm_cols * sub_extremes[i].min_width) {
1636 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1637 " sub %d at norm\n", i);
1638 sub_status[i] = AT_NORM;
1639 success = FALSE;
1640 }
1641 break;
1642
1643 case AT_MAX:
1644 /* If the column is at max, the the normal width (which has
1645 been changed), must tested against the max. */
1646 if (width_norm_cols * cols_per_sub
1647 < num_norm_cols * sub_extremes[i].max_width) {
1648 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1649 " sub %d at norm\n", i);
1650 sub_status[i] = AT_NORM;
1651 success = FALSE;
1652 }
1653 break;
1654 }
1655 }
1656 } while (!success);
1657
1658 /* ---- STEP 5: Apply calculate the widths. ---- */
1659 rest_n= num_norm_cols;
1660 rest_w = width_norm_cols;
1661 for (i = 0; i < sub->num_subs; i++) {
1662 if (sub_status[i] == AT_MIN)
1663 sub_width = sub_extremes[i].min_width;
1664 else if (sub_status[i] == AT_MAX)
1665 sub_width = sub_extremes[i].max_width;
1666 else {
1667 cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col);
1668 sub_width = rest_w * cols_per_sub / rest_n;
1669 rest_w -= sub_width;
1670 rest_n-= cols_per_sub;
1671 }
1672
1673 Dw_table_sub_calc_col_widths (&sub->subs[i], sub_width,
1674 total_width);
1675 }
1676
1677 g_free (sub_status);
1678 }
1679
1680 g_free (sub_extremes);
1681 }
1682 }
1683
1684
1685 static DwIterator *Dw_table_iterator (DwWidget *widget,
1686 gint mask,
1687 gboolean at_end)
1688 {
1689 DwIteratorInt *it = g_new (DwIteratorInt, 1);
1690 it->it.widget = widget;
1691 it->it.mask = mask;
1692 it->it.next = Dw_table_iterator_next;
1693 it->it.prev = Dw_table_iterator_prev;
1694 it->it.clone = p_Dw_iterator_clone_std_int;
1695 it->it.compare = p_Dw_iterator_compare_std_int;
1696 it->it.free = p_Dw_iterator_free_std;
1697 it->it.highlight = p_Dw_iterator_highlight_std;
1698 it->it.get_allocation = p_Dw_iterator_get_allocation_std_only_widgets;
1699
1700 if (at_end) {
1701 it->it.content.type = DW_CONTENT_END;
1702 it->pos = DW_TABLE(widget)->num_children;
1703 } else {
1704 it->it.content.type = DW_CONTENT_START;
1705 it->pos = -1;
1706 }
1707
1708 return (DwIterator*)it;
1709 }
1710
1711 static gboolean Dw_table_iterator_next (DwIterator *it)
1712 {
1713 DwTable *table = DW_TABLE (it->widget);
1714 DwIteratorInt *ii = (DwIteratorInt*)it;
1715
1716 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_table_iterator_next: %d of %d\n",
1717 ii->pos, table->num_children);
1718
1719 if (it->content.type == DW_CONTENT_END)
1720 return FALSE;
1721
1722 /* tables only contain widgets: */
1723 if ((it->mask & DW_CONTENT_WIDGET) == 0) {
1724 it->content.type = DW_CONTENT_END;
1725 return FALSE;
1726 }
1727
1728 do {
1729 ii->pos++;
1730 if (ii->pos >= table->num_children) {
1731 it->content.type = DW_CONTENT_END;
1732 return FALSE;
1733 }
1734 } while (table->children[ii->pos] == NULL ||
1735 table->children[ii->pos]->type != DW_TABLE_CELL);
1736
1737 it->content.type = DW_CONTENT_WIDGET;
1738 it->content.data.widget = table->children[ii->pos]->data.cell.widget;
1739 return TRUE;
1740 }
1741
1742 static gboolean Dw_table_iterator_prev (DwIterator *it)
1743 {
1744 DwTable *table = DW_TABLE (it->widget);
1745 DwIteratorInt *ii = (DwIteratorInt*)it;
1746
1747 if (it->content.type == DW_CONTENT_START)
1748 return FALSE;
1749
1750 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_table_iterator_prev: %d of %d\n",
1751 ii->pos, table->num_children);
1752
1753 /* tables only contain widgets: */
1754 if ((it->mask & DW_CONTENT_WIDGET) == 0) {
1755 it->content.type = DW_CONTENT_START;
1756 return FALSE;
1757 }
1758
1759 do {
1760 ii->pos--;
1761 if (ii->pos < 0) {
1762 it->content.type = DW_CONTENT_START;
1763 return FALSE;
1764 }
1765 } while (table->children[ii->pos] == NULL ||
1766 table->children[ii->pos]->type != DW_TABLE_CELL);
1767
1768 it->content.type = DW_CONTENT_WIDGET;
1769 it->content.data.widget = table->children[ii->pos]->data.cell.widget;
1770 return TRUE;
1771 }
0 /*
1 * File: dw_table.h
2 *
3 * Copyright (C) 2001 Sebastian Geerken <sgeerken@users.sourceforge.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #ifndef __DW_TABLE_H__
12 #define __DW_TABLE_H__
13
14 #include "dw_container.h"
15 #include "dw_table_cell.h"
16 #include "bitvec.h"
17
18 #ifdef __cplusplus
19 extern "C" {
20 #endif /* __cplusplus */
21
22 #define DW_TYPE_TABLE (a_Dw_table_get_type ())
23 #define DW_TABLE(obj) GTK_CHECK_CAST (obj,DW_TYPE_TABLE, DwTable)
24 #define DW_TABLE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_TABLE, \
25 DwTableClass)
26 #define DW_IS_TABLE(obj) GTK_CHECK_TYPE (obj, DW_TYPE_TABLE)
27
28
29 typedef struct _DwTable DwTable;
30 typedef struct _DwTableClass DwTableClass;
31 typedef struct _DwTableChild DwTableChild;
32 typedef struct _DwTableSub DwTableSub;
33
34 struct _DwTableChild
35 {
36 enum {
37 DW_TABLE_CELL, /* cell starts here */
38 DW_TABLE_SPAN_SPACE /* part of a spanning cell */
39 } type;
40
41 union {
42 struct {
43 DwWidget *widget;
44 gint colspan, rowspan;
45 } cell;
46 struct {
47 gint start_col, start_row; /* where the cell starts */
48 } span_space;
49 } data;
50 };
51
52 struct _DwTableSub
53 {
54 DwTable *table;
55 gint start_col, end_col;
56 bitvec_t *spanning_rows, *removed_rows;
57 DwExtremes span_extremes, total_extremes;
58 gboolean fixed_width;
59 gint use_percentage;
60 gfloat percentage;
61
62 DwTableSub *subs;
63 gint num_subs;
64 };
65
66 struct _DwTable
67 {
68 DwContainer container;
69
70 /* set by set_... */
71 gint32 avail_width, avail_ascent, avail_descent;
72
73 gint cur_row, cur_col;
74
75 gint32 *col_width; /* num_cols members */
76 gint num_cols;
77 gint num_col_width_max;
78
79 gint32 *cum_height; /* row cumulative height array: */
80 gint num_rows; /* num_cum_height is (num_rows + 1), */
81 gint cum_height_max; /* cum_height[0] is always SPACING, */
82 /* cum_height[num_rows] is the total table height */
83
84 DwStyle **row_style;
85 gint row_style_max;
86
87 gint32 *baseline;
88 gint baseline_max;
89
90 DwTableSub *sub;
91
92 DwTableChild **children;
93 gint num_children;
94 gint num_children_max; /* number allocated */
95 };
96
97 struct _DwTableClass
98 {
99 DwContainerClass parent_class;
100 };
101
102 GtkType a_Dw_table_get_type (void);
103 DwWidget* a_Dw_table_new (void);
104
105 void a_Dw_table_add_cell (DwTable *table,
106 DwWidget *widget,
107 gint colspan,
108 gint rowspan);
109 void a_Dw_table_add_row (DwTable *table,
110 DwStyle *style);
111 DwTableCell* a_Dw_table_get_cell_ref (DwTable *table);
112
113 #ifdef __cplusplus
114 }
115 #endif /* __cplusplus */
116
117 #endif /* __DW_TABLE_H__ */
0 /*
1 * File: dw_table_cell.c
2 *
3 * Copyright (C) 2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This widget is used for table cells with character alignment.
13 *
14 * Some notes: If the character is at a position quite far within the
15 * text, this would result in very wide table columns. Since I do not
16 * think that an author intends this, DwTableCell makes use of the
17 * DwPage::ignore_line1_offset_sometimes feature, which will in this
18 * case disable alignment (see comment in dw_page.h). Actually for the
19 * whole column, which is simpler to implement (DwTableCell still deals
20 * with too large values, but DwPage simply ignores them).
21 */
22
23 #include "dw_table_cell.h"
24
25 /*#define DEBUG_LEVEL 1*/
26 #include "debug.h"
27
28 static void Dw_table_cell_class_init (DwTableCellClass *klass);
29 static void Dw_table_cell_init (DwTableCell *table_cell);
30
31 static void Dw_table_cell_word_wrap (DwPage *page,
32 gint word_ind);
33 static gint32 Dw_table_cell_get_value (DwAlignedPage *aligned_page);
34 static void Dw_table_cell_set_max_value (DwAlignedPage *aligned_page,
35 gint32 max_value,
36 gint32 value);
37 static DwAlignedPageClass *parent_class;
38
39
40 /*
41 * Standard Gtk+ function.
42 */
43 GtkType a_Dw_table_cell_get_type (void)
44 {
45 static GtkType type = 0;
46
47 if (!type) {
48 GtkTypeInfo info = {
49 "DwTableCell",
50 sizeof (DwTableCell),
51 sizeof (DwTableCellClass),
52 (GtkClassInitFunc) Dw_table_cell_class_init,
53 (GtkObjectInitFunc) Dw_table_cell_init,
54 (GtkArgSetFunc) NULL,
55 (GtkArgGetFunc) NULL,
56 (GtkClassInitFunc) NULL
57 };
58
59 type = gtk_type_unique (DW_TYPE_ALIGNED_PAGE, &info);
60 }
61
62 return type;
63 }
64
65
66 /*
67 * Standard Gtk+ function.
68 */
69 static void Dw_table_cell_init (DwTableCell *table_cell)
70 {
71 DW_PAGE(table_cell)->ignore_line1_offset_sometimes = TRUE;
72 table_cell->char_word_index = -1;
73 }
74
75
76 /*
77 * Standard Gtk+ function: Create a new table_cell item.
78 * ref_cell is either another table cell in the same column, or NULL for
79 * the first DwTableCell in the column. Typically used with
80 * a_Dw_table_get_cell_ref().
81 */
82 DwWidget* a_Dw_table_cell_new (DwTableCell *ref_cell)
83 {
84 GtkObject *object;
85
86 object = gtk_object_new (DW_TYPE_TABLE_CELL, NULL);
87 DBG_OBJ_CREATE (object, "DwTableCell");
88 p_Dw_aligned_page_set_ref_page (DW_ALIGNED_PAGE (object),
89 (DwAlignedPage*)ref_cell);
90 return DW_WIDGET (object);
91 }
92
93
94 /*
95 * Standard Gtk+ function.
96 */
97 static void Dw_table_cell_class_init (DwTableCellClass *klass)
98 {
99 DwAlignedPageClass *aligned_page_class = DW_ALIGNED_PAGE_CLASS (klass);
100
101 DW_PAGE_CLASS(klass)->word_wrap = Dw_table_cell_word_wrap;
102 aligned_page_class->get_value = Dw_table_cell_get_value;
103 aligned_page_class->set_max_value = Dw_table_cell_set_max_value;
104
105 parent_class = gtk_type_class (DW_TYPE_ALIGNED_PAGE);
106 }
107
108 /*
109 * Standard DwPage function.
110 * Extended to use for the first word containing the alignnment character.
111 */
112 static void Dw_table_cell_word_wrap (DwPage *page,
113 gint word_ind)
114 {
115 DwTableCell *cell = DW_TABLE_CELL (page);
116 DwPageWord *word;
117 char *p;
118
119 DW_PAGE_CLASS(parent_class)->word_wrap (page, word_ind);
120
121 if (cell->char_word_index == -1) {
122 word = &page->words[word_ind];
123 if (word->content.type == DW_CONTENT_TEXT) {
124 if ((p = strchr (word->content.data.text,
125 word->style->text_align_char))) {
126 cell->char_word_index = word_ind;
127 cell->char_word_pos = p - word->content.data.text + 1;
128 } else if (word->style->text_align_char == ' ' &&
129 word->content.space) {
130 cell->char_word_index = word_ind + 1;
131 cell->char_word_pos = 0;
132 }
133 }
134 }
135
136 if (word_ind == cell->char_word_index)
137 p_Dw_aligned_page_update_value (DW_ALIGNED_PAGE (cell));
138 }
139
140
141 /*
142 * Implementation of DwAlignedPage::get_value.
143 */
144 static gint32 Dw_table_cell_get_value (DwAlignedPage *aligned_page)
145 {
146 DwTableCell *cell = DW_TABLE_CELL (aligned_page);
147 DwPage *page = DW_PAGE (aligned_page);
148 DwPageWord *word;
149 int i, word_index;
150 gint32 w;
151
152 if (cell->char_word_index == -1)
153 word_index = page->num_words -1;
154 else
155 word_index = cell->char_word_index;
156
157 w = 0;
158 for (i = 0; i < word_index; i++) {
159 word = &page->words[i];
160 w += word->size.width + word->orig_space;
161 }
162
163 if (cell->char_word_index == -1) {
164 if (page->num_words > 0) {
165 word = &page->words[page->num_words - 1];
166 w += word->size.width;
167 }
168 } else {
169 word = &page->words[cell->char_word_index];
170 w += gdk_text_width (word->style->font->font, word->content.data.text,
171 cell->char_word_pos);
172 }
173
174 return w;
175 }
176
177
178 /*
179 * Implementation of DwAlignedPage::set_max_value.
180 */
181 static void Dw_table_cell_set_max_value (DwAlignedPage *aligned_page,
182 gint32 max_value,
183 gint32 value)
184 {
185 DEBUG_MSG (1, "Dw_table_cell_set_max_value([\"%s\" ...], %d, %d)\n",
186 (DW_PAGE(aligned_page)->num_words > 0 ?
187 (DW_PAGE(aligned_page)->words[0].content.type
188 == DW_CONTENT_TEXT ?
189 DW_PAGE(aligned_page)->words[0].content.data.text : "(?)")
190 : "(empty)"),
191 max_value, value);
192
193 DW_PAGE(aligned_page)->line1_offset = max_value - value;
194 p_Dw_widget_queue_resize (DW_WIDGET (aligned_page), 0, TRUE);
195 }
0 #ifndef __DW_TABLE_CELL_H__
1 #define __DW_TABLE_CELL_H__
2
3 #include "dw_aligned_page.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_TABLE_CELL (a_Dw_table_cell_get_type ())
10 #define DW_TABLE_CELL(obj) GTK_CHECK_CAST (obj,DW_TYPE_TABLE_CELL, \
11 DwTableCell)
12 #define DW_TABLE_CELL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_TABLE_CELL, DwTableCellClass)
14 #define DW_IS_TABLE_CELL(obj) GTK_CHECK_TYPE (obj, DW_TYPE_TABLE_CELL)
15
16
17 typedef struct _DwTableCell DwTableCell;
18 typedef struct _DwTableCellClass DwTableCellClass;
19
20 struct _DwTableCell
21 {
22 DwAlignedPage aligned_page;
23 gint char_word_index, char_word_pos;
24 };
25
26 struct _DwTableCellClass
27 {
28 DwAlignedPageClass parent_class;
29 };
30
31
32 GtkType a_Dw_table_cell_get_type (void);
33 DwWidget* a_Dw_table_cell_new (DwTableCell *ref_cell);
34
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
39 #endif /* __DW_TABLE_CELL_H__ */
0 /*
1 * A few notes:
2 *
3 * - Currently, a window is created every time before it is shown, and
4 * destroyed, before it is hidden. This saves (probably?) some
5 * memory, but can simply be changed. An alternative is having a
6 * global window for all tooltips.
7 *
8 * - Tooltips are positioned near the pointer, as opposed to Gtk+
9 * tooltips, which are positioned near the widget.
10 *
11 * Sebastian
12 */
13
14 #include <gtk/gtk.h>
15 #include "dw_tooltip.h"
16
17 /* The amount of space around the text, including the border. */
18 #define PADDING 4
19
20 /* The difference between pointer position and upper left corner of the
21 * tooltip. */
22 #define DIFF 10
23
24 static gboolean Dw_tooltip_draw (DwTooltip *tooltip);
25
26 static DwTooltip *Dw_tooltip_new0 (const char *text, gint ref_count)
27 {
28 DwTooltip *tooltip;
29
30 tooltip = g_new (DwTooltip, 1);
31 tooltip->ref_count = ref_count;
32 tooltip->window = NULL;
33 tooltip->timeout_id = 0;
34 tooltip->text = g_strdup (text);
35 return tooltip;
36 }
37
38 /*
39 * Create a new tooltip, with ref_count set to 1.
40 */
41 DwTooltip* a_Dw_tooltip_new (const gchar *text)
42 {
43 return Dw_tooltip_new0(text, 1);
44 }
45
46 /*
47 * Create a new tooltip, with ref_count set to 0. Tyically used for DwStyle.
48 */
49 DwTooltip* a_Dw_tooltip_new_no_ref (const gchar *text)
50 {
51 return Dw_tooltip_new0(text, 0);
52 }
53
54
55 /*
56 * Destroy the tooltip. Used by Dw_tooltip_unref.
57 */
58 void Dw_tooltip_destroy (DwTooltip *tooltip)
59 {
60 a_Dw_tooltip_on_leave (tooltip);
61 g_free (tooltip->text);
62 g_free (tooltip);
63 }
64
65
66 /*
67 * Call this function if the pointer has entered the widget/word.
68 */
69 void a_Dw_tooltip_on_enter (DwTooltip *tooltip)
70 {
71 a_Dw_tooltip_on_leave (tooltip);
72 tooltip->timeout_id = gtk_timeout_add (500, (GtkFunction)Dw_tooltip_draw,
73 tooltip);
74 }
75
76
77 /*
78 * Call this function if the pointer has left the widget/word.
79 */
80 void a_Dw_tooltip_on_leave (DwTooltip *tooltip)
81 {
82 if (tooltip->timeout_id != 0) {
83 gtk_timeout_remove (tooltip->timeout_id);
84 tooltip->timeout_id = 0;
85 }
86
87 if (tooltip->window != NULL) {
88 gtk_widget_destroy (tooltip->window);
89 tooltip->window = NULL;
90 }
91 }
92
93
94 /*
95 * Call this function if the pointer has moved within the widget/word.
96 */
97 void a_Dw_tooltip_on_motion (DwTooltip *tooltip)
98 {
99 a_Dw_tooltip_on_enter (tooltip);
100 }
101
102 /*
103 * Draw the tooltip. Called as a timeout function.
104 */
105 static gboolean Dw_tooltip_draw (DwTooltip *tooltip)
106 {
107 GtkStyle *style;
108 gint px, py, x, y, width, ascent, descent, screen_w, screen_h, ttw, tth;
109
110 gdk_window_get_pointer (NULL, &px, &py, NULL);
111 x = px + DIFF;
112 y = py + DIFF;
113
114 tooltip->window = gtk_window_new (GTK_WINDOW_POPUP);
115 gtk_widget_set_app_paintable (tooltip->window, TRUE);
116 gtk_widget_set_name (tooltip->window, "gtk-tooltips");
117 gtk_widget_ensure_style (tooltip->window);
118 style = tooltip->window->style;
119 width = gdk_string_width (style->font, tooltip->text);
120 ascent = style->font->ascent;
121 descent = style->font->descent;
122
123 ttw = width + 2 * PADDING;
124 tth = ascent + descent + 2 * PADDING;
125 gtk_widget_set_usize (tooltip->window, ttw, tth);
126
127 screen_w = gdk_screen_width();
128 screen_h = gdk_screen_height();
129
130 if (ttw >= screen_w)
131 /* If the width of a tooltips does not fit into the screen, put
132 * them at x = 0. (Yes, that's far from perfect ...) */
133 x = 0;
134 else if (x + ttw > screen_w)
135 /* If they would otherwise reach out of the screen, move them
136 * a bit left. */
137 x = screen_w - ttw;
138
139 /* The case that the height of a tooltip of the screen is greater
140 * that the screen height is ignored ;-) If the tooltip reaches
141 * out of the screen at the bottom, it is displayed *above* the
142 * pointer: to process events properly, it is necessary to keep
143 * the pointer out of the tooltip. */
144 if (y + tth > screen_h)
145 y = py - tth - DIFF;
146
147 gtk_widget_popup (tooltip->window, x, y);
148 style = tooltip->window->style;
149 gtk_paint_flat_box (style, tooltip->window->window,
150 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
151 NULL, GTK_WIDGET (tooltip->window), "tooltip",
152 0, 0, -1, -1);
153 gtk_paint_string (style, tooltip->window->window,
154 GTK_STATE_NORMAL,
155 NULL, GTK_WIDGET (tooltip->window), "tooltip",
156 PADDING, ascent + PADDING,
157 tooltip->text);
158
159 tooltip->timeout_id = 0;
160 return FALSE;
161 }
162
163
164
0 #ifndef __DW_TOOLTIP_H__
1 #define __DW_TOOLTIP_H__
2
3 #include <gtk/gtkwidget.h>
4
5 typedef struct _DwTooltip DwTooltip;
6
7 struct _DwTooltip
8 {
9 gint ref_count;
10 GtkWidget *window;
11 gchar *text;
12 guint timeout_id;
13 };
14
15 DwTooltip* a_Dw_tooltip_new (const gchar *text);
16 DwTooltip* a_Dw_tooltip_new_no_ref (const gchar *text);
17
18 void a_Dw_tooltip_on_enter (DwTooltip *tooltip);
19 void a_Dw_tooltip_on_leave (DwTooltip *tooltip);
20 void a_Dw_tooltip_on_motion (DwTooltip *tooltip);
21
22 #define a_Dw_tooltip_ref(tooltip) ((tooltip)->ref_count++)
23 #define a_Dw_tooltip_unref(tooltip) if (--((tooltip)->ref_count) == 0) \
24 Dw_tooltip_destroy (tooltip)
25
26 /* Don't use this function directly! */
27 void Dw_tooltip_destroy (DwTooltip *tooltip);
28
29 #endif /* __DW_TOOLTIP_H__ */
0 /*
1 * File: dw_widget.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <gtk/gtk.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include "msg.h"
15 #include "dw_widget.h"
16 #include "dw_marshal.h"
17 #include "dw_container.h"
18 #include "dw_gtk_viewport.h"
19
20 #define DEBUG_EVENT 10
21 #define DEBUG_SIZE 0
22 #define DEBUG_ALLOC 0
23
24 /*#define DEBUG_LEVEL 10*/
25 #include "debug.h"
26
27 static void Dw_widget_init (DwWidget *widget);
28 static void Dw_widget_class_init (DwWidgetClass *klass);
29
30 static void Dw_widget_destroy (GtkObject *object);
31
32 static void Dw_widget_real_size_request (DwWidget *widget,
33 DwRequisition *requisition);
34 static void Dw_widget_real_get_extremes (DwWidget *widget,
35 DwExtremes *extremes);
36
37 enum
38 {
39 BUTTON_PRESS_EVENT,
40 BUTTON_RELEASE_EVENT,
41 MOTION_NOTIFY_EVENT,
42 ENTER_NOTIFY_EVENT,
43 LEAVE_NOTIFY_EVENT,
44 LAST_SIGNAL
45 };
46
47
48 static GtkObjectClass *parent_class;
49 static guint widget_signals[LAST_SIGNAL] = { 0 };
50
51 /*
52 * Standard Gtk+ function
53 */
54 GtkType a_Dw_widget_get_type (void)
55 {
56 static GtkType type = 0;
57
58 if (!type) {
59 GtkTypeInfo info = {
60 "DwWidget",
61 sizeof (DwWidget),
62 sizeof (DwWidgetClass),
63 (GtkClassInitFunc) Dw_widget_class_init,
64 (GtkObjectInitFunc) Dw_widget_init,
65 (GtkArgSetFunc) NULL,
66 (GtkArgGetFunc) NULL,
67 (GtkClassInitFunc) NULL
68 };
69
70 type = gtk_type_unique (GTK_TYPE_OBJECT, &info);
71 }
72
73 return type;
74 }
75
76
77 /*
78 * Standard Gtk+ function
79 */
80 static void Dw_widget_init (DwWidget *widget)
81 {
82 widget->flags = DW_NEEDS_RESIZE | DW_EXTREMES_CHANGED | DW_HAS_CONTENT;
83 widget->parent = NULL;
84 widget->viewport = NULL;
85
86 widget->allocation.x = -1;
87 widget->allocation.y = -1;
88 widget->allocation.width = 1;
89 widget->allocation.ascent = 1;
90 widget->allocation.descent = 0;
91
92 widget->cursor = NULL;
93 widget->style = NULL;
94 widget->bg_color = NULL;
95 widget->button_sensitive = TRUE;
96 widget->button_sensitive_set = FALSE;
97 }
98
99
100 /*
101 * Standard Gtk+ function
102 */
103 static void Dw_widget_class_init (DwWidgetClass *klass)
104 {
105 GtkObjectClass *object_class;
106
107 parent_class = gtk_type_class (gtk_object_get_type ());
108
109 object_class = GTK_OBJECT_CLASS (klass);
110
111 widget_signals[BUTTON_PRESS_EVENT] =
112 gtk_signal_new ("button_press_event",
113 GTK_RUN_LAST,
114 object_class->type,
115 GTK_SIGNAL_OFFSET (DwWidgetClass, button_press_event),
116 p_Dw_marshal_BOOL__INT_INT_POINTER,
117 GTK_TYPE_BOOL,
118 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
119 widget_signals[BUTTON_RELEASE_EVENT] =
120 gtk_signal_new ("button_release_event",
121 GTK_RUN_LAST,
122 object_class->type,
123 GTK_SIGNAL_OFFSET (DwWidgetClass, button_release_event),
124 p_Dw_marshal_BOOL__INT_INT_POINTER,
125 GTK_TYPE_BOOL,
126 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
127 widget_signals[MOTION_NOTIFY_EVENT] =
128 gtk_signal_new ("motion_notify_event",
129 GTK_RUN_LAST,
130 object_class->type,
131 GTK_SIGNAL_OFFSET (DwWidgetClass, motion_notify_event),
132 p_Dw_marshal_BOOL__INT_INT_POINTER,
133 GTK_TYPE_BOOL,
134 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
135 widget_signals[ENTER_NOTIFY_EVENT] =
136 gtk_signal_new ("enter_notify_event",
137 GTK_RUN_LAST,
138 object_class->type,
139 GTK_SIGNAL_OFFSET (DwWidgetClass, enter_notify_event),
140 p_Dw_marshal_BOOL__POINTER_POINTER,
141 GTK_TYPE_BOOL,
142 2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
143 widget_signals[LEAVE_NOTIFY_EVENT] =
144 gtk_signal_new ("leave_notify_event",
145 GTK_RUN_LAST,
146 object_class->type,
147 GTK_SIGNAL_OFFSET (DwWidgetClass, leave_notify_event),
148 p_Dw_marshal_BOOL__POINTER_POINTER,
149 GTK_TYPE_BOOL,
150 2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
151 gtk_object_class_add_signals (object_class, widget_signals, LAST_SIGNAL);
152
153 object_class->destroy = Dw_widget_destroy;
154
155 klass->size_request = Dw_widget_real_size_request;
156 klass->get_extremes = Dw_widget_real_get_extremes;
157 klass->size_allocate = NULL;
158 klass->mark_size_change = NULL;
159 klass->mark_extremes_change = NULL;
160 klass->set_width = NULL;
161 klass->set_ascent = NULL;
162 klass->set_descent = NULL;
163 klass->draw = NULL;
164 klass->button_press_event = NULL;
165 klass->button_release_event = NULL;
166 klass->motion_notify_event = NULL;
167 klass->enter_notify_event = NULL;
168 klass->leave_notify_event = NULL;
169 klass->iterator = NULL;
170 }
171
172
173 /*
174 * Standard Gtk+ function
175 */
176 static void Dw_widget_destroy (GtkObject *object)
177 {
178 DwWidget *widget;
179
180 widget = DW_WIDGET (object);
181
182 /* The widget the pointer is in? */
183 if (widget->viewport != NULL &&
184 widget == GTK_DW_VIEWPORT(widget->viewport)->last_entered)
185 /* todo: perhaps call the leave_notify function? */
186 GTK_DW_VIEWPORT(widget->viewport)->last_entered = NULL;
187
188 if (widget->style)
189 a_Dw_style_unref (widget->style);
190
191 if (widget->parent)
192 Dw_container_remove (DW_CONTAINER (widget->parent), widget);
193 else
194 Dw_gtk_viewport_remove_dw (GTK_DW_VIEWPORT (widget->viewport));
195
196 parent_class->destroy (object);
197 }
198
199 /*
200 * Standard Dw function
201 */
202 static void Dw_widget_real_size_request (DwWidget *widget,
203 DwRequisition *requisition)
204 {
205 g_warning ("DwWidget::size_request not implemented for `%s'",
206 gtk_type_name (GTK_OBJECT_TYPE (widget)));
207
208 /* return random size to prevent crashes*/
209 requisition->width = 50;
210 requisition->ascent = 50;
211 requisition->descent = 50;
212 }
213
214
215 /*
216 * Standard Dw function
217 */
218 static void Dw_widget_real_get_extremes (DwWidget *widget,
219 DwExtremes *extremes)
220 {
221 /* Simply return the requisition width */
222 DwRequisition requisition;
223
224 p_Dw_widget_size_request (widget, &requisition);
225 extremes->min_width = extremes->max_width = requisition.width;
226 }
227
228
229 /*
230 * This function is a wrapper for DwWidget::size_request; it calls
231 * this method only when needed.
232 */
233 void p_Dw_widget_size_request (DwWidget *widget,
234 DwRequisition *requisition)
235 {
236 DwWidgetClass *klass;
237
238 if (DW_WIDGET_NEEDS_RESIZE (widget)) {
239 /* todo: check requisition == &(widget->requisition) and do what? */
240 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
241 (* (klass->size_request)) (widget, requisition);
242 widget->requisition = *requisition;
243 DW_WIDGET_UNSET_FLAGS (widget, DW_NEEDS_RESIZE);
244
245 DBG_OBJ_SET_NUM (widget, "requisition.width", widget->requisition.width);
246 DBG_OBJ_SET_NUM (widget, "requisition.ascent",
247 widget->requisition.ascent);
248 DBG_OBJ_SET_NUM (widget, "requisition.descent",
249 widget->requisition.descent);
250 } else
251 *requisition = widget->requisition;
252 }
253
254 /*
255 * Wrapper for DwWidget::get_extremes.
256 */
257 void p_Dw_widget_get_extremes (DwWidget *widget,
258 DwExtremes *extremes)
259 {
260 DwWidgetClass *klass;
261
262 if (DW_WIDGET_EXTREMES_CHANGED (widget)) {
263 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
264 (* (klass->get_extremes)) (widget, extremes);
265 widget->extremes = *extremes;
266 DW_WIDGET_UNSET_FLAGS (widget, DW_EXTREMES_CHANGED);
267
268 DBG_OBJ_SET_NUM (widget, "extremes.min_width",
269 widget->extremes.min_width);
270 DBG_OBJ_SET_NUM (widget, "extremes.max_width",
271 widget->extremes.max_width);
272 } else
273 *extremes = widget->extremes;
274 }
275
276
277 /*
278 * Wrapper for DwWidget::size_allocate, only called when needed.
279 */
280 void p_Dw_widget_size_allocate (DwWidget *widget,
281 DwAllocation *allocation)
282 {
283 DwWidgetClass *klass;
284
285 if (DW_WIDGET_NEEDS_ALLOCATE (widget) ||
286 allocation->x != widget->allocation.x ||
287 allocation->y != widget->allocation.y ||
288 allocation->width != widget->allocation.width ||
289 allocation->ascent != widget->allocation.ascent ||
290 allocation->descent != widget->allocation.descent) {
291
292 DEBUG_MSG (DEBUG_ALLOC,
293 "a %stop-level %s with parent_ref = %d is newly allocated "
294 "from %d, %d, %d x %d x %d ...\n",
295 widget->parent ? "non-" : "",
296 gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref,
297 widget->allocation.x, widget->allocation.y,
298 widget->allocation.width, widget->allocation.ascent,
299 widget->allocation.descent);
300
301 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
302 if (klass->size_allocate)
303 (* (klass->size_allocate)) (widget, allocation);
304
305 DEBUG_MSG (DEBUG_ALLOC, "... to %d, %d, %d x %d x %d\n",
306 widget->allocation.x, widget->allocation.y,
307 widget->allocation.width, widget->allocation.ascent,
308 widget->allocation.descent);
309
310 widget->allocation = *allocation;
311 DW_WIDGET_UNSET_FLAGS (widget, DW_NEEDS_ALLOCATE);
312
313 DBG_OBJ_SET_NUM (widget, "allocation.x", widget->allocation.x);
314 DBG_OBJ_SET_NUM (widget, "allocation.y", widget->allocation.y);
315 DBG_OBJ_SET_NUM (widget, "allocation.width", widget->allocation.width);
316 DBG_OBJ_SET_NUM (widget, "allocation.ascent", widget->allocation.ascent);
317 DBG_OBJ_SET_NUM (widget, "allocation.descent",
318 widget->allocation.descent);
319
320 }
321
322 /*DW_WIDGET_UNSET_FLAGS (widget, DW_NEEDS_RESIZE);*/
323 }
324
325
326 void p_Dw_widget_set_width (DwWidget *widget,
327 gint32 width)
328 {
329 DwWidgetClass *klass;
330 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
331 if (klass->set_width)
332 (* (klass->set_width)) (widget, width);
333 }
334
335
336 void p_Dw_widget_set_ascent (DwWidget *widget,
337 gint32 ascent)
338 {
339 DwWidgetClass *klass;
340 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
341 if (klass->set_ascent)
342 (* (klass->set_ascent)) (widget, ascent);
343 }
344
345
346 void p_Dw_widget_set_descent (DwWidget *widget,
347 gint32 descent)
348 {
349 DwWidgetClass *klass;
350 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
351 if (klass->set_descent)
352 (* (klass->set_descent)) (widget, descent);
353 }
354
355
356 void p_Dw_widget_draw (DwWidget *widget,
357 DwRectangle *area,
358 GdkEventExpose *event)
359 {
360 /* NOTE: This function depends on that widgets are always drawn top-down.
361 * The initial draw call is done for the top-level widget by the viewport,
362 * all other draw calls are done for children. */
363 GtkDwViewport *viewport;
364 GdkDrawable *orig_pixmap, *dest;
365 gint x, y;
366 DwWidgetClass *klass;
367
368 orig_pixmap = widget->parent ? widget->parent->clip_pixmap : NULL;
369 widget->clip_pixmap = orig_pixmap;
370 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
371 if (klass->draw)
372 (* (klass->draw)) (widget, area, event);
373
374 if (widget->clip_pixmap &&
375 (widget->parent == NULL ||
376 widget->clip_pixmap != widget->parent->clip_pixmap)) {
377 /* Copy clipping pixmap into backing pixmap, when this widget has called
378 * p_Dw_widget_will_clip(). */
379 viewport = GTK_DW_VIEWPORT (widget->viewport);
380 dest = orig_pixmap ? orig_pixmap : viewport->back_pixmap;
381 x = area->x + widget->allocation.x -
382 gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
383 y = area->y + widget->allocation.y -
384 gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
385 gdk_draw_pixmap (dest, widget->viewport->style->black_gc,
386 widget->clip_pixmap, x, y,
387 x, y, area->width, area->height);
388 gdk_pixmap_unref (widget->clip_pixmap);
389 }
390 }
391
392
393 /*
394 * Handles a mouse event.
395 *
396 * This function is called by Dw_gtk_viewport_mouse_event, the type of
397 * the event is determined by event->type. x and y are world coordinates.
398 * widget may be NULL (if the pointer is outside the top-level widget).
399 *
400 * When event is NULL, GDK_MOTION_NOTIFY is used as the type. This will
401 * soon be the case when GDK_MOTION_NOTIFY events are simulated as a
402 * result of viewport changes (bug #94)
403 */
404 gint Dw_widget_mouse_event (DwWidget *widget,
405 GtkWidget *viewwidget,
406 gint32 x,
407 gint32 y,
408 GdkEvent *event)
409 {
410 gint signal_no;
411 gboolean return_val;
412 DwWidgetClass *klass;
413 GtkDwViewport *viewport = GTK_DW_VIEWPORT (viewwidget);
414 GdkEventType event_type;
415 DwWidget *ancestor, *w1, *w2, **track;
416 gint track_len, i;
417
418 DEBUG_MSG(DEBUG_EVENT,
419 "------------------------- EVENT -------------------------\n");
420
421 /* simulate crossing events */
422 /* todo: resizing/moving widgets */
423 if (widget != viewport->last_entered) {
424 DEBUG_MSG (DEBUG_EVENT, "----------> crossing event\n");
425 DEBUG_MSG (DEBUG_EVENT, " last = %p, now = %p\n",
426 viewport->last_entered, widget);
427
428 /* Determine the next common ancestor of the widgets. */
429 if (viewport->last_entered == NULL || widget == NULL)
430 ancestor = NULL;
431 else {
432 /* There is probably a faster algorithm. ;-) */
433 ancestor = NULL;
434 for (w1 = viewport->last_entered; ancestor == NULL && w1 != NULL;
435 w1 = w1->parent)
436 for (w2 = widget; ancestor == NULL && w2 != NULL; w2 = w2->parent)
437 if (w1 == w2)
438 ancestor = w1;
439 }
440
441 /* Construct the track:
442 * viewport->last_entered => anchestor => (current) widget */
443
444 DEBUG_MSG (DEBUG_EVENT, "common ancestor: %s %p\n",
445 (ancestor ?
446 gtk_type_name (GTK_OBJECT_TYPE (ancestor)) : "(none)"),
447 ancestor);
448
449 track_len = 0;
450 if (viewport->last_entered)
451 for (w1 = viewport->last_entered; w1 != ancestor; w1 = w1->parent)
452 track_len++;
453 if (ancestor)
454 track_len++; /* for the ancestor */
455 if (widget)
456 for (w1 = widget; w1 != ancestor; w1 = w1->parent)
457 track_len++;
458
459 track = g_new (DwWidget*, track_len);
460 i = 0;
461 if (viewport->last_entered)
462 for (w1 = viewport->last_entered; w1 != ancestor; w1 = w1->parent)
463 track[i++] = w1;
464 if (ancestor)
465 track[i++] = ancestor;
466 if (widget) {
467 i = track_len - 1;
468 for (w1 = widget; w1 != ancestor; w1 = w1->parent)
469 track[i--] = w1;
470 }
471
472 /* Send events to all events on the track */
473 /* todo: emit signals */
474 for (i = 0; i < track_len; i++) {
475 klass = DW_WIDGET_CLASS (GTK_OBJECT(track[i])->klass);
476 if (i != 0) {
477 if (klass->enter_notify_event)
478 klass->enter_notify_event (track[i], track[i - 1],
479 (GdkEventMotion*) event);
480 DEBUG_MSG (DEBUG_EVENT, "entering %s %p\n",
481 gtk_type_name (GTK_OBJECT_TYPE (track[i])), track[i]);
482 }
483 if (i != track_len - 1) {
484 if (klass->leave_notify_event)
485 klass->leave_notify_event (track[i], track[i + 1],
486 (GdkEventMotion*) event);
487 DEBUG_MSG (DEBUG_EVENT, "leaving %s %p\n",
488 gtk_type_name (GTK_OBJECT_TYPE (track[i])), track[i]);
489 }
490 }
491
492 DEBUG_MSG (DEBUG_EVENT, "<----------\n");
493
494 g_free (track);
495 viewport->last_entered = widget;
496 if (widget)
497 Dw_widget_update_cursor(widget);
498 else
499 gdk_window_set_cursor (GTK_LAYOUT(viewport)->bin_window, NULL);
500 }
501
502 /* other events */
503 event_type = event ? event->type : GDK_MOTION_NOTIFY;
504
505 while (widget) {
506 switch (event_type) {
507 case GDK_BUTTON_PRESS:
508 case GDK_2BUTTON_PRESS:
509 case GDK_3BUTTON_PRESS:
510 if (widget->button_sensitive)
511 signal_no = widget_signals[BUTTON_PRESS_EVENT];
512 else
513 signal_no = -1;
514 break;
515
516 case GDK_BUTTON_RELEASE:
517 if (widget->button_sensitive)
518 signal_no = widget_signals[BUTTON_RELEASE_EVENT];
519 else
520 signal_no = -1;
521 break;
522
523 case GDK_MOTION_NOTIFY:
524 signal_no = widget_signals[MOTION_NOTIFY_EVENT];
525 break;
526
527 default:
528 signal_no = -1;
529 break;
530 }
531
532 DEBUG_MSG (DEBUG_EVENT, "Sending %s event to %p, a %s.\n",
533 (event_type == GDK_MOTION_NOTIFY ? "motion notify" :
534 event_type == GDK_BUTTON_RELEASE ? "button release" :
535 "button press"),
536 widget, gtk_type_name (GTK_OBJECT_TYPE (widget)));
537
538 if (signal_no != -1) {
539 return_val = FALSE;
540 gtk_signal_emit (GTK_OBJECT (widget), signal_no,
541 x - widget->allocation.x, y - widget->allocation.y,
542 event, &return_val);
543 if (return_val) {
544 DEBUG_MSG (DEBUG_EVENT, "-> Processed.\n");
545 return TRUE;
546 }
547 }
548
549 widget = widget->parent;
550 }
551
552 DEBUG_MSG (DEBUG_EVENT, "-> Not processed at all.\n");
553 return FALSE;
554 }
555
556
557 /*
558 * Change the style of a widget. The old style is automatically
559 * unreferred, the new is referred. If this call causes the widget to
560 * change its size, Dw_widget_queue_resize is called.
561 */
562 void a_Dw_widget_set_style (DwWidget *widget,
563 DwStyle *style)
564 {
565 gboolean size_changed;
566
567 if (widget->style) {
568 a_Dw_style_unref (widget->style);
569 size_changed = a_Dw_style_size_diffs (widget->style, style);
570 } else
571 size_changed = TRUE;
572
573 a_Dw_style_ref (style);
574 widget->style = style;
575
576 if (widget->parent == NULL && widget->viewport != NULL)
577 Dw_gtk_viewport_update_background (GTK_DW_VIEWPORT (widget->viewport));
578
579 if (size_changed)
580 p_Dw_widget_queue_resize (widget, 0, TRUE);
581 else
582 p_Dw_widget_queue_draw (widget);
583 }
584
585
586 /*
587 * Set the cursor of the viewport.
588 * Called from several other functions.
589 */
590 void Dw_widget_update_cursor (DwWidget *widget)
591 {
592 GtkDwViewport *viewport = GTK_DW_VIEWPORT (widget->viewport);
593 DwWidget *cursor_widget;
594
595 if (GTK_WIDGET_REALIZED (viewport)) {
596 /* Search cursor to use, going up from last_entered (not from widget!).
597 */
598 cursor_widget = viewport->last_entered;
599 while (cursor_widget && cursor_widget->cursor == NULL)
600 cursor_widget = cursor_widget->parent;
601
602 if (cursor_widget)
603 gdk_window_set_cursor (GTK_LAYOUT(viewport)->bin_window,
604 cursor_widget->cursor);
605 else
606 gdk_window_set_cursor (GTK_LAYOUT(viewport)->bin_window,
607 NULL);
608 }
609 }
610
611 /*
612 * Set the cursor for a DwWidget. cursor has to be stored elsewhere, it
613 * is not copied (and not destroyed). If cursor is NULL, the cursor of
614 * the parent widget is used.
615 */
616 void a_Dw_widget_set_cursor (DwWidget *widget,
617 GdkCursor *cursor)
618 {
619 widget->cursor = cursor;
620 Dw_widget_update_cursor (widget);
621 }
622
623
624 /*
625 * If this function is called with button_sensitive == FALSE, the widget will
626 * never receive button press/release events, instead they are sent to the
627 * parent widgets. This attribute is inherited from the parent, if this
628 * function is never called.
629 *
630 * TODO: A bit hackish, this is only needed for disabling selection
631 * within <BUTTON>'s, and so make the button accessible at all.
632 * It would be better to handle this problem in a way links are handled,
633 * but this case is much more complicated, since a button is more complex
634 * than a link.
635 *
636 * NOTE: This may be replaced by somehow using signals for events.
637 */
638 void a_Dw_widget_set_button_sensitive (DwWidget *widget,
639 gboolean button_sensitive)
640 {
641 widget->button_sensitive = button_sensitive;
642 widget->button_sensitive_set = TRUE;
643 }
644
645
646 /*
647 * ...
648 */
649 DwWidget *a_Dw_widget_get_toplevel (DwWidget *widget)
650 {
651 while (widget->parent)
652 widget = widget->parent;
653
654 return widget;
655 }
656
657 /*
658 * Scrolls the viewport, so that the region [x, y, width, height] (widget
659 * coordinates) is seen, according to hpos and vpos.
660 */
661 void a_Dw_widget_scroll_to (DwWidget *widget,
662 DwHPosition hpos,
663 DwVPosition vpos,
664 gint32 x,
665 gint32 y,
666 gint32 width,
667 gint32 height)
668 {
669 Dw_gtk_viewport_scroll_to (GTK_DW_VIEWPORT (widget->viewport),
670 hpos, vpos,
671 x + widget->allocation.x,
672 y + widget->allocation.y,
673 width, height);
674 }
675
676 /*
677 * Retreive an iterator pointing before the first content element
678 * of the widget.
679 */
680 DwIterator* a_Dw_widget_iterator (DwWidget *widget,
681 gint mask,
682 gboolean at_end)
683 {
684 DwWidgetClass *klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
685 if (klass->iterator)
686 return klass->iterator(widget, mask, at_end);
687 else
688 return NULL;
689 }
690
691 /*
692 * This implementation of DwIterator::clone() can be used, when the
693 * structure DwIterator is used.
694 */
695 DwIterator* p_Dw_iterator_clone_std (DwIterator *it)
696 {
697 DwIterator *it2 = g_new (DwIterator, 1);
698 *it2 = *it;
699 return it2;
700 }
701
702 /*
703 * An implementation of DwIterator::free(), which should be sufficient for most
704 * iterator implementations.
705 */
706 void p_Dw_iterator_free_std (DwIterator *it)
707 {
708 g_free (it);
709 }
710
711 /*
712 * No-op implementation of DwIterator::highlight().
713 */
714 void p_Dw_iterator_highlight_std (DwIterator *it,
715 gint start,
716 gint end,
717 DwHighlightLayer layer)
718 {
719 /* do nothing */
720 }
721
722 /*
723 * This implementation of DwIterator::get_allocation can be used by all
724 * widgets, whose iterators only return widgets themselves.
725 */
726 void p_Dw_iterator_get_allocation_std_only_widgets (DwIterator *it,
727 gint start,
728 gint end,
729 DwAllocation *allocation)
730 {
731 g_return_if_fail (it->content.type == DW_CONTENT_WIDGET);
732
733 *allocation = it->content.data.widget->allocation;
734 }
735
736 /*
737 * Scrolls the viewport, so that the region between it1 and it2 is
738 * seen, according to hpos and vpos. The parameters start and end have
739 * the same meaning as in DwIterator::get_allocation(); start refers
740 * to it1, while end ferers to it2.
741 *
742 * If it1 and it2 point to the same location (see code below), only
743 * it1 is regarded, and both start and end refer to it.
744 */
745 void a_Dw_iterator_scroll_to (DwIterator *it1,
746 DwIterator *it2,
747 gint start,
748 gint end,
749 DwHPosition hpos,
750 DwVPosition vpos)
751 {
752 DwAllocation alloc1, alloc2, alloc;
753 gint32 x1, x2, y1, y2, vp_width, vp_height;
754 DwExtIterator *eit1, *eit2, *eit;
755 gint cur_start, cur_end, cmp;
756 gboolean at_start;
757
758 DBG_MSG (it1->widget->viewport, "scrolling", 0, "a_Dw_iterator_scroll_to");
759 DBG_MSG_START (it1->widget->viewport);
760
761 if (it1 == it2 ||
762 (it1->widget == it2->widget && a_Dw_iterator_compare (it1, it2) == 0)) {
763 a_Dw_iterator_get_allocation (it1, start, end, &alloc);
764 Dw_gtk_viewport_scroll_to (GTK_DW_VIEWPORT (it1->widget->viewport),
765 hpos, vpos, alloc.x, alloc.y,
766 alloc.width, alloc.ascent + alloc.descent);
767 } else {
768 /* First, determine the rectangle all iterators from it1 and it2
769 * allocate, i.e. the smallest rectangle containing all allocations of
770 * these iterators. */
771 eit1 = a_Dw_ext_iterator_new (it1);
772 eit2 = a_Dw_ext_iterator_new (it2);
773
774 x1 = DW_INFINITY;
775 x2 = - DW_INFINITY;
776 y1 = DW_INFINITY;
777 y2 = - DW_INFINITY;
778
779 DBG_MSG_START (it1->widget->viewport);
780 for (eit = a_Dw_ext_iterator_clone (eit1), at_start = TRUE;
781 (cmp = a_Dw_ext_iterator_compare (eit, eit2)) <= 0;
782 a_Dw_ext_iterator_next (eit), at_start = FALSE) {
783 if (at_start)
784 cur_start = start;
785 else
786 cur_start = 0;
787
788 if (cmp == 0)
789 cur_end = end;
790 else
791 cur_end = DW_INFINITY;
792
793 a_Dw_ext_iterator_get_allocation (eit, cur_start, cur_end, &alloc);
794 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
795 "allocation of %s, from %d to %d: [%d, %d, %d x %d x %d]\n",
796 a_Dw_content_html (&eit->content), cur_start, cur_end,
797 alloc.x, alloc.y, alloc.width, alloc.ascent, alloc.descent);
798 x1 = MIN (x1, alloc.x);
799 x2 = MAX (x2, alloc.x + alloc.width);
800 y1 = MIN (y1, alloc.y);
801 y2 = MAX (y2, alloc.y + alloc.ascent + alloc.descent);
802 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
803 "result is [%d, %d, %d x %d]", x1, y1, x2 - x1, y2 - y1);
804 }
805 DBG_MSG_END (it1->widget->viewport);
806
807 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
808 "1. region is [%d, %d, %d x %d]", x1, y1, x2 - x1, y2 - y1);
809
810 a_Dw_iterator_get_allocation (it1, start, DW_INFINITY, &alloc1);
811 a_Dw_iterator_get_allocation (it2, 0, end, &alloc2);
812
813 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
814 "alloc1 = [%d, %d, %d x %d x %d]",
815 alloc1.x, alloc1.y,
816 alloc1.width, alloc1.ascent, alloc1.descent);
817 DBG_MSGF (it2->widget->viewport, "scrolling", 0,
818 "alloc2 = [%d, %d, %d x %d x %d]",
819 alloc2.x, alloc2.y,
820 alloc2.width, alloc2.ascent, alloc2.descent);
821
822 if (alloc1.x > alloc2.x) {
823 /*
824 * This is due to a line break within the region. When the line is
825 * longer than the viewport, and the region is actually quite short,
826 * the user would not see anything of the region, as in this figure
827 * (with region marked as "#"):
828 *
829 * +----------+ ,-- alloc1
830 * | | V
831 * | | ### ###
832 * ### ### | |
833 * ^ | | <-- viewport
834 * | +----------+
835 * `-- alloc2
836 * |----------------------------|
837 * width
838 *
839 * Therefor, we the region smaller, so that the region will be
840 * displayed like this:
841 *
842 * ,-- alloc1
843 * +----|-----+
844 * | V |
845 * | ### ###|
846 * ### ### | |
847 * ^ | | <-- viewport
848 * `-- alloc2 +----------+
849 * |----------|
850 * width
851 *
852 * todo: Changes in the viewport size, until the idle function is
853 * called, are not regarded.
854 */
855
856 vp_width = it1->widget->viewport->allocation.width
857 - GTK_CONTAINER(it1->widget->viewport)->border_width;
858 DBG_MSGF (it2->widget->viewport, "scrolling", 0,
859 "vp_width = %d", vp_width);
860 if (x2 - x1 > vp_width) {
861 x1 = x2 - vp_width;
862 x2 = x1 + vp_width;
863 }
864 }
865
866 if (alloc1.y > alloc2.y) {
867 /* This is similar to the case above, e.g. if the region ends in
868 * another table column. */
869 vp_height = it1->widget->viewport->allocation.height
870 - GTK_CONTAINER(it1->widget->viewport)->border_width;
871 DBG_MSGF (it2->widget->viewport, "scrolling", 0,
872 "vp_height = %d", vp_height);
873 if (y2 - y1 > vp_height) {
874 y1 = y2 - vp_height;
875 y2 = y1 + vp_height;
876 }
877 }
878
879 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
880 "2. region is [%d, %d, %d x %d]", x1, y1, x2 - x1, y2 - y1);
881
882 Dw_gtk_viewport_scroll_to (GTK_DW_VIEWPORT (it1->widget->viewport),
883 hpos, vpos, x1, y1, x2 - x1, y2 - y1);
884 }
885
886 DBG_MSG_END (it1->widget->viewport);
887 }
888
889 /*
890 * For DwIteratorInt.
891 */
892 DwIterator* p_Dw_iterator_clone_std_int (DwIterator *it)
893 {
894 DwIteratorInt *it2 = g_new (DwIteratorInt, 1);
895 *it2 = *(DwIteratorInt*)it;
896 return (DwIterator*)it2;
897 }
898
899 /*
900 * For DwIteratorInt.
901 */
902 gint p_Dw_iterator_compare_std_int (DwIterator *it1,
903 DwIterator *it2)
904 {
905 DwIteratorInt *ii1 = (DwIteratorInt*)it1;
906 DwIteratorInt *ii2 = (DwIteratorInt*)it2;
907
908 g_return_val_if_fail (it1->widget == it2->widget, 0);
909
910 if (ii1->pos == ii2->pos)
911 return 0;
912 if (ii1->pos < ii2->pos)
913 return -1;
914 else
915 return +1;
916 }
917
918 /*
919 * This function returns a descriptive text for a piece of content,
920 * useful for debugging.
921 * NOTE: This function is not very reliable, and should really only
922 * used for non-critical tasks like debugging.
923 */
924 gchar *a_Dw_content_text (DwContent *content)
925 {
926 /* We cycle through several buffers, so that printf should have
927 * no problems. */
928 #define BUF_NUM 5
929 #define BUF_SIZE 2048
930 static gchar buf[BUF_SIZE * BUF_NUM];
931 static int cur_buf = 0;
932 gchar *ptr;
933
934 ptr = buf + cur_buf * BUF_SIZE;
935 cur_buf = (cur_buf + 1) % BUF_NUM;
936
937 switch (content->type) {
938 case DW_CONTENT_START:
939 sprintf (ptr, "<start>");
940 break;
941 case DW_CONTENT_END:
942 sprintf (ptr, "<end>");
943 break;
944 case DW_CONTENT_TEXT:
945 sprintf (ptr, "\"%s\"", content->data.text);
946 break;
947 case DW_CONTENT_WIDGET:
948 sprintf (ptr, "the %s %p",
949 gtk_type_name (GTK_OBJECT_TYPE (content->data.widget)),
950 content->data.widget);
951 break;
952 case DW_CONTENT_ANCHOR:
953 sprintf (ptr, "#%s", content->data.anchor);
954 break;
955 case DW_CONTENT_BREAK:
956 sprintf (ptr, "<break(%d)>", content->data.break_space);
957 break;
958 default:
959 sprintf (ptr, "<unknown %d>", content->type);
960 break;
961 }
962
963 return ptr;
964 }
965
966 /*
967 * Like a_Dw_content_text, but returns HTML.
968 * NOTE: This function is not very reliable, and should really only
969 * used for non-critical tasks like debugging.
970 */
971 gchar* a_Dw_content_html (DwContent *content)
972 {
973 /* We cycle through several buffers, so that printf should have
974 * no problems. */
975 #define BUF_NUM 5
976 #define BUF_SIZE 2048
977 static gchar buf[BUF_SIZE * BUF_NUM];
978 static int cur_buf = 0;
979 gchar *ptr, *ptr1, *ptr2;
980
981 ptr = buf + cur_buf * BUF_SIZE;
982 cur_buf = (cur_buf + 1) % BUF_NUM;
983
984 switch (content->type) {
985 case DW_CONTENT_START:
986 sprintf (ptr, "<i>&lt;start&gt;</i>");
987 break;
988 case DW_CONTENT_END:
989 sprintf (ptr, "<i>&lt;end&gt;</i>");
990 break;
991 case DW_CONTENT_TEXT:
992 *ptr = '"';
993 ptr2 = ptr + 1;
994
995 for (ptr1 = content->data.text; *ptr1; ptr1++) {
996 switch (*ptr1) {
997 case '<':
998 strcpy (ptr2, "&lt;");
999 ptr2 += 4;
1000 break;
1001 case '>':
1002 strcpy (ptr2, "&gt;");
1003 ptr2 += 4;
1004 break;
1005 case '&':
1006 strcpy (ptr2, "&amp;");
1007 ptr2 += 5;
1008 break;
1009 default:
1010 *ptr2 = *ptr1;
1011 ptr2++;
1012 }
1013 }
1014
1015 ptr2[0] = '"';
1016 ptr2[1] = 0;
1017 break;
1018 case DW_CONTENT_WIDGET:
1019 sprintf (ptr, "the %s %p",
1020 gtk_type_name (GTK_OBJECT_TYPE (content->data.widget)),
1021 content->data.widget);
1022 break;
1023 case DW_CONTENT_ANCHOR:
1024 sprintf (ptr, "#%s", content->data.anchor);
1025 break;
1026 case DW_CONTENT_BREAK:
1027 sprintf (ptr, "<i>&lt;break(%d)&gt;</i>", content->data.break_space);
1028 break;
1029 default:
1030 sprintf (ptr, "<i>&lt;unknown %d&gt;</i>", content->type);
1031 break;
1032 }
1033
1034 return ptr;
1035 }
1036
1037 /*
1038 * This function returns a descriptive text for an iterator, useful
1039 * for debugging.
1040 * NOTE: This function is not very reliable, and should really only
1041 * used for non-critical tasks like debugging.
1042 */
1043 gchar *a_Dw_iterator_text (DwIterator *it)
1044 {
1045 /* We cycle through several buffers, so that printf should have
1046 * no problems. */
1047 #define BUF_NUM 5
1048 #define BUF_SIZE 2048
1049 static gchar buf[BUF_SIZE * BUF_NUM];
1050 static int cur_buf = 0;
1051 gchar *ptr;
1052
1053 ptr = buf + cur_buf * BUF_SIZE;
1054 cur_buf = (cur_buf + 1) % BUF_NUM;
1055
1056 if (it)
1057 sprintf (ptr, "[%s in the %s %p]",
1058 a_Dw_content_text (&it->content),
1059 gtk_type_name (GTK_OBJECT_TYPE (it->widget)), it->widget);
1060 else
1061 strcpy (ptr, "[NULL]");
1062
1063 return ptr;
1064 }
1065
1066 /*
1067 * This function prints the contents of a whole widget tree.
1068 * NOTE: This function is not very reliable, and should really only
1069 * used for non-critical tasks like debugging.
1070 */
1071 static void Dw_widget_print_tree0 (DwWidget *widget, int indent)
1072 {
1073 DwIterator *it;
1074
1075 MSG ("%*sthe %s %p, contains:\n",
1076 indent, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget);
1077 it = a_Dw_widget_iterator (widget, 0xff, FALSE);
1078 while (a_Dw_iterator_next (it)) {
1079 if (it->content.type == DW_CONTENT_WIDGET)
1080 Dw_widget_print_tree0 (it->content.data.widget, indent + 3);
1081 else
1082 MSG ("%*s%s\n", indent + 3, "", a_Dw_content_text (&it->content));
1083 }
1084 }
1085
1086 void a_Dw_widget_print_tree (DwWidget *widget)
1087 {
1088 MSG ("--- START OF WIDGET TREE ---\n");
1089 Dw_widget_print_tree0 (widget, 0);
1090 MSG ("--- END OF WIDGET TREE ---\n");
1091 }
1092
1093
1094 /*
1095 * The following is a standard implementation for iterators containing
1096 * exactly one piece of text.
1097 */
1098
1099 DwIterator* p_Dw_widget_text_iterator (DwWidget *widget,
1100 gint32 mask,
1101 gboolean at_end,
1102 gchar *text)
1103 {
1104 DwIteratorText *it;
1105
1106 if (mask & DW_CONTENT_TEXT) {
1107 it = g_new (DwIteratorText, 1);
1108 it->it.widget = widget;
1109 it->it.mask = mask;
1110 it->it.content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
1111 it->it.next = p_Dw_iterator_text_next;
1112 it->it.prev = p_Dw_iterator_text_prev;
1113 it->it.clone = p_Dw_iterator_text_clone;
1114 it->it.compare = p_Dw_iterator_text_compare;
1115 it->it.free = p_Dw_iterator_free_std;
1116 it->it.highlight = p_Dw_iterator_highlight_std;
1117 it->it.get_allocation = p_Dw_iterator_text_get_allocation;
1118 it->text = text;
1119 } else
1120 it = NULL;
1121
1122 return (DwIterator*)it;
1123 }
1124
1125 gboolean p_Dw_iterator_text_next (DwIterator *it)
1126 {
1127 if (it->content.type == DW_CONTENT_START) {
1128 it->content.type = DW_CONTENT_TEXT;
1129 it->content.data.text = ((DwIteratorText*)it)->text;
1130 return TRUE;
1131 } else {
1132 it->content.type = DW_CONTENT_END;
1133 return FALSE;
1134 }
1135 }
1136
1137 gboolean p_Dw_iterator_text_prev (DwIterator *it)
1138 {
1139 if (it->content.type == DW_CONTENT_END) {
1140 it->content.type = DW_CONTENT_TEXT;
1141 it->content.data.text = ((DwIteratorText*)it)->text;
1142 return TRUE;
1143 } else {
1144 it->content.type = DW_CONTENT_START;
1145 return FALSE;
1146 }
1147 }
1148
1149 DwIterator* p_Dw_iterator_text_clone (DwIterator *it)
1150 {
1151 DwIteratorText *it2 = g_new (DwIteratorText, 1);
1152 *it2 = *(DwIteratorText*)it;
1153 return (DwIterator*)it2;
1154 }
1155
1156 gint p_Dw_iterator_text_compare (DwIterator *it1,
1157 DwIterator *it2)
1158 {
1159 if (it1->content.type == it2->content.type)
1160 return 0;
1161
1162 switch (it1->content.type) {
1163 case DW_CONTENT_START:
1164 return -1;
1165 case DW_CONTENT_TEXT:
1166 if (it2->content.type == DW_CONTENT_START)
1167 return +1;
1168 else
1169 return -1;
1170 case DW_CONTENT_END:
1171 return +1;
1172 default:
1173 return 0;
1174 }
1175 }
1176
1177 void p_Dw_iterator_text_get_allocation (DwIterator *it,
1178 gint start,
1179 gint end,
1180 DwAllocation *allocation)
1181 {
1182 /*
1183 * Return the allocation of the widget. This is a bit incorrect, since
1184 * start and end are not regarded, but should be correct enough for most
1185 * purposes.
1186 */
1187 *allocation = it->widget->allocation;
1188 }
1189
1190 /*
1191 * Calculates the intersection of widget->allocation and area, returned in
1192 * intersection (in widget coordinates!). Typically used by containers when
1193 * drawing their children. Returns whether intersection is not empty.
1194 */
1195 gint p_Dw_widget_intersect (DwWidget *widget,
1196 DwRectangle *area,
1197 DwRectangle *intersection)
1198 {
1199 #if 1
1200 DwRectangle parent_area, child_area;
1201
1202 parent_area = *area;
1203 parent_area.x += widget->parent->allocation.x;
1204 parent_area.y += widget->parent->allocation.y;
1205
1206 child_area.x = widget->allocation.x;
1207 child_area.y = widget->allocation.y;
1208 child_area.width = widget->allocation.width;
1209 child_area.height = DW_WIDGET_HEIGHT(widget);
1210
1211 if (p_Dw_rectangle_intersect (&parent_area, &child_area, intersection)) {
1212 intersection->x -= widget->allocation.x;
1213 intersection->y -= widget->allocation.y;
1214 return TRUE;
1215 } else
1216 return FALSE;
1217 #else
1218 intersection->x = 0;
1219 intersection->y = 0;
1220 intersection->width = widget->allocation.width;
1221 intersection->height = DW_WIDGET_HEIGHT(widget);
1222
1223 return TRUE;
1224 #endif
1225 }
1226
1227
1228 void p_Dw_widget_set_parent (DwWidget *widget,
1229 DwWidget *parent)
1230 {
1231 gtk_object_ref (GTK_OBJECT (widget));
1232 gtk_object_sink (GTK_OBJECT (widget));
1233 widget->parent = parent;
1234 widget->viewport = parent->viewport;
1235 /*widget->window = parent->window;*/
1236
1237 if (!widget->button_sensitive_set)
1238 widget->button_sensitive = parent->button_sensitive;
1239
1240 DBG_OBJ_ASSOC (widget, parent);
1241 }
1242
1243
1244 /*
1245 * Converting between coordinates.
1246 */
1247
1248 gint32 p_Dw_widget_x_viewport_to_world (DwWidget *widget,
1249 gint16 viewport_x)
1250 {
1251 GtkAdjustment *adjustment;
1252
1253 g_return_val_if_fail (widget && widget->viewport, 0);
1254 adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (widget->viewport));
1255 g_return_val_if_fail (adjustment != NULL, 0);
1256
1257 return viewport_x + (gint32)adjustment->value;
1258 }
1259
1260
1261 gint32 p_Dw_widget_y_viewport_to_world (DwWidget *widget,
1262 gint16 viewport_y)
1263 {
1264 GtkAdjustment *adjustment;
1265
1266 g_return_val_if_fail (widget && widget->viewport, 0);
1267 adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget->viewport));
1268 g_return_val_if_fail (adjustment != NULL, 0);
1269
1270 return viewport_y + (gint32)adjustment->value;
1271 }
1272
1273
1274 gint16 p_Dw_widget_x_world_to_viewport (DwWidget *widget,
1275 gint32 world_x)
1276 {
1277 GtkAdjustment *adjustment;
1278
1279 g_return_val_if_fail (widget && widget->viewport, 0);
1280 adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (widget->viewport));
1281 g_return_val_if_fail (adjustment != NULL, 0);
1282
1283 return world_x - (gint32)adjustment->value;
1284 }
1285
1286
1287 gint16 p_Dw_widget_y_world_to_viewport (DwWidget *widget,
1288 gint32 world_y)
1289 {
1290 GtkAdjustment *adjustment;
1291
1292 g_return_val_if_fail (widget && widget->viewport, 0);
1293 adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget->viewport));
1294 g_return_val_if_fail (adjustment != NULL, 0);
1295
1296 return world_y - (gint32)adjustment->value;
1297 }
1298
1299
1300 /*
1301 * Calculate the intersection of (x, y, width, height) (widget
1302 * coordinates) and the current viewport area. gdk_intersection has
1303 * (of course) viewport coordinates, the return value is TRUE iff the
1304 * intersection is not empty.
1305 */
1306 static gboolean Dw_widget_intersect_viewport (DwWidget *widget,
1307 gint32 x,
1308 gint32 y,
1309 gint32 width,
1310 gint32 height,
1311 GdkRectangle *gdk_intersection)
1312 {
1313 GtkLayout *layout;
1314 DwRectangle widget_area, viewport_area, intersection;
1315
1316 g_return_val_if_fail (widget && widget->viewport, FALSE);
1317
1318 layout = GTK_LAYOUT (widget->viewport);
1319
1320 widget_area.x = widget->allocation.x + x;
1321 widget_area.y = widget->allocation.y + y;
1322 widget_area.width = width;
1323 widget_area.height = height;
1324
1325 viewport_area.x = layout->xoffset;
1326 viewport_area.y = layout->yoffset;
1327 viewport_area.width = widget->viewport->allocation.width;
1328 viewport_area.height = widget->viewport->allocation.height;
1329
1330 if (p_Dw_rectangle_intersect (&widget_area, &viewport_area,
1331 &intersection)) {
1332 gdk_intersection->x = intersection.x - layout->xoffset;
1333 gdk_intersection->y = intersection.y - layout->yoffset;
1334 gdk_intersection->width = intersection.width;
1335 gdk_intersection->height = intersection.height;
1336 return TRUE;
1337 } else
1338 return FALSE;
1339 }
1340
1341
1342 /*
1343 * ...
1344 */
1345 void p_Dw_widget_queue_draw (DwWidget *widget)
1346 {
1347 p_Dw_widget_queue_draw_area (widget, 0, 0, widget->allocation.width,
1348 DW_WIDGET_HEIGHT(widget));
1349 }
1350
1351
1352 /*
1353 * ...
1354 */
1355 void p_Dw_widget_queue_draw_area (DwWidget *widget,
1356 gint32 x,
1357 gint32 y,
1358 gint32 width,
1359 gint32 height)
1360 {
1361 /* todo: maybe only the intersection? */
1362 Dw_gtk_viewport_queue_draw (GTK_DW_VIEWPORT (widget->viewport),
1363 x + widget->allocation.x,
1364 y + widget->allocation.y, width, height);
1365 }
1366
1367
1368 /*
1369 * Resizing of Widgets.
1370 * The interface was adopted by Gtk+, but the implementation is far simpler,
1371 * since Gtk+ handles a lot of cases which are irrelevant to Dw.
1372 */
1373
1374 /*
1375 * This function should be called, if the widget changed its size.
1376 */
1377 void p_Dw_widget_queue_resize (DwWidget *widget,
1378 gint ref,
1379 gboolean extremes_changed)
1380 {
1381 DwWidget *widget2, *child;
1382 DwWidgetClass *klass;
1383
1384 DEBUG_MSG (DEBUG_SIZE,
1385 "a %stop-level %s with parent_ref = %d has changed its size\n",
1386 widget->parent ? "non-" : "",
1387 gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref);
1388
1389 klass = (DwWidgetClass*)(((GtkObject*)widget)->klass);
1390 DW_WIDGET_SET_FLAGS (widget, DW_NEEDS_RESIZE);
1391 if (klass->mark_size_change)
1392 klass->mark_size_change (widget, ref);
1393
1394 if (extremes_changed) {
1395 DW_WIDGET_SET_FLAGS (widget, DW_EXTREMES_CHANGED);
1396 if (klass->mark_extremes_change)
1397 klass->mark_extremes_change (widget, ref);
1398 }
1399
1400 for (widget2 = widget->parent, child = widget;
1401 widget2;
1402 child = widget2, widget2 = widget2->parent) {
1403 klass = (DwWidgetClass*)(((GtkObject*)widget2)->klass);
1404 DW_WIDGET_SET_FLAGS (widget2, DW_NEEDS_RESIZE);
1405 if (klass->mark_size_change)
1406 klass->mark_size_change (widget2, child->parent_ref);
1407 DW_WIDGET_SET_FLAGS (widget2, DW_NEEDS_ALLOCATE);
1408
1409 DEBUG_MSG (DEBUG_ALLOC,
1410 "setting DW_NEEDS_ALLOCATE for a %stop-level %s "
1411 "with parent_ref = %d\n",
1412 widget2->parent ? "non-" : "",
1413 gtk_type_name (GTK_OBJECT_TYPE (widget2)),
1414 widget2->parent_ref);
1415
1416 if (extremes_changed) {
1417 DW_WIDGET_SET_FLAGS (widget2, DW_EXTREMES_CHANGED);
1418 if (klass->mark_extremes_change)
1419 klass->mark_extremes_change (widget2, child->parent_ref);
1420 }
1421 }
1422
1423 if (widget->viewport)
1424 Dw_gtk_viewport_queue_resize (GTK_DW_VIEWPORT (widget->viewport));
1425 }
1426
1427 /*
1428 * If a widget might draw outside of its allocation, this function should be
1429 * called at the beginning of the draw method. It will cause drawings be done
1430 * in a temporary pixmap, which is later copied into the backing pixmap, so
1431 * that drawings outside of the allocation are later discarded.
1432 *
1433 * Clipping causes a bit overhead due to the second copying, so it should
1434 * only used when neccessary.
1435 */
1436 void p_Dw_widget_will_clip (DwWidget *widget)
1437 {
1438 DwWidget *widget2;
1439
1440 widget->clip_pixmap =
1441 gdk_pixmap_new (widget->viewport->window,
1442 widget->viewport->allocation.width,
1443 widget->viewport->allocation.height,
1444 GTK_DW_VIEWPORT(widget->viewport)->depth);
1445
1446 /* Determine the effective background color. (There is a defined background
1447 * at least for the top-level widget.) */
1448 for (widget2 = widget;
1449 widget2 != NULL && widget2->style->background_color == NULL;
1450 widget2 = widget2->parent)
1451 ;
1452
1453 g_return_if_fail (widget2 != NULL);
1454
1455 gdk_draw_rectangle (widget->clip_pixmap,
1456 widget2->style->background_color->gc,
1457 TRUE, 0, 0,
1458 widget->viewport->allocation.width,
1459 widget->viewport->allocation.height);
1460 }
1461
1462
1463 /*
1464 * Set the background "behind" the widget, if it is not the background of the
1465 * parent widget, e.g. the background of a table row.
1466 */
1467 void p_Dw_widget_set_bg_color (DwWidget *widget,
1468 DwStyleColor *color)
1469 {
1470 widget->bg_color = color;
1471 }
1472
1473 /*
1474 * Get the actual background of a widget.
1475 */
1476 DwStyleColor* p_Dw_widget_get_bg_color (DwWidget *widget)
1477 {
1478 while (widget != NULL) {
1479 if (widget->style->background_color)
1480 return widget->style->background_color;
1481 if (widget->bg_color)
1482 return widget->bg_color;
1483
1484 widget = widget->parent;
1485 }
1486
1487 g_warning ("No background color found!");
1488 return NULL;
1489 }
1490
1491
1492 /*
1493 * Draw borders and background of a widget part, which allocation is
1494 * given by (x, y, width, height) (widget coordinates).
1495 */
1496 void p_Dw_widget_draw_box (DwWidget *widget,
1497 DwStyle *style,
1498 DwRectangle *area,
1499 gint32 x,
1500 gint32 y,
1501 gint32 width,
1502 gint32 height,
1503 gboolean inverse)
1504 {
1505 GdkRectangle gdk_area;
1506 gint32 vx, vy;
1507
1508 if (Dw_widget_intersect_viewport (widget, area->x, area->y,
1509 area->width, area->height, &gdk_area)) {
1510 vx = p_Dw_widget_x_viewport_to_world (widget, 0);
1511 vy = p_Dw_widget_y_viewport_to_world (widget, 0);
1512
1513 p_Dw_style_draw_border (DW_WIDGET_WINDOW (widget), &gdk_area,
1514 vx, vy,
1515 widget->allocation.x + x,
1516 widget->allocation.y + y,
1517 width, height,
1518 style, inverse);
1519
1520 if (style->background_color)
1521 p_Dw_style_draw_background (DW_WIDGET_WINDOW (widget), &gdk_area,
1522 vx, vy,
1523 widget->allocation.x + x,
1524 widget->allocation.y + y,
1525 width, height,
1526 style, inverse);
1527 }
1528 }
1529
1530
1531 /*
1532 * Draw borders and background of a widget.
1533 */
1534 void p_Dw_widget_draw_widget_box (DwWidget *widget,
1535 DwRectangle *area,
1536 gboolean inverse)
1537 {
1538 GdkRectangle gdk_area;
1539 gint32 vx, vy;
1540
1541 if (Dw_widget_intersect_viewport (widget, area->x, area->y,
1542 area->width, area->height, &gdk_area)) {
1543 vx = p_Dw_widget_x_viewport_to_world (widget, 0);
1544 vy = p_Dw_widget_y_viewport_to_world (widget, 0);
1545
1546 p_Dw_style_draw_border (DW_WIDGET_WINDOW (widget), &gdk_area,
1547 vx, vy,
1548 widget->allocation.x,
1549 widget->allocation.y,
1550 widget->allocation.width,
1551 DW_WIDGET_HEIGHT(widget),
1552 widget->style, inverse);
1553
1554 /* - Toplevel widget background colors are set as viewport
1555 * background color. This is not crucial for the rendering, but
1556 * looks a bit nicer when scrolling. Furthermore, the viewport
1557 * does anything else in this case.
1558 *
1559 * - Since widgets are always drawn from top to bottom, it is
1560 * *not* necessary to draw the background if
1561 * widget->style->background_color is NULL (shining through).
1562 */
1563 if (widget->parent && widget->style->background_color)
1564 p_Dw_style_draw_background (DW_WIDGET_WINDOW (widget), &gdk_area,
1565 vx, vy,
1566 widget->allocation.x,
1567 widget->allocation.y,
1568 widget->allocation.width,
1569 DW_WIDGET_HEIGHT(widget),
1570 widget->style, inverse);
1571 }
1572 }
1573
1574 /*
1575 * This function is used by some widgets, when they are selected (as a whole).
1576 *
1577 * todo: This could be accelerated by using clipping bitmaps. Two important
1578 * issues:
1579 *
1580 * (i) There should always been a pixel in the upper-left corner of the
1581 * *widget*, so probably two different clipping bitmaps have to be
1582 * used (10/01 and 01/10).
1583 *
1584 * (ii) Should a new GC always be created?
1585 */
1586 void p_Dw_widget_draw_selected (DwWidget *widget,
1587 DwRectangle *area)
1588 {
1589 GdkRectangle gdk_area;
1590 /* All coordinates are widget coordinates. */
1591 gint32 x, y, startxa, startya, startxb, startyb, endx, endy, ix, iy;
1592 gint32 dx, dy; /* the difference between viewport and widget */
1593 DwStyleColor *bg_color;
1594 GdkWindow *window;
1595
1596 if (Dw_widget_intersect_viewport (widget, area->x, area->y,
1597 area->width, area->height, &gdk_area)) {
1598 /* Calculate from where to start the respective drawing loops below.
1599 * There should always been a pixel in the upper-left corner of the
1600 * *widget*, so the start depends on whether the drawing area (in widget
1601 * coordinates) has even or odd offsets. */
1602
1603 /* the intersection in widget coordinates */
1604 ix = p_Dw_widget_x_viewport_to_world (widget, gdk_area.x) -
1605 widget->allocation.x;
1606 iy = p_Dw_widget_y_viewport_to_world (widget, gdk_area.y) -
1607 widget->allocation.y;
1608
1609 if (ix % 2 == 0) {
1610 startxa = ix;
1611 startxb = ix + 1;
1612 } else {
1613 startxa = ix + 1;
1614 startxb = ix;
1615 }
1616
1617 if (iy % 2 == 0) {
1618 startya = iy;
1619 startyb = iy + 1;
1620 } else {
1621 startya = iy + 1;
1622 startyb = iy;
1623 }
1624
1625 dx = p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x);
1626 dy = p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y);
1627 endx = ix + gdk_area.width;
1628 endy = iy + gdk_area.height;
1629
1630 bg_color = p_Dw_widget_get_bg_color (widget);
1631 window = DW_WIDGET_WINDOW (widget);
1632
1633 for (x = startxa; x < endx; x += 2)
1634 for (y = startya; y < endy; y += 2)
1635 gdk_draw_point (window, bg_color->inverse_gc, x + dx, y + dy);
1636
1637 for (x = startxb; x < endx; x += 2)
1638 for (y = startyb; y < endy; y += 2)
1639 gdk_draw_point (window, bg_color->inverse_gc, x + dx, y + dy);
1640 }
1641 }
0 #ifndef __DW_WIDGET_H__
1 #define __DW_WIDGET_H__
2
3 #include <gtk/gtkobject.h>
4 #include <gtk/gtkwidget.h>
5 #include <gdk/gdktypes.h>
6
7 #include "dw_style.h"
8 #include "dw.h"
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif /* __cplusplus */
13
14 #define DW_TYPE_WIDGET (a_Dw_widget_get_type ())
15 #define DW_WIDGET(obj) GTK_CHECK_CAST (obj, DW_TYPE_WIDGET, DwWidget)
16 #define DW_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_WIDGET, \
17 DwWidgetClass)
18 #define DW_IS_WIDGET(obj) GTK_CHECK_TYPE (obj, DW_TYPE_WIDGET)
19
20
21 #define DW_WIDGET_SET_FLAGS(wid, flag) (DW_WIDGET(wid)->flags |= (flag))
22 #define DW_WIDGET_UNSET_FLAGS(wid, flag) (DW_WIDGET(wid)->flags &= ~(flag))
23
24 #define DW_WIDGET_NEEDS_RESIZE(wid) (DW_WIDGET(wid)->flags & \
25 DW_NEEDS_RESIZE)
26 #define DW_WIDGET_NEEDS_ALLOCATE(wid) (DW_WIDGET(wid)->flags & \
27 DW_NEEDS_ALLOCATE)
28 #define DW_WIDGET_EXTREMES_CHANGED(wid) (DW_WIDGET(wid)->flags & \
29 DW_EXTREMES_CHANGED)
30
31 #define DW_WIDGET_USES_HINTS(wid) (DW_WIDGET(wid)->flags & \
32 DW_USES_HINTS)
33 #define DW_WIDGET_HAS_CONTENT(wid) (DW_WIDGET(wid)->flags & \
34 DW_HAS_CONTENT)
35
36 #define DW_WIDGET_HEIGHT(wid) ((wid)->allocation.ascent + \
37 (wid)->allocation.descent)
38 #define DW_WIDGET_CONTENT_HEIGHT(wid) \
39 ((wid)->allocation.ascent + (wid)->allocation.descent - \
40 p_Dw_style_box_diff_height((wid)->style))
41
42 #define DW_WIDGET_CONTENT_WIDTH(wid) ((wid)->allocation.width - \
43 p_Dw_style_box_diff_width((wid)->style))
44
45 typedef enum {
46 DW_NEEDS_RESIZE = 1 << 0,
47 DW_NEEDS_ALLOCATE = 1 << 1,
48 DW_EXTREMES_CHANGED = 1 << 2,
49 DW_USES_HINTS = 1 << 3,
50 DW_HAS_CONTENT = 1 << 4,
51 } DwWidgetFlags;
52
53 typedef enum
54 {
55 DW_HPOS_LEFT,
56 DW_HPOS_CENTER,
57 DW_HPOS_RIGHT,
58 DW_HPOS_INTO_VIEW, /* scroll only, until the content in question comes
59 * into view */
60 DW_HPOS_NO_CHANGE
61 } DwHPosition;
62
63 typedef enum
64 {
65 DW_VPOS_TOP,
66 DW_VPOS_CENTER,
67 DW_VPOS_BOTTOM,
68 DW_VPOS_INTO_VIEW, /* scroll only, until the content in question comes
69 * into view */
70 DW_VPOS_NO_CHANGE
71 } DwVPosition;
72
73 /* content types for iterator data */
74 typedef enum
75 {
76 DW_CONTENT_START = 1 << 0,
77 DW_CONTENT_END = 1 << 1,
78 DW_CONTENT_TEXT = 1 << 2,
79 DW_CONTENT_WIDGET = 1 << 3,
80 DW_CONTENT_ANCHOR = 1 << 4,
81 DW_CONTENT_BREAK = 1 << 5,
82 DW_CONTENT_ALL = 0xff
83 } DwContentType;
84
85 /* Different "layers" may be highlighted in a widget. */
86 typedef enum
87 {
88 DW_HIGHLIGHT_SELECTION,
89 DW_HIGHLIGHT_FINDTEXT,
90 DW_HIGHLIGHT_NUM_LAYERS
91 } DwHighlightLayer;
92
93 #define DW_WIDGET_WINDOW(widget) \
94 ((widget)->clip_pixmap ? (widget)->clip_pixmap : \
95 ((GtkDwViewport*)(widget)->viewport)->back_pixmap)
96
97 typedef struct _DwAllocation DwAllocation;
98 typedef struct _DwRequisition DwRequisition;
99 typedef struct _DwExtremes DwExtremes;
100 typedef struct _DwRectPosition DwRectPosition;
101 typedef struct _DwContent DwContent;
102 typedef struct _DwIterator DwIterator;
103 typedef struct _DwIteratorInt DwIteratorInt;
104 typedef struct _DwIteratorText DwIteratorText;
105
106 typedef struct _DwWidget DwWidget;
107 typedef struct _DwWidgetClass DwWidgetClass;
108
109 #define DW_PAINT_DEFAULT_BGND 0xd6d6c0
110
111
112 struct _DwAllocation
113 {
114 gint32 x;
115 gint32 y;
116 gint32 width;
117 gint32 ascent;
118 gint32 descent;
119 };
120
121
122 struct _DwRequisition
123 {
124 gint32 width;
125 gint32 ascent;
126 gint32 descent;
127 };
128
129
130 struct _DwExtremes
131 {
132 gint32 min_width;
133 gint32 max_width;
134 };
135
136
137 struct _DwRectPosition
138 {
139 gint32 x, y, width, height;
140 DwHPosition hpos;
141 DwVPosition vpos;
142 };
143
144
145 struct _DwContent
146 {
147 DwContentType type;
148 gboolean space;
149 union {
150 char *text;
151 DwWidget *widget;
152 char *anchor;
153 gint break_space;
154 } data;
155 };
156
157 struct _DwIterator
158 {
159 DwWidget *widget;
160 gint mask;
161
162 /* the current data, after first call of next */
163 DwContent content;
164
165 /* For simplicity, static stuff is put into the structure. */
166
167 /*
168 * Move iterator forward and store content it. Returns TRUE on
169 * success.
170 */
171 gboolean (*next) (DwIterator*);
172
173 /*
174 * Move iterator backward and store content it. Returns TRUE on
175 * success.
176 */
177 gboolean (*prev) (DwIterator*);
178
179 /*
180 * Highlight a part of the current content. Unhighlight the current
181 * content by passing -1 as start. For text, start and end define the
182 * characters, otherwise, the region is defined as [0, 1], i.e. for
183 * highlighting a whole DwContent, pass 0 and >= 1.
184 */
185 void (*highlight) (DwIterator*,
186 gint start,
187 gint end,
188 DwHighlightLayer layer);
189
190 /*
191 * Return the region, which a part of the item, the iterator points on,
192 * allocates. The parameters start and end have the same meaning as in
193 * DwIterator::highlight().
194 */
195 void (*get_allocation) (DwIterator*,
196 gint start,
197 gint end,
198 DwAllocation*);
199 /*
200 * Create an exact copy of the iterator, which then can be used
201 * independantly of the original one.
202 */
203 DwIterator* (*clone) (DwIterator*);
204
205 /*
206 * Return a value < 0, if first iterator is smaller, > 0, if it is
207 * greater, or 0 for equal iterators. The implementation may assume
208 * that both iterators belong to the same widget.
209 */
210 gint (*compare) (DwIterator*,
211 DwIterator*);
212
213 /*
214 * Free memory of iterator.
215 */
216 void (*free) (DwIterator*);
217 };
218
219 /* This iterator type is quite commonly used. */
220 struct _DwIteratorInt
221 {
222 DwIterator it;
223 int pos;
224 };
225
226 struct _DwIteratorText
227 {
228 DwIterator it;
229 gchar *text;
230 };
231
232 struct _DwWidget
233 {
234 GtkObject object;
235
236 /* the parent widget, NULL for top-level widgets */
237 DwWidget *parent;
238
239 /* This value is defined by the parent widget, and used for incremential
240 * resizing. See Dw.txt for an explanation. */
241 gint parent_ref;
242
243 /* the viewport in which the widget is shown */
244 GtkWidget *viewport;
245
246 /* see definition at the beginning */
247 DwWidgetFlags flags;
248
249 /* the current allocation: size and position, always relative to the
250 * scrolled area! */
251 DwAllocation allocation;
252
253 /* a_Dw_widget_size_request stores the result of the last call of
254 * Dw_xxx_size_request here, don't read this directly, but call
255 * a_Dw_widget_size_request. */
256 DwRequisition requisition;
257
258 /* analogue to requisition */
259 DwExtremes extremes;
260
261 GdkCursor *cursor; /* todo: move this to style */
262 DwStyle *style;
263
264 /* See p_Dw_widget_set_bg_color(). */
265 DwStyleColor *bg_color;
266
267 /* See a_Dw_widget_set_button_sensitive(). */
268 gboolean button_sensitive;
269 gboolean button_sensitive_set;
270
271 /* A temporary pixmap used for clipping. See p_Dw_widget_will_clip(). */
272 GdkPixmap *clip_pixmap;
273 };
274
275
276 struct _DwWidgetClass
277 {
278 GtkObjectClass parent_class;
279
280 void (*size_request) (DwWidget *widget,
281 DwRequisition *requisition);
282 void (*get_extremes) (DwWidget *widget,
283 DwExtremes *extremes);
284 void (*size_allocate) (DwWidget *widget,
285 DwAllocation *allocation);
286 void (*mark_size_change) (DwWidget *widget,
287 gint ref);
288 void (*mark_extremes_change) (DwWidget *widget,
289 gint ref);
290 void (*set_width) (DwWidget *widget,
291 gint32 width);
292 void (*set_ascent) (DwWidget *widget,
293 gint32 ascent);
294 void (*set_descent) (DwWidget *widget,
295 gint32 descent);
296 void (*draw) (DwWidget *widget,
297 DwRectangle *area,
298 GdkEventExpose *event);
299
300 gboolean (*button_press_event) (DwWidget *widget,
301 gint32 x,
302 gint32 y,
303 GdkEventButton *event);
304 gboolean (*button_release_event) (DwWidget *widget,
305 gint32 x,
306 gint32 y,
307 GdkEventButton *event);
308 gboolean (*motion_notify_event) (DwWidget *widget,
309 gint32 x,
310 gint32 y,
311 GdkEventMotion *event);
312 gboolean (*enter_notify_event) (DwWidget *widget,
313 DwWidget *last_widget,
314 GdkEventMotion *event);
315 gboolean (*leave_notify_event) (DwWidget *widget,
316 DwWidget *next_widget,
317 GdkEventMotion *event);
318
319 DwIterator* (*iterator) (DwWidget*,
320 gint32 mask,
321 gboolean at_end);
322 };
323
324
325 GtkType a_Dw_widget_get_type (void);
326
327 void p_Dw_widget_size_request (DwWidget *widget,
328 DwRequisition *requisition);
329 void p_Dw_widget_get_extremes (DwWidget *widget,
330 DwExtremes *extremes);
331 void p_Dw_widget_size_allocate (DwWidget *widget,
332 DwAllocation *allocation);
333 void p_Dw_widget_set_width (DwWidget *widget,
334 gint32 width);
335 void p_Dw_widget_set_ascent (DwWidget *widget,
336 gint32 ascent);
337 void p_Dw_widget_set_descent (DwWidget *widget,
338 gint32 descent);
339 void p_Dw_widget_draw (DwWidget *widget,
340 DwRectangle *area,
341 GdkEventExpose *event);
342
343 void a_Dw_widget_set_style (DwWidget *widget,
344 DwStyle *style);
345 void a_Dw_widget_set_cursor (DwWidget *widget,
346 GdkCursor *cursor);
347
348 void a_Dw_widget_set_button_sensitive (DwWidget *widget,
349 gboolean button_sensitive);
350
351 DwWidget *a_Dw_widget_get_toplevel (DwWidget *widget);
352
353 void a_Dw_widget_scroll_to (DwWidget *widget,
354 DwHPosition hpos,
355 DwVPosition vpos,
356 gint32 x,
357 gint32 y,
358 gint32 width,
359 gint32 height);
360
361 /* iterators */
362 DwIterator* a_Dw_widget_iterator (DwWidget *widget,
363 gint mask,
364 gboolean at_end);
365 #define a_Dw_iterator_next(it) ((it) ? (it)->next(it) : FALSE)
366 #define a_Dw_iterator_prev(it) ((it) ? (it)->prev(it) : FALSE)
367 #define a_Dw_iterator_highlight(it, s, e, l) ((it) ? \
368 (it)->highlight(it, s, e, l) : (void)0)
369 #define a_Dw_iterator_get_allocation(it, s, e, a) \
370 ((it) ? \
371 (it1)->get_allocation(it, s, e, a)\
372 : (void)0)
373 #define a_Dw_iterator_clone(it) ((it) ? (it)->clone(it) : NULL)
374 #define a_Dw_iterator_compare(it1, it2) ((it1)->compare(it1, it2))
375 #define a_Dw_iterator_free(it) ((it) ? (it)->free(it) : (void)0)
376
377 /* for convenience */
378 #define a_Dw_iterator_unhighlight(it, l) ((it) ? \
379 (it)->highlight(it, -1, -1, l) : (void)0)
380 void a_Dw_iterator_scroll_to (DwIterator *it1,
381 DwIterator *it2,
382 gint start,
383 gint end,
384 DwHPosition hpos,
385 DwVPosition vpos);
386
387 DwIterator* p_Dw_iterator_clone_std (DwIterator *it);
388 void p_Dw_iterator_free_std (DwIterator *it);
389 void p_Dw_iterator_highlight_std (DwIterator *it,
390 gint from,
391 gint start,
392 DwHighlightLayer layer);
393
394 void p_Dw_iterator_get_allocation_std_only_widgets (DwIterator *it,
395 gint start,
396 gint end,
397 DwAllocation
398 *allocation);
399
400 DwIterator* p_Dw_iterator_clone_std_int (DwIterator *it);
401 gint p_Dw_iterator_compare_std_int (DwIterator *it1,
402 DwIterator *it2);
403
404 /* for debugging */
405 gchar* a_Dw_content_text (DwContent *content);
406 gchar* a_Dw_content_html (DwContent *content);
407 gchar* a_Dw_iterator_text (DwIterator *it);
408 void a_Dw_widget_print_tree (DwWidget *widget);
409
410 DwIterator* p_Dw_widget_text_iterator (DwWidget* widget,
411 gint32 mask,
412 gboolean at_end,
413 gchar *text);
414 gboolean p_Dw_iterator_text_next (DwIterator *it);
415 gboolean p_Dw_iterator_text_prev (DwIterator *it);
416 DwIterator* p_Dw_iterator_text_clone (DwIterator *it);
417 gint p_Dw_iterator_text_compare (DwIterator *it1,
418 DwIterator *it2);
419 void p_Dw_iterator_text_get_allocation (DwIterator *it,
420 gint start,
421 gint end,
422 DwAllocation *allocation);
423
424 /* Only for Dw module */
425 gint p_Dw_widget_intersect (DwWidget *widget,
426 DwRectangle *area,
427 DwRectangle *intersection);
428 void p_Dw_widget_set_parent (DwWidget *widget,
429 DwWidget *parent);
430
431 gint32 p_Dw_widget_x_viewport_to_world (DwWidget *widget,
432 gint16 viewport_x);
433 gint32 p_Dw_widget_y_viewport_to_world (DwWidget *widget,
434 gint16 viewport_y);
435 gint16 p_Dw_widget_x_world_to_viewport (DwWidget *widget,
436 gint32 world_x);
437 gint16 p_Dw_widget_y_world_to_viewport (DwWidget *widget,
438 gint32 world_y);
439
440 gint Dw_widget_mouse_event (DwWidget *widget,
441 GtkWidget *viewwidget,
442 gint32 x,
443 gint32 y,
444 GdkEvent *event);
445 void Dw_widget_update_cursor (DwWidget *widget);
446 void p_Dw_widget_queue_draw (DwWidget *widget);
447 void p_Dw_widget_queue_draw_area (DwWidget *widget,
448 gint32 x,
449 gint32 y,
450 gint32 width,
451 gint32 height);
452 void p_Dw_widget_queue_resize (DwWidget *widget,
453 gint ref,
454 gboolean extremes_changed);
455 void p_Dw_widget_will_clip (DwWidget *widget);
456
457 void p_Dw_widget_set_bg_color (DwWidget *widget,
458 DwStyleColor *color);
459 DwStyleColor* p_Dw_widget_get_bg_color(DwWidget *widget);
460
461 /* Wrappers for Dw_style_draw_box */
462 void p_Dw_widget_draw_box (DwWidget *widget,
463 DwStyle *style,
464 DwRectangle *area,
465 gint32 x,
466 gint32 y,
467 gint32 width,
468 gint32 height,
469 gboolean inverse);
470 void p_Dw_widget_draw_widget_box (DwWidget *widget,
471 DwRectangle *area,
472 gboolean inverse);
473
474 void p_Dw_widget_draw_selected (DwWidget *widget,
475 DwRectangle *area);
476
477
478 #ifdef __cplusplus
479 }
480 #endif /* __cplusplus */
481
482 #endif /* __DW_WIDGET_H__ */
0 /*
1 * File: findtext.c
2 *
3 * Copyright 2002 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This module contains all the functionality for finding text, based
13 * on DwWordIterator.
14 *
15 * Search string semantics: "foo" searches for a word containing
16 * "foo", " foo" for a word beginning with "foo", "foo " for a word
17 * ending with "foo", and " foo " for the word "foo" itself. Similar,
18 * if you specify several words, spaces at the beginning and at the
19 * end will be treated in the same way.
20 *
21 * Searching is done in a slight variant of Knuth-Morris-Pratt, with
22 * words instead of characters, and an extended comparison, via
23 * Findtext_compare, both in the construction of the next table
24 * (Findtext_key_new), and the actual searching (Findtext_search0).
25 * This works, since other comparison that FINDTEXT_FULL are only
26 * allowed at the beginning (FINDTEXT_RIGHT), and at the end
27 * (FINDTEXT_LEFT); they have following effects:
28 *
29 * - Constructing the next table (Findtext_key_new): In
30 * comparisons, the first word is only used as the "needle", so
31 * there may be a state for which nexttab has the value 0
32 * (nexttab[0] itself is always -1). In Findtext_search0, this
33 * will cause the comparison correctly start again with the
34 * second word, since the word in the text before, which is
35 * /identical/ with the respective word in the key, so contains
36 * the first word in the key. The last word is never used in
37 * Findtext_key_new.
38 *
39 * - Actual searching (Findtext_search0): Since nexttab[0] is
40 * always -1, a failed comparison will restart the search. The
41 * state, which is switched to after the comparison with the last
42 * word failed depends only on the words before.
43 */
44
45 /*
46 * todo: If a one-word key is found twice in a word, the second
47 * occurence is ignored (a side-effect of word-by-word
48 * searching).
49 */
50
51 #include "msg.h"
52 #include "findtext.h"
53 #include "misc.h"
54 #include "debug.h"
55 #include <string.h>
56 #include <ctype.h>
57
58 /*
59 * Searches needle in haystack, according to type:
60 *
61 * FINDTEXT_FULL Needle equals haystack.
62 * FINDTEXT_FREE Haystack contains needle.
63 * FINDTEXT_LEFT Needle is the left part of haystack.
64 * FINDTEXT_RIGHT Needle is the right part of haystack.
65 *
66 * Returns pointer of occurrence in haystack, or NULL.
67 */
68 static gchar *Findtext_compare (gchar *haystack, gchar *needle,
69 FindtextPatternType type,
70 gboolean case_sens)
71 {
72 gchar *p;
73
74 if (case_sens)
75 switch (type) {
76 case FINDTEXT_FULL:
77 return (strcmp (haystack, needle) == 0) ? haystack : NULL;
78 case FINDTEXT_FREE:
79 return strstr (haystack, needle);
80 case FINDTEXT_LEFT:
81 return (strncmp (haystack, needle, strlen(needle)) == 0) ?
82 haystack : NULL;
83 case FINDTEXT_RIGHT:
84 p = haystack + strlen (haystack) - strlen (needle);
85 return (strcmp (p, needle) == 0) ? p : NULL;
86 }
87 else
88 switch (type) {
89 case FINDTEXT_FULL:
90 return (g_strcasecmp (haystack, needle) == 0) ? haystack : NULL;
91 case FINDTEXT_FREE:
92 return a_Misc_stristr (haystack, needle);
93 case FINDTEXT_LEFT:
94 return (g_strncasecmp (haystack, needle, strlen(needle)) == 0) ?
95 haystack : NULL;
96 case FINDTEXT_RIGHT:
97 p = haystack + strlen (haystack) - strlen (needle);
98 return (g_strcasecmp (p, needle) == 0) ? p : NULL;
99 }
100
101 /* compiler happiness */
102 return FALSE;
103 }
104
105 /*
106 * Creates a new key from a string.
107 */
108 static FindtextKey *Findtext_key_new (gchar *string, gboolean case_sens)
109 {
110 FindtextKey *key;
111 gint i, j, *splitpos;
112 gboolean first_spaces, last_spaces;
113
114 key = g_new (FindtextKey, 1);
115 splitpos = a_Misc_strsplitpos (string, " \t\n");
116
117 if (splitpos[0] == -1) {
118 /* Only spaces in the key. Construct a key with the words
119 * <empty-right> <space> <empty-left>, which will find spaces. */
120 key->len = 2;
121 key->words = g_new (gchar*, key->len);
122 key->types = g_new (FindtextPatternType, key->len);
123 key->nexttab = g_new (gint, key->len);
124
125 key->words[0] = g_strdup ("");
126 key->words[1] = g_strdup ("");
127 key->nexttab[0] = key->nexttab[1] = -1; /* Actually not needed, since
128 * comparison always succeeds. */
129 key->types[0] = FINDTEXT_RIGHT;
130 key->types[1] = FINDTEXT_LEFT;
131 } else {
132 first_spaces = (string[0] == ' ');
133 last_spaces = (string[strlen(string) - 1] == ' ');
134
135 key->len = 0;
136 while (splitpos[2 * key->len] != -1)
137 key->len++;
138
139 key->words = g_new (gchar*, key->len);
140 for (i = 0; i < key->len; i++)
141 key->words[i] =
142 a_Misc_strpdup (string, splitpos[2 * i], splitpos[2 * i + 1]);
143
144 key->types = g_new (FindtextPatternType, key->len);
145 key->nexttab = g_new (gint, key->len);
146
147 if (key->len == 1) {
148 key->nexttab[0] = -1;
149 if (first_spaces && last_spaces)
150 key->types[0] = FINDTEXT_FULL;
151 else if (!first_spaces && !last_spaces)
152 key->types[0] = FINDTEXT_FREE;
153 else if (first_spaces && !last_spaces)
154 key->types[0] = FINDTEXT_LEFT;
155 else /*if (!first_spaces && last_spaces)*/
156 key->types[0] = FINDTEXT_RIGHT;
157 } else {
158 for (i = 0; i < key->len; i++)
159 key->types[i] = FINDTEXT_FULL;
160
161 /* Begin and end. */
162 if (!first_spaces)
163 key->types[0] = FINDTEXT_RIGHT;
164 if (!last_spaces)
165 key->types[key->len - 1] = FINDTEXT_LEFT;
166
167 /* Build the next table. */
168 i = 0;
169 j = -1;
170 key->nexttab[0] = -1;
171
172 do {
173 if (j == -1 || Findtext_compare (key->words[i], key->words[j],
174 key->types[j], case_sens)) {
175 i++;
176 j++;
177 key->nexttab[i] = j;
178 _MSG ("nexttab[%d] = %d\n", i, j);
179 } else {
180 j = key->nexttab[j];
181 }
182 } while (i < key->len - 1);
183 }
184 }
185
186 g_free (splitpos);
187 return key;
188 }
189
190 /*
191 * Frees the memory allocated by a key.
192 */
193 static void Findtext_key_destroy (FindtextKey *key)
194 {
195 int i;
196 for (i = 0; i < key->len; i++)
197 g_free (key->words[i]);
198 g_free (key->words);
199 g_free (key->types);
200 g_free (key->nexttab);
201 g_free (key);
202 }
203
204
205
206 /*
207 * Creates a new findtext state. The widget must be set by
208 * a_Findtext_state_set_widget before the search can be started.
209 */
210 FindtextState *a_Findtext_state_new (void)
211 {
212 FindtextState *fts;
213
214 fts = g_new0 (FindtextState, 1);
215 DBG_OBJ_CREATE (fts, "FindtextState");
216 return fts;
217 }
218
219
220 /*
221 * Frees the memory allocated by a findtext state.
222 */
223 void a_Findtext_state_destroy (FindtextState *state)
224 {
225 if (state->key)
226 Findtext_key_destroy (state->key);
227 g_free (state->keystr);
228 if (state->iterator)
229 a_Dw_word_iterator_free (state->iterator);
230 if (state->hl_iterator)
231 a_Dw_word_iterator_free (state->hl_iterator);
232 g_free (state);
233 }
234
235
236 /*
237 * Sets the widget to search in. Widget may be NULL, in which case
238 * a_Findtext_search will always return FINDTEXT_NOT_FOUND.
239 */
240 void a_Findtext_state_set_widget (FindtextState *state,
241 DwWidget *widget)
242 {
243 state->widget = widget;
244 if (state->iterator)
245 a_Dw_word_iterator_free (state->iterator);
246 state->iterator = NULL;
247 if (state->hl_iterator)
248 a_Dw_word_iterator_free (state->hl_iterator);
249 state->hl_iterator = NULL;
250 /* A widget change will restart the search. */
251 if (state->key)
252 Findtext_key_destroy (state->key);
253 state->key = NULL;
254 g_free (state->keystr);
255 state->keystr = NULL;
256 }
257
258 static gboolean Findtext_search0 (FindtextState *state,
259 gboolean case_sens)
260 {
261 int i, j;
262 gboolean nextit;
263 gchar *p = NULL;
264
265 if (state->iterator->word == NULL)
266 return FALSE;
267
268 j = 0;
269 nextit = TRUE;
270
271 do {
272 if (j == -1 || (p = Findtext_compare (state->iterator->word,
273 state->key->words[j],
274 state->key->types[j],
275 case_sens))) {
276 if (j == 0)
277 state->first_hl_start = p - state->iterator->word;
278 if (j == state->key->len - 1) {
279 state->last_hl_end = strlen (state->key->words[j]);
280 if (j == 0)
281 state->last_hl_end += state->first_hl_start;
282 }
283
284 j++;
285 nextit = a_Dw_word_iterator_next (state->iterator);
286 } else
287 j = state->key->nexttab[j];
288 } while (nextit && j < state->key->len);
289
290 if (j >= state->key->len) {
291 /* Go back to where the word was found. */
292 for (i = 0; i < state->key->len; i++)
293 a_Dw_word_iterator_prev (state->iterator);
294 return TRUE;
295 } else
296 return FALSE;
297 }
298
299
300
301 FindtextResult a_Findtext_search (FindtextState *state,
302 gchar *str,
303 gboolean case_sens)
304 {
305 int i;
306 gboolean new_key = FALSE, was_highlighted = FALSE, first_trial;
307 FindtextResult result2;
308
309 if (state->widget == NULL)
310 return FINDTEXT_NOT_FOUND;
311
312 /* If there is still highlighted text */
313 if (state->hl_iterator) {
314 was_highlighted = TRUE;
315 for (i = 0; i < state->key->len - 1; i++) {
316 a_Dw_word_iterator_unhighlight (state->hl_iterator,
317 DW_HIGHLIGHT_FINDTEXT);
318 a_Dw_word_iterator_next (state->hl_iterator);
319 }
320 a_Dw_word_iterator_unhighlight (state->hl_iterator,
321 DW_HIGHLIGHT_FINDTEXT);
322 a_Dw_word_iterator_free (state->hl_iterator);
323 state->hl_iterator = NULL;
324 }
325
326 /* If the key (or the widget) changes (including case sensitivity),
327 the search is started from the beginning. */
328 if (state->keystr == NULL || state->case_sens != case_sens ||
329 strcmp(state->keystr, str) != 0) {
330 new_key = TRUE;
331
332 g_free (state->keystr);
333 state->keystr = g_strdup (str);
334 state->case_sens = case_sens;
335
336 if (state->key != NULL)
337 Findtext_key_destroy (state->key);
338 state->key = Findtext_key_new (str, case_sens);
339
340 if (state->iterator)
341 a_Dw_word_iterator_free (state->iterator);
342 state->iterator = a_Dw_word_iterator_new (state->widget);
343 a_Dw_word_iterator_next (state->iterator);
344 }
345
346 first_trial = !was_highlighted || new_key;
347
348 if (Findtext_search0 (state, case_sens)) {
349 /* Highlighlighting is done with a clone. */
350 state->hl_iterator = a_Dw_word_iterator_clone (state->iterator);
351 if (state->key->len == 1) {
352 a_Dw_word_iterator_scroll_to (state->iterator, state->iterator,
353 state->first_hl_start,
354 state->last_hl_end,
355 DW_HPOS_INTO_VIEW, DW_VPOS_CENTER);
356 a_Dw_word_iterator_highlight (state->hl_iterator,
357 state->first_hl_start,
358 state->last_hl_end,
359 DW_HIGHLIGHT_FINDTEXT);
360 DBG_MSGF (state, "findtext", 0, "highlighting %s from %d to %d",
361 state->hl_iterator->word,
362 state->first_hl_start, state->last_hl_end);
363 } else {
364 a_Dw_word_iterator_highlight (state->hl_iterator,
365 state->first_hl_start,
366 strlen (state->hl_iterator->word) + 1,
367 DW_HIGHLIGHT_FINDTEXT);
368 DBG_MSGF (state, "findtext", 0, "highlighting '%s' from %d to %d",
369 state->hl_iterator->word, state->first_hl_start,
370 strlen (state->hl_iterator->word) + 1);
371 a_Dw_word_iterator_next (state->hl_iterator);
372 for (i = 1; i < state->key->len - 1; i++) {
373 a_Dw_word_iterator_highlight (state->hl_iterator, 0,
374 strlen (state->hl_iterator->word)
375 + 1,
376 DW_HIGHLIGHT_FINDTEXT);
377 DBG_MSGF (state, "findtext", 0, "highlighting '%s' from %d to %d",
378 state->hl_iterator->word,
379 0, strlen (state->hl_iterator->word) + 1);
380 a_Dw_word_iterator_next (state->hl_iterator);
381 }
382 a_Dw_word_iterator_highlight (state->hl_iterator, 0,
383 state->last_hl_end,
384 DW_HIGHLIGHT_FINDTEXT);
385 DBG_MSGF (state, "findtext", 0, "highlighting '%s' from %d to %d",
386 state->hl_iterator->word, 0, state->last_hl_end);
387
388 a_Dw_word_iterator_scroll_to (state->iterator, state->hl_iterator,
389 state->first_hl_start,
390 state->last_hl_end,
391 DW_HPOS_INTO_VIEW, DW_VPOS_CENTER);
392 }
393 for (i = 0; i < state->key->len - 1; i++)
394 a_Dw_word_iterator_prev (state->hl_iterator);
395
396 /* The search will continue from the word after the found position. */
397 a_Dw_word_iterator_next (state->iterator);
398
399 return FINDTEXT_SUCCESS;
400 }
401
402 if (first_trial)
403 return FINDTEXT_NOT_FOUND;
404 else {
405 /* Nothing found anymore, reset the state for the next trial. */
406 a_Dw_word_iterator_free (state->iterator);
407 state->iterator = a_Dw_word_iterator_new (state->widget);
408 a_Dw_word_iterator_next (state->iterator);
409
410 /* We expect a success. */
411 result2 = a_Findtext_search (state, str, case_sens);
412 g_assert (result2 == FINDTEXT_SUCCESS);
413 return FINDTEXT_RESTART;
414 }
415 }
416
417 /*
418 * This function is called when the user closes the "find text" dialog.
419 */
420 void a_Findtext_reset_search (FindtextState *state)
421 {
422 int i;
423
424 g_free (state->keystr);
425 state->keystr = NULL;
426
427 if (state->hl_iterator) {
428 for (i = 0; i < state->key->len - 1; i++) {
429 a_Dw_word_iterator_unhighlight (state->hl_iterator,
430 DW_HIGHLIGHT_FINDTEXT);
431 a_Dw_word_iterator_next (state->hl_iterator);
432 }
433 a_Dw_word_iterator_unhighlight (state->hl_iterator,
434 DW_HIGHLIGHT_FINDTEXT);
435 a_Dw_word_iterator_free (state->hl_iterator);
436 state->hl_iterator = NULL;
437 }
438 }
0 #ifndef __FINDTEXT_H__
1 #define __FINDTEXT_H__
2
3 #include "dw_ext_iterator.h"
4
5 /*
6 * Return values of a_Findtext_search.
7 */
8 typedef enum {
9 FINDTEXT_SUCCESS, /* The next occurance of the pattern has been
10 found. */
11 FINDTEXT_RESTART, /* There is no further occurance of the pattern,
12 instead, the first occurance has been selected. */
13 FINDTEXT_NOT_FOUND /* The patten does not at all occur in the text. */
14 } FindtextResult;
15
16 /*
17 * See Findtext_compare for an explanation.
18 */
19 typedef enum {
20 FINDTEXT_FULL,
21 FINDTEXT_FREE,
22 FINDTEXT_LEFT,
23 FINDTEXT_RIGHT
24 } FindtextPatternType;
25
26 typedef struct _FindtextState FindtextState;
27 typedef struct _FindtextKey FindtextKey;
28
29 /*
30 * This structure is associated by the GtkDwViewport.
31 */
32 struct _FindtextState
33 {
34 FindtextKey *key;
35 gchar *keystr;
36 gboolean case_sens;
37 DwWidget *widget; /* The top of the widget tree, in which
38 the search is done. From this, the
39 iterator will be constructed. */
40 DwWordIterator *iterator; /* The position from where the next search
41 will start. */
42 DwWordIterator *hl_iterator; /* The position from where key->len words
43 are highlighted. */
44 gint first_hl_start, last_hl_end;
45 };
46
47 struct _FindtextKey
48 {
49 gint len;
50 gchar **words;
51 FindtextPatternType *types;
52 gint *nexttab;
53 };
54
55 FindtextState* a_Findtext_state_new (void);
56 void a_Findtext_state_destroy (FindtextState *state);
57 void a_Findtext_state_set_widget (FindtextState *state,
58 DwWidget *widget);
59
60 FindtextResult a_Findtext_search (FindtextState *state,
61 gchar *str,
62 gboolean case_sens);
63 void a_Findtext_reset_search (FindtextState *state);
64
65 #endif /* __FINDTEXT_H__ */
0 /*
1 * File: gif.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 2000-2002 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * The GIF decoder for dillo. It is responsible for decoding GIF data
14 * and transferring it to the dicache.
15 */
16
17
18 /* Notes 13 Oct 1997 --RLL
19 *
20 * Today, just for the hell of it, I implemented a new decoder from
21 * scratch. It's oriented around pushing bytes, while the old decoder
22 * was based around reads which may suspend. There were basically
23 * three motivations.
24 *
25 * 1. To increase the speed.
26 *
27 * 2. To fix some bugs I had seen, most likely due to suspension.
28 *
29 * 3. To make sure that the code had no buffer overruns or the like.
30 *
31 * 4. So that the code could be released under a freer license.
32 *
33 * Let's see how we did on speed. I used a large image for testing
34 * (fvwm95-2.gif).
35 *
36 * The old decoder spent a total of about 1.04 seconds decoding the
37 * image. Another .58 seconds went into Image_line, almost
38 * entirely conversion from colormap to RGB.
39 *
40 * The new decoder spent a total of 0.46 seconds decoding the image.
41 * However, the time for Image_line went up to 1.01 seconds.
42 * Thus, even though the decoder seems to be about twice as fast,
43 * the net gain is pretty minimal. Could this be because of cache
44 * effects?
45 *
46 * Lessons learned: The first, which I keep learning over and over, is
47 * not to try to optimize too much. It doesn't work. Just keep things
48 * simple.
49 *
50 * Second, it seems that the colormap to RGB conversion is really a
51 * significant part of the overall time. It's possible that going
52 * directly to 16 bits would help, but that's optimization again :)
53 */
54
55
56 /* todo:
57 * + Make sure to handle error cases gracefully (including aborting the
58 * connection, if necessary).
59 */
60
61 #include <config.h>
62 #ifdef ENABLE_GIF
63
64 #include <stdio.h> /* for sprintf */
65 #include <string.h> /* for memcpy and memmove */
66
67 #include <gtk/gtk.h>
68 #include "msg.h"
69 #include "image.h"
70 #include "web.h"
71 #include "cache.h"
72 #include "dicache.h"
73 #include "prefs.h"
74
75 #define DEBUG_LEVEL 6
76 #include "debug.h"
77
78 #define INTERLACE 0x40
79 #define LOCALCOLORMAP 0x80
80
81 #define LM_to_uint(a,b) ((((guchar)b)<<8)|((guchar)a))
82
83 #define MAXCOLORMAPSIZE 256
84 #define MAX_LWZ_BITS 12
85
86
87 typedef struct _DilloGif {
88 DilloImage *Image;
89 DilloUrl *url;
90 gint version;
91
92 gint state;
93 size_t Start_Ofs;
94 guint Flags;
95
96 guchar input_code_size;
97 guchar *linebuf;
98 gint pass;
99
100 guint y;
101
102 /* state for lwz_read_byte */
103 gint code_size;
104
105 /* The original GifScreen from giftopnm */
106 guint Width;
107 guint Height;
108 size_t ColorMap_ofs;
109 guint ColorResolution;
110 guint NumColors;
111 gint Background;
112 guint spill_line_index;
113 #if 0
114 guint AspectRatio; /* AspectRatio (not used) */
115 #endif
116
117 /* Gif89 extensions */
118 gint transparent;
119 #if 0
120 /* None are used: */
121 gint delayTime;
122 gint inputFlag;
123 gint disposal;
124 #endif
125
126 /* state for the new push-oriented decoder */
127 gint packet_size; /* The amount of the data block left to process */
128 guint window;
129 gint bits_in_window;
130 guint last_code; /* Last "compressed" code in the look up table */
131 guint line_index;
132 guchar **spill_lines;
133 gint num_spill_lines_max;
134 gint length[(1 << MAX_LWZ_BITS) + 1];
135 gint code_and_byte[(1 << MAX_LWZ_BITS) + 1];
136 } DilloGif;
137
138 /* Some invariants:
139 *
140 * last_code <= code_mask
141 *
142 * code_and_byte is stored packed: (code << 8) | byte
143 */
144
145
146 /*
147 * Forward declarations
148 */
149 static void Gif_write(DilloGif *gif, void *Buf, guint BufSize);
150 static void Gif_close(DilloGif *gif, CacheClient_t *Client);
151 static size_t Gif_process_bytes(DilloGif *gif, const guchar *buf,
152 gint bufsize, void *Buf);
153 static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, gint version);
154 static void Gif_callback(int Op, CacheClient_t *Client);
155
156 /* exported function */
157 DwWidget *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
158 void **Data);
159
160
161 /*
162 * MIME handler for "image/gif" type
163 * (Sets Gif_callback as cache-client)
164 */
165 DwWidget *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
166 void **Data)
167 {
168 DilloWeb *web = Ptr;
169 DICacheEntry *DicEntry;
170
171 if ( !web->Image )
172 web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
173 /* todo: get the backgound color from the parent widget -- Livio. */
174
175 /* Add an extra reference to the Image (for dicache usage) */
176 a_Image_ref(web->Image);
177
178 DicEntry = a_Dicache_get_entry(web->url);
179 if ( !DicEntry ) {
180 /* Let's create an entry for this image... */
181 DicEntry = a_Dicache_add_entry(web->url);
182
183 /* ... and let the decoder feed it! */
184 *Data = Gif_new(web->Image, DicEntry->url, DicEntry->version);
185 *Call = (CA_Callback_t) Gif_callback;
186 } else {
187 /* Let's feed our client from the dicache */
188 a_Dicache_ref(DicEntry->url, DicEntry->version);
189 *Data = web->Image;
190 *Call = (CA_Callback_t) a_Dicache_callback;
191 }
192 return DW_WIDGET (web->Image->dw);
193 }
194
195 /*
196 * Create a new gif structure for decoding a gif into a RGB buffer
197 */
198 static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, gint version)
199 {
200 DilloGif *gif = g_malloc(sizeof(DilloGif));
201
202 gif->Image = Image;
203 gif->url = url;
204 gif->version = version;
205
206 gif->Flags = 0;
207 gif->state = 0;
208 gif->Start_Ofs = 0;
209 gif->linebuf = NULL;
210 gif->Background = -1;
211 gif->transparent = -1;
212 gif->num_spill_lines_max = 0;
213 gif->spill_lines = NULL;
214 gif->window = 0;
215 gif->packet_size = 0;
216 gif->ColorMap_ofs = 0;
217
218 return gif;
219 }
220
221 /*
222 * This function is a cache client, it receives data from the cache
223 * and dispatches it to the appropriate gif-processing functions
224 */
225 static void Gif_callback(int Op, CacheClient_t *Client)
226 {
227 if ( Op )
228 Gif_close(Client->CbData, Client);
229 else
230 Gif_write(Client->CbData, Client->Buf, Client->BufSize);
231 }
232
233 /*
234 * Receive and process new chunks of GIF image data
235 */
236 static void Gif_write(DilloGif *gif, void *Buf, guint BufSize)
237 {
238 guchar *buf;
239 gint bufsize, bytes_consumed;
240
241 /* Sanity checks */
242 if (!Buf || !gif->Image || BufSize == 0)
243 return;
244
245 buf = ((guchar *) Buf) + gif->Start_Ofs;
246 bufsize = BufSize - gif->Start_Ofs;
247
248 DEBUG_MSG(5, "Gif_write: %u bytes\n", BufSize);
249
250 /* Process the bytes in the input buffer. */
251 bytes_consumed = Gif_process_bytes(gif, buf, bufsize, Buf);
252
253 if (bytes_consumed < 1)
254 return;
255 gif->Start_Ofs += bytes_consumed;
256
257 DEBUG_MSG(5, "exit Gif_write, bufsize=%ld\n", (glong)bufsize);
258 }
259
260 /*
261 * Finish the decoding process (and free the memory)
262 */
263 static void Gif_close(DilloGif *gif, CacheClient_t *Client)
264 {
265 gint i;
266
267 DEBUG_MSG(5, "destroy gif %p\n", gif);
268
269 a_Dicache_close(gif->url, gif->version, Client);
270
271 g_free(gif->linebuf);
272
273 if (gif->spill_lines != NULL) {
274 for (i = 0; i < gif->num_spill_lines_max; i++)
275 g_free(gif->spill_lines[i]);
276 g_free(gif->spill_lines);
277 }
278 g_free(gif);
279 }
280
281
282 /* --- GIF Extensions ----------------------------------------------------- */
283
284 /*
285 * This reads a sequence of GIF data blocks.. and ignores them!
286 * Buf points to the first data block.
287 *
288 * Return Value
289 * 0 = There wasn't enough bytes read yet to read the whole datablock
290 * otherwise the size of the data blocks
291 */
292 static inline size_t Gif_data_blocks(const guchar *Buf, size_t BSize)
293 {
294 size_t Size = 0;
295
296 if (BSize < 1)
297 return 0;
298 while (Buf[0]) {
299 if (BSize <= (size_t)(Buf[0] + 1))
300 return 0;
301 Size += Buf[0] + 1;
302 BSize -= Buf[0] + 1;
303 Buf += Buf[0] + 1;
304 }
305 return Size + 1;
306 }
307
308 /*
309 * This is a GIF extension. We ignore it with this routine.
310 * Buffer points to just after the extension label.
311 *
312 * Return Value
313 * 0 -- block not processed
314 * otherwise the size of the extension label.
315 */
316 static inline size_t Gif_do_generic_ext(const guchar *Buf, size_t BSize)
317 {
318 size_t Size = Buf[0] + 1, DSize;
319
320 /* The Block size (the first byte) is supposed to be a specific size
321 * for each extension... we don't check.
322 */
323
324 if (Buf[0] > BSize)
325 return 0;
326 DSize = Gif_data_blocks(Buf + Size, BSize - Size);
327 if (!DSize)
328 return 0;
329 Size += DSize;
330 return Size <= BSize ? Size : 0;
331 }
332
333 /*
334 * ?
335 */
336 static inline size_t
337 Gif_do_gc_ext(DilloGif *gif, const guchar *Buf, size_t BSize)
338 {
339 /* Graphic Control Extension */
340 size_t Size = Buf[0] + 2;
341 guint Flags;
342
343 if (Size > BSize)
344 return 0;
345 Buf++;
346 Flags = Buf[0];
347
348 /* The packed fields */
349 #if 0
350 gif->disposal = (Buf[0] >> 2) & 0x7;
351 gif->inputFlag = (Buf[0] >> 1) & 0x1;
352
353 /* Delay time */
354 gif->delayTime = LM_to_uint(Buf[1], Buf[2]);
355 #endif
356
357 /* Transparent color index, may not be valid (unless flag is set) */
358 if ((Flags & 0x1)) {
359 gif->transparent = Buf[3];
360 }
361 return Size;
362 }
363
364 #define App_Ext (0xff)
365 #define Cmt_Ext (0xfe)
366 #define GC_Ext (0xf9)
367 #define Txt_Ext (0x01)
368
369 /*
370 * ?
371 * Return value:
372 * TRUE when the extension is over
373 */
374 static size_t Gif_do_extension(DilloGif *gif, guint Label,
375 const guchar *buf,
376 size_t BSize)
377 {
378 switch (Label) {
379 case GC_Ext: /* Graphics extension */
380 return Gif_do_gc_ext(gif, buf, BSize);
381
382 case Cmt_Ext: /* Comment extension */
383 return Gif_data_blocks(buf, BSize);
384
385 case Txt_Ext: /* Plain text Extension */
386 /* This extension allows (rcm thinks) the image to be rendered as text.
387 */
388 case App_Ext: /* Application Extension */
389 default:
390 return Gif_do_generic_ext(buf, BSize); /*Ignore Extension */
391 }
392 }
393
394 /* --- General Image Decoder ----------------------------------------------- */
395 /* Here begins the new push-oriented decoder. */
396
397 /*
398 * ?
399 */
400 static void Gif_lwz_init(DilloGif *gif)
401 {
402 gif->num_spill_lines_max = 1;
403 gif->spill_lines = g_malloc(sizeof(guchar *) * gif->num_spill_lines_max);
404
405 gif->spill_lines[0] = g_malloc(gif->Width);
406 gif->bits_in_window = 0;
407
408 /* First code in table = clear_code +1
409 * Last code in table = first code in table
410 * clear_code = (1<< input code size)
411 */
412 gif->last_code = (1 << gif->input_code_size) + 1;
413 memset(gif->code_and_byte, 0,
414 (1 + gif->last_code) * sizeof(gif->code_and_byte[0]));
415 gif->code_size = gif->input_code_size + 1;
416 gif->line_index = 0;
417 }
418
419 /*
420 * Send the image line to the dicache, also handling the interlacing.
421 */
422 static void Gif_emit_line(DilloGif *gif, const guchar *linebuf)
423 {
424 a_Dicache_write(gif->Image, gif->url, gif->version, linebuf, 0, gif->y);
425 if (gif->Flags & INTERLACE) {
426 switch (gif->pass) {
427 case 0:
428 case 1:
429 gif->y += 8;
430 break;
431 case 2:
432 gif->y += 4;
433 break;
434 case 3:
435 gif->y += 2;
436 break;
437 }
438 if (gif->y >= gif->Height) {
439 gif->pass++;
440 switch (gif->pass) {
441 case 1:
442 gif->y = 4;
443 break;
444 case 2:
445 gif->y = 2;
446 break;
447 case 3:
448 gif->y = 1;
449 break;
450 default:
451 /* arriving here is an error in the input image. */
452 gif->y = 0;
453 break;
454 }
455 }
456 } else {
457 if (gif->y < gif->Height)
458 gif->y++;
459 }
460 }
461
462 /*
463 * I apologize for the large size of this routine and the goto error
464 * construct - I almost _never_ do that. I offer the excuse of
465 * optimizing for speed.
466 *
467 * RCM -- busted these down into smaller subroutines... still very hard to
468 * read.
469 */
470
471
472 /*
473 * Decode the packetized lwz bytes
474 */
475 static void Gif_literal(DilloGif *gif, guint code)
476 {
477 gif->linebuf[gif->line_index++] = code;
478 if (gif->line_index >= gif->Width) {
479 Gif_emit_line(gif, gif->linebuf);
480 gif->line_index = 0;
481 }
482 gif->length[gif->last_code + 1] = 2;
483 gif->code_and_byte[gif->last_code + 1] = (code << 8);
484 gif->code_and_byte[gif->last_code] |= code;
485 }
486
487 /*
488 * ?
489 */
490 /* Profiling reveals over half the GIF time is spent here: */
491 static void Gif_sequence(DilloGif *gif, guint code)
492 {
493 guint o_index, o_size, orig_code;
494 guint sequence_length = gif->length[code];
495 guint line_index = gif->line_index;
496 gint num_spill_lines;
497 gint spill_line_index = gif->spill_line_index;
498 guchar *last_byte_ptr, *obuf;
499
500 gif->length[gif->last_code + 1] = sequence_length + 1;
501 gif->code_and_byte[gif->last_code + 1] = (code << 8);
502
503 /* We're going to traverse the sequence backwards. Thus,
504 * we need to allocate spill lines if the sequence won't
505 * fit entirely within the present scan line. */
506 if (line_index + sequence_length <= gif->Width) {
507 num_spill_lines = 0;
508 obuf = gif->linebuf;
509 o_index = line_index + sequence_length;
510 o_size = sequence_length - 1;
511 } else {
512 num_spill_lines = (line_index + sequence_length - 1) /
513 gif->Width;
514 o_index = (line_index + sequence_length - 1) % gif->Width + 1;
515 if (num_spill_lines > gif->num_spill_lines_max) {
516 /* Allocate more spill lines. */
517 spill_line_index = gif->num_spill_lines_max;
518 gif->num_spill_lines_max = num_spill_lines << 1;
519 gif->spill_lines = g_realloc(gif->spill_lines,
520 gif->num_spill_lines_max *
521 sizeof(guchar *));
522
523 for (; spill_line_index < gif->num_spill_lines_max;
524 spill_line_index++)
525 gif->spill_lines[spill_line_index] =
526 g_malloc(gif->Width);
527 }
528 spill_line_index = num_spill_lines - 1;
529 obuf = gif->spill_lines[spill_line_index];
530 o_size = o_index;
531 }
532 gif->line_index = o_index; /* for afterwards */
533
534 /* for fixing up later if last_code == code */
535 orig_code = code;
536 last_byte_ptr = obuf + o_index - 1;
537
538 /* spill lines are allocated, and we are clear to
539 * write. This loop does not write the first byte of
540 * the sequence, however (last byte traversed). */
541 while (sequence_length > 1) {
542 sequence_length -= o_size;
543 /* Write o_size bytes to
544 * obuf[o_index - o_size..o_index). */
545 for (; o_size > 0 && o_index > 0; o_size--) {
546 guint code_and_byte = gif->code_and_byte[code];
547
548 DEBUG_MSG(5, "%d ", gif->code_and_byte[code] & 255);
549
550 obuf[--o_index] = code_and_byte & 255;
551 code = code_and_byte >> 8;
552 }
553 /* Prepare for writing to next line. */
554 if (o_index == 0) {
555 if (spill_line_index > 0) {
556 spill_line_index--;
557 obuf = gif->spill_lines[spill_line_index];
558 o_size = gif->Width;
559 } else {
560 obuf = gif->linebuf;
561 o_size = sequence_length - 1;
562 }
563 o_index = gif->Width;
564 }
565 }
566 /* Ok, now we write the first byte of the sequence. */
567 /* We are sure that the code is literal. */
568 DEBUG_MSG(5, "%d", code);
569 obuf[--o_index] = code;
570 gif->code_and_byte[gif->last_code] |= code;
571
572 /* Fix up the output if the original code was last_code. */
573 if (orig_code == gif->last_code) {
574 *last_byte_ptr = code;
575 DEBUG_MSG(5, " fixed (%d)!", code);
576 }
577 DEBUG_MSG(5, "\n");
578
579 /* Output any full lines. */
580 if (gif->line_index >= gif->Width) {
581 Gif_emit_line(gif, gif->linebuf);
582 gif->line_index = 0;
583 }
584 if (num_spill_lines) {
585 if (gif->line_index)
586 Gif_emit_line(gif, gif->linebuf);
587 for (spill_line_index = 0;
588 spill_line_index < num_spill_lines - (gif->line_index ? 1 : 0);
589 spill_line_index++)
590 Gif_emit_line(gif, gif->spill_lines[spill_line_index]);
591 }
592 if (num_spill_lines) {
593 /* Swap the last spill line with the gif line, using
594 * linebuf as the swap temporary. */
595 guchar *linebuf = gif->spill_lines[num_spill_lines - 1];
596
597 gif->spill_lines[num_spill_lines - 1] = gif->linebuf;
598 gif->linebuf = linebuf;
599 }
600 gif->spill_line_index = spill_line_index;
601 }
602
603 /*
604 * ?
605 *
606 * Return Value:
607 * 2 -- quit
608 * 1 -- new last code needs to be done
609 * 0 -- okay, but reset the code table
610 * < 0 on error
611 * -1 if the decompression code was not in the lookup table
612 */
613 static gint Gif_process_code(DilloGif *gif, guint code, guint clear_code)
614 {
615
616 /* A short table describing what to do with the code:
617 * code < clear_code : This is uncompressed, raw data
618 * code== clear_code : Reset the decompression table
619 * code== clear_code+1: End of data stream
620 * code > clear_code+1: Compressed code; look up in table
621 */
622 if (code < clear_code) {
623 /* a literal code. */
624 DEBUG_MSG(5, "literal\n");
625 Gif_literal(gif, code);
626 return 1;
627 } else if (code >= clear_code + 2) {
628 /* a sequence code. */
629 if (code > gif->last_code)
630 return -1;
631 Gif_sequence(gif, code);
632 return 1;
633 } else if (code == clear_code) {
634 /* clear code. Resets the whole table */
635 DEBUG_MSG(5, "clear\n");
636 return 0;
637 } else {
638 /* end code. */
639 DEBUG_MSG(5, "end\n");
640 return 2;
641 }
642 }
643
644 /*
645 * ?
646 */
647 static gint Gif_decode(DilloGif *gif, const guchar *buf, size_t bsize)
648 {
649 /*
650 * Data block processing. The image stuff is a series of data blocks.
651 * Each data block is 1 to 256 bytes long. The first byte is the length
652 * of the data block. 0 == the last data block.
653 */
654 size_t bufsize, packet_size;
655 guint clear_code;
656 guint window;
657 gint bits_in_window;
658 guint code;
659 gint code_size;
660 guint code_mask;
661
662 bufsize = bsize;
663
664 /* Want to get all inner loop state into local variables. */
665 packet_size = gif->packet_size;
666 window = gif->window;
667 bits_in_window = gif->bits_in_window;
668 code_size = gif->code_size;
669 code_mask = (1 << code_size) - 1;
670 clear_code = 1 << gif->input_code_size;
671
672 /* If packet size == 0, we are at the start of a data block.
673 * The first byte of the data block indicates how big it is (0 == last
674 * datablock)
675 * packet size is set to this size; it indicates how much of the data block
676 * we have left to process.
677 */
678 while (bufsize > 0) {
679 /* lwz_bytes is the number of remaining lwz bytes in the packet. */
680 gint lwz_bytes = MIN(packet_size, bufsize);
681
682 bufsize -= lwz_bytes;
683 packet_size -= lwz_bytes;
684 for (; lwz_bytes > 0; lwz_bytes--) {
685 /* printf ("%d ", *buf) would print the depacketized lwz stream. */
686
687 /* Push the byte onto the "end" of the window (MSB). The low order
688 * bits always come first in the LZW stream. */
689 window = (window >> 8) | (*buf++ << 24);
690 bits_in_window += 8;
691
692 while (bits_in_window >= code_size) {
693 /* Extract the code. The code is code_size (3 to 12) bits long,
694 * at the start of the window */
695 code = (window >> (32 - bits_in_window)) & code_mask;
696
697 DEBUG_MSG(5, "code = %d, ", code);
698
699 bits_in_window -= code_size;
700 switch (Gif_process_code(gif, code, clear_code)) {
701 case 1: /* Increment last code */
702 gif->last_code++;
703 /*gif->code_and_byte[gif->last_code+1]=0; */
704
705 if ((gif->last_code & code_mask) == 0) {
706 if (gif->last_code == (1 << MAX_LWZ_BITS))
707 gif->last_code--;
708 else {
709 code_size++;
710 code_mask = (1 << code_size) - 1;
711 }
712 }
713 break;
714
715 case 0: /* Reset codes size and mask */
716 gif->last_code = clear_code + 1;
717 code_size = gif->input_code_size + 1;
718 code_mask = (1 << code_size) - 1;
719 break;
720
721 case 2: /* End code... consume remaining data chunks..? */
722 goto error; /* Could clean up better? */
723 default:
724 printf("dillo_gif_decode: error!\n");
725 goto error;
726 }
727 }
728 }
729
730 /* We reach here if
731 * a) We have reached the end of the data block;
732 * b) we ran out of data before reaching the end of the data block
733 */
734 if (bufsize <= 0)
735 break; /* We are out of data; */
736
737 /* Start of new data block */
738 bufsize--;
739 if (!(packet_size = *buf++)) {
740 /* This is the "block terminator" -- the last data block */
741 gif->state = 999; /* BUG: should Go back to getting GIF blocks. */
742 break;
743 }
744 }
745
746 gif->packet_size = packet_size;
747 gif->window = window;
748 gif->bits_in_window = bits_in_window;
749 gif->code_size = code_size;
750 return bsize - bufsize;
751
752 error:
753 gif->state = 999;
754 return bsize - bufsize;
755 }
756
757 /*
758 * ?
759 */
760 static gint Gif_check_sig(DilloGif *gif, const guchar *ibuf, gint ibsize)
761 {
762 /* at beginning of file - read magic number */
763 if (ibsize < 6)
764 return 0;
765 if (strncmp(ibuf, "GIF", 3) != 0) {
766 gif->state = 999;
767 return 6;
768 }
769 if (strncmp(ibuf + 3, "87a", 3) != 0 &&
770 strncmp(ibuf + 3, "89a", 3) != 0) {
771 gif->state = 999;
772 return 6;
773 }
774 gif->state = 1;
775 return 6;
776 }
777
778 /* Read the color map
779 *
780 * Implements, from the spec:
781 * Global Color Table
782 * Local Color Table
783 */
784 static inline size_t
785 Gif_do_color_table(DilloGif *gif, void *Buf,
786 const guchar *buf, size_t bsize, size_t CT_Size)
787 {
788 size_t Size = 3 * (1 << (1 + CT_Size));
789
790 if (Size > bsize)
791 return 0;
792
793 gif->ColorMap_ofs = (gulong) buf - (gulong) Buf;
794 gif->NumColors = (1 << (1 + CT_Size));
795 return Size;
796 }
797
798 /*
799 * This implements, from the spec:
800 * <Logical Screen> ::= Logical Screen Descriptor [Global Color Table]
801 */
802 static size_t Gif_get_descriptor(DilloGif *gif, void *Buf,
803 const guchar *buf, gint bsize)
804 {
805
806 /* screen descriptor */
807 size_t Size = 7, /* Size of descriptor */
808 mysize; /* Size of color table */
809 guchar Flags;
810
811 if (bsize < 7)
812 return 0;
813 Flags = buf[4];
814
815 if (Flags & LOCALCOLORMAP) {
816 mysize = Gif_do_color_table(
817 gif, Buf, buf + 7, (size_t)bsize - 7, Flags & (size_t)0x7);
818 if (!mysize)
819 return 0;
820 Size += mysize; /* Size of the color table that follows */
821 gif->Background = buf[5];
822 }
823 /* gif->Width = LM_to_uint(buf[0], buf[1]);
824 gif->Height = LM_to_uint(buf[2], buf[3]); */
825 gif->ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
826 /* gif->AspectRatio = buf[6]; */
827
828 return Size;
829 }
830
831 /*
832 * This implements, from the spec:
833 * <Table-Based Image> ::= Image Descriptor [Local Color Table] Image Data
834 *
835 * ('Buf' points to just after the Image separator)
836 * we should probably just check that the local stuff is consistent
837 * with the stuff at the header. For now, we punt...
838 */
839 static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
840 const guchar *buf, size_t bsize)
841 {
842 guchar Flags;
843 size_t Size = 9 + 1; /* image descriptor size + first byte of image data */
844
845 if (bsize < 10)
846 return 0;
847
848 gif->Width = LM_to_uint(buf[4], buf[5]);
849 gif->Height = LM_to_uint(buf[6], buf[7]);
850 gif->linebuf = g_malloc(gif->Width);
851
852 a_Dicache_set_parms(gif->url, gif->version, gif->Image,
853 gif->Width, gif->Height, DILLO_IMG_TYPE_INDEXED);
854
855 Flags = buf[8];
856
857 gif->Flags |= Flags & INTERLACE;
858 gif->pass = 0;
859 bsize -= 9;
860 buf += 9;
861 if (Flags & LOCALCOLORMAP) {
862 size_t LSize = Gif_do_color_table(
863 gif, Buf, buf, bsize, Flags & (size_t)0x7);
864
865 if (!LSize)
866 return 0;
867 Size += LSize;
868 buf += LSize;
869 bsize -= LSize;
870 }
871 /* Finally, get the first byte of the LZW image data */
872 if (bsize < 1)
873 return 0;
874 gif->input_code_size = *buf++;
875 if (gif->input_code_size > 8) {
876 gif->state = 999;
877 return Size;
878 }
879 gif->y = 0;
880 Gif_lwz_init(gif);
881 gif->spill_line_index = 0;
882 gif->state = 3; /*Process the lzw data next */
883 if (gif->Image && gif->ColorMap_ofs) {
884 a_Dicache_set_cmap(gif->url, gif->version, gif->Image,
885 (guchar *) Buf + gif->ColorMap_ofs,
886 gif->NumColors, 256, gif->transparent);
887 }
888 return Size;
889 }
890
891 /* --- Top level data block processors ------------------------------------ */
892 #define Img_Desc (0x2c)
893 #define Trailer (0x3B)
894 #define Ext_Id (0x21)
895
896 /*
897 * This identifies which kind of GIF blocks are next, and processes them.
898 * It returns if there isn't enough data to process the next blocks, or if
899 * the next block is the lzw data (which is streamed differently)
900 *
901 * This implements, from the spec, <Data>* Trailer
902 * <Data> ::= <Graphic Block> | <Special-Purpose Block>
903 * <Special-Purpose Block> ::= Application Extension | Comment Extension
904 * <Graphic Block> ::= [Graphic Control Extension] <Graphic-Rendering Block>
905 * <Graphic-Rendering Block> ::= <Table-Based Image> | Plain Text Extension
906 *
907 * <Data>* --> GIF_Block
908 * <Data> --> while (...)
909 * <Special-Purpose Block> --> Gif_do_extension
910 * Graphic Control Extension --> Gif_do_extension
911 * Plain Text Extension --> Gif_do_extension
912 * <Table-Based Image> --> Gif_do_img_desc
913 *
914 * Return Value
915 * 0 if not enough data is present, otherwise the number of bytes
916 * "consumed"
917 */
918 static size_t GIF_Block(DilloGif * gif, void *Buf,
919 const guchar *buf, size_t bsize)
920 {
921 size_t Size = 0, mysize;
922 guchar C;
923
924 if (bsize < 1)
925 return 0;
926 while (gif->state == 2) {
927 if (bsize < 1)
928 return Size;
929 bsize--;
930 switch (*buf++) {
931 case Ext_Id:
932 /* get the extension type */
933 if (bsize < 2)
934 return Size;
935
936 /* Have the extension block intepreted. */
937 C = *buf++;
938 bsize--;
939 mysize = Gif_do_extension(gif, C, buf, bsize);
940
941 if (!mysize)
942 /* Not all of the extension is there.. quit until more data
943 * arrives */
944 return Size;
945
946 bsize -= mysize;
947 buf += mysize;
948
949 /* Increment the amount consumed by the extension introducer
950 * and id, and extension block size */
951 Size += mysize + 2;
952 /* Do more GIF Blocks */
953 continue;
954
955 case Img_Desc: /* Image descriptor */
956 mysize = Gif_do_img_desc(gif, Buf, buf, bsize);
957 if (!mysize)
958 return Size;
959
960 /* Increment the amount consumed by the Image Separator and the
961 * Resultant blocks */
962 Size += 1 + mysize;
963 return Size;
964
965 case Trailer:
966 gif->state = 999; /* BUG: should close the rest of the file */
967 return Size + 1;
968 break; /* GIF terminator */
969
970 default: /* Unknown */
971 /* gripe and complain */
972 MSG ("gif.c::GIF_Block: Error, 0x%x found\n", *(buf-1));
973 gif->state = 999;
974 return Size + 1;
975 }
976 }
977 return Size;
978 }
979
980
981 /*
982 * Process some bytes from the input gif stream. It's a state machine.
983 *
984 * From the GIF spec:
985 * <GIF Data Stream> ::= Header <Logical Screen> <Data>* Trailer
986 *
987 * <GIF Data Stream> --> Gif_process_bytes
988 * Header --> State 0
989 * <Logical Screen> --> State 1
990 * <Data>* --> State 2
991 * Trailer --> State > 3
992 *
993 * State == 3 is special... this is inside of <Data> but all of the stuff in
994 * there has been gotten and set up. So we stream it outside.
995 */
996 static size_t Gif_process_bytes(DilloGif *gif, const guchar *ibuf,
997 gint bufsize, void *Buf)
998 {
999 gint tmp_bufsize = bufsize;
1000 size_t mysize;
1001
1002 switch (gif->state) {
1003 case 0:
1004 mysize = Gif_check_sig(gif, ibuf, tmp_bufsize);
1005 if (!mysize)
1006 break;
1007 tmp_bufsize -= mysize;
1008 ibuf += mysize;
1009 if (gif->state != 1)
1010 break;
1011
1012 case 1:
1013 mysize = Gif_get_descriptor(gif, Buf, ibuf, tmp_bufsize);
1014 if (!mysize)
1015 break;
1016 tmp_bufsize -= mysize;
1017 ibuf += mysize;
1018 gif->state = 2;
1019
1020 case 2:
1021 /* Ok, this loop construction looks weird. It implements the <Data>* of
1022 * the GIF grammar. All sorts of stuff is allocated to set up for the
1023 * decode part (state ==2) and then there is the actual decode part (3)
1024 */
1025 mysize = GIF_Block(gif, Buf, ibuf, (size_t)tmp_bufsize);
1026 if (!mysize)
1027 break;
1028 tmp_bufsize -= mysize;
1029 ibuf += mysize;
1030 if (gif->state != 3)
1031 break;
1032
1033 case 3:
1034 /* get an image byte */
1035 /* The users sees all of this stuff */
1036 mysize = Gif_decode(gif, ibuf, (size_t)tmp_bufsize);
1037 if (mysize == 0)
1038 break;
1039 ibuf += mysize;
1040 tmp_bufsize -= mysize;
1041
1042 default:
1043 /* error - just consume all input */
1044 tmp_bufsize = 0;
1045 break;
1046 }
1047
1048 DEBUG_MSG(5, "Gif_process_bytes: final state %d, %ld bytes consumed\n",
1049 gif->state, (glong)(bufsize - tmp_bufsize));
1050
1051 return bufsize - tmp_bufsize;
1052 }
1053
1054 #endif /* ENABLE_GIF */
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This Gtk+ widget is a variant of GtkButton, which adds two features:
13 *
14 * 1. the possibility to react on different mouse buttons (not only button
15 * 1), and
16 * 2. a clean way to attach menus.
17 *
18 * To archieve this use one of the following functions:
19 *
20 * a_Gtk_ext_button_set_inactive,
21 * a_Gtk_ext_button_set_command,
22 * a_Gtk_ext_button_attach_menu, or
23 * a_Gtk_ext_button_attach_menu_creator.
24 *
25 * See comments there for more informations.
26 *
27 * About signals: For the command button mode, there are some new signals,
28 * "clicked1", "clicked2", and "clicked3", which are emitted for the respective
29 * mouse button. The signal "clicked" should be connected to as well, since
30 * it is still used for non-mouse event handling, e.g. when then button is
31 * activated by the <Enter> key.
32 */
33
34 #include "gtk_ext_button.h"
35 #include <gtk/gtklabel.h>
36 #include <gtk/gtkmain.h>
37 #include <gtk/gtksignal.h>
38
39 static void Gtk_ext_button_class_init (GtkExtButtonClass *klass);
40 static void Gtk_ext_button_init (GtkExtButton *button);
41
42 static void Gtk_ext_button_destroy (GtkObject *object);
43 static gint Gtk_ext_button_button_press (GtkWidget *widget,
44 GdkEventButton *event);
45 static gint Gtk_ext_button_button_release (GtkWidget *widget,
46 GdkEventButton *event);
47 static gint Gtk_ext_button_enter_notify (GtkWidget *widget,
48 GdkEventCrossing *event);
49 static gint Gtk_ext_button_leave_notify (GtkWidget *widget,
50 GdkEventCrossing *event);
51 static void Gtk_ext_button_menu_hidden (GtkExtButton *ext_button);
52
53 static gint clicked_signals[3];
54
55 /*
56 * Create a new GtkExtButton with no child.
57 */
58 GtkWidget *a_Gtk_ext_button_new ()
59 {
60 return gtk_type_new (a_Gtk_ext_button_get_type ());
61 }
62
63 /*
64 * Create a new GtkExtButton with a label.
65 */
66 GtkWidget *a_Gtk_ext_button_new_with_label (const gchar *label)
67 {
68 GtkWidget *button;
69 GtkWidget *label_widget;
70
71 button = a_Gtk_ext_button_new ();
72 label_widget = gtk_label_new (label);
73 gtk_misc_set_alignment (GTK_MISC (label_widget), 0.5, 0.5);
74
75 gtk_container_add (GTK_CONTAINER (button), label_widget);
76 gtk_widget_show (label_widget);
77
78 return button;
79 }
80
81 /*
82 * Standard Gtk+ function.
83 */
84 guint a_Gtk_ext_button_get_type ()
85 {
86 static gint type = 0;
87
88 if (!type) {
89 GtkTypeInfo info = {
90 "GtkExtButton",
91 sizeof (GtkExtButton),
92 sizeof (GtkExtButtonClass),
93 (GtkClassInitFunc) Gtk_ext_button_class_init,
94 (GtkObjectInitFunc) Gtk_ext_button_init,
95 (GtkArgSetFunc)NULL,
96 (GtkArgGetFunc)NULL,
97 (GtkClassInitFunc)NULL
98 };
99 type = gtk_type_unique (gtk_button_get_type (), &info);
100 }
101
102 return type;
103 }
104
105 /*
106 * Standard Gtk+ function.
107 */
108 static void Gtk_ext_button_class_init(GtkExtButtonClass *klass)
109 {
110 GtkObjectClass *object_class;
111 GtkWidgetClass *widget_class;
112
113 object_class = (GtkObjectClass*) klass;
114 object_class->destroy = Gtk_ext_button_destroy;
115
116 clicked_signals[0] =
117 gtk_signal_new ("clicked1",
118 GTK_RUN_FIRST | GTK_RUN_ACTION,
119 object_class->type,
120 GTK_SIGNAL_OFFSET (GtkExtButtonClass, clicked1),
121 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
122 clicked_signals[1] =
123 gtk_signal_new ("clicked2",
124 GTK_RUN_FIRST | GTK_RUN_ACTION,
125 object_class->type,
126 GTK_SIGNAL_OFFSET (GtkExtButtonClass, clicked2),
127 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
128 clicked_signals[2] =
129 gtk_signal_new ("clicked3",
130 GTK_RUN_FIRST | GTK_RUN_ACTION,
131 object_class->type,
132 GTK_SIGNAL_OFFSET (GtkExtButtonClass, clicked3),
133 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
134 gtk_object_class_add_signals (object_class, clicked_signals, 3);
135
136 widget_class = (GtkWidgetClass*)klass;
137 widget_class->button_press_event = Gtk_ext_button_button_press;
138 widget_class->button_release_event = Gtk_ext_button_button_release;
139 widget_class->enter_notify_event = Gtk_ext_button_enter_notify;
140 widget_class->leave_notify_event = Gtk_ext_button_leave_notify;
141 }
142
143
144 /*
145 * Standard Gtk+ function.
146 */
147 static void Gtk_ext_button_init(GtkExtButton *button)
148 {
149 int i;
150
151 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
152
153 button->pressed_button = 0;
154 button->active_menu = NULL;
155 button->menu_signal_id = -1;
156
157 for(i = 0; i < 5; i++)
158 button->action[i] = GTK_EXT_BUTTON_INACTIVE;
159 }
160
161
162 /*
163 * Standard Gtk+ function.
164 */
165 static void Gtk_ext_button_destroy (GtkObject *object)
166 {
167 GtkExtButton *button;
168
169 button = GTK_EXT_BUTTON (object);
170 if (button->menu_signal_id != -1)
171 gtk_signal_disconnect (GTK_OBJECT (button->active_menu),
172 button->menu_signal_id);
173 }
174
175
176 /*
177 * This is used for gtk_menu_popup, to position the menu.
178 */
179 static void Gtk_ext_button_position_menu (GtkMenu *menu,
180 gint *x,
181 gint *y,
182 gpointer user_data)
183 {
184 GtkWidget *widget;
185 GtkRequisition requisition;
186
187 widget = GTK_WIDGET (user_data);
188 gdk_window_get_origin (widget->window, x, y);
189
190 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
191 if (*y + widget->allocation.height + requisition.height
192 > gdk_screen_height())
193 /* Show menu above button, since there is not enough space below. */
194 *y -= requisition.height;
195 else
196 /* Show menu below button. */
197 *y += widget->allocation.height;
198
199 /* If the menu does not fit horizontilly, adjust position. */
200 if (*x + requisition.width > gdk_screen_width ())
201 *x = gdk_screen_width () - requisition.width;
202 }
203
204
205 /*
206 * Standard Gtk+ function.
207 */
208 static gint Gtk_ext_button_button_press (GtkWidget *widget,
209 GdkEventButton *event)
210 {
211 GtkButton *button;
212 GtkExtButton *ext_button;
213 GtkStateType new_state;
214 GtkMenu *menu;
215
216 button = GTK_BUTTON (widget);
217 ext_button = GTK_EXT_BUTTON (widget);
218
219 if (ext_button->pressed_button) {
220 /* Already a button pressed. */
221 return FALSE;
222 } else if (event->button >= 1 && event->button <= 3 &&
223 ext_button->action[event->button - 1]
224 != GTK_EXT_BUTTON_INACTIVE) {
225 ext_button->pressed_button = event->button;
226
227 gtk_grab_add (widget);
228 button->button_down = TRUE;
229 new_state = (button->in_button ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL);
230
231 if (GTK_WIDGET_STATE (button) != new_state) {
232 gtk_widget_set_state (GTK_WIDGET (button), new_state);
233 gtk_widget_queue_draw (GTK_WIDGET (button));
234 }
235
236 menu = NULL;
237
238 switch(ext_button->action[event->button - 1]) {
239 case GTK_EXT_BUTTON_INACTIVE:
240 g_assert_not_reached ();
241 break;
242
243 case GTK_EXT_BUTTON_COMMAND:
244 /* Nothing to do anymore. */
245 break;
246
247 case GTK_EXT_BUTTON_MENU:
248 menu = ext_button->action_data[event->button - 1].menu;
249 break;
250
251 case GTK_EXT_BUTTON_MENU_CREATOR:
252 menu =
253 ext_button->action_data[event->button - 1].creator.func (
254 ext_button,
255 ext_button->action_data[event->button - 1].creator.data);
256 break;
257 }
258
259 if (menu) {
260 ext_button->active_menu = menu;
261 gtk_menu_popup (menu, NULL, widget, Gtk_ext_button_position_menu,
262 widget, event->button, event->time);
263 ext_button->menu_signal_id =
264 gtk_signal_connect_object (GTK_OBJECT (menu), "hide",
265 GTK_SIGNAL_FUNC (
266 Gtk_ext_button_menu_hidden),
267 (gpointer) button);
268 }
269
270 return TRUE;
271 } else
272 return FALSE;
273 }
274
275
276 /*
277 * Standard Gtk+ function.
278 */
279 static gint Gtk_ext_button_button_release (GtkWidget *widget,
280 GdkEventButton *event)
281 {
282 GtkButton *button;
283 GtkExtButton *ext_button;
284 GtkStateType new_state;
285
286 ext_button = GTK_EXT_BUTTON (widget);
287
288 /* Only react on the button initially pressed. */
289 if (event->button == ext_button->pressed_button) {
290 button = GTK_BUTTON (widget);
291
292 if (button->in_button)
293 gtk_signal_emit (GTK_OBJECT (widget),
294 clicked_signals[event->button - 1]);
295
296 gtk_grab_remove (widget);
297
298 button->button_down = FALSE;
299 ext_button->pressed_button = 0;
300 new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
301
302 if (GTK_WIDGET_STATE (button) != new_state) {
303 gtk_widget_set_state (GTK_WIDGET (button), new_state);
304 /* We _draw () instead of queue_draw so that if the operation
305 * blocks, the label doesn't vanish.
306 */
307 gtk_widget_draw (GTK_WIDGET (button), NULL);
308 }
309 }
310
311 return TRUE;
312 }
313
314
315 /*
316 * Standard Gtk+ function.
317 */
318 static gint Gtk_ext_button_enter_notify (GtkWidget *widget,
319 GdkEventCrossing *event)
320 {
321 GtkButton *button;
322 GtkWidget *event_widget;
323 GtkStateType new_state;
324
325 button = GTK_BUTTON (widget);
326 event_widget = gtk_get_event_widget ((GdkEvent*) event);
327
328 if ((event_widget == widget) && (event->detail != GDK_NOTIFY_INFERIOR)) {
329 button->in_button = TRUE;
330 new_state =
331 (button->button_down ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
332
333 if (GTK_WIDGET_STATE (button) != new_state) {
334 gtk_widget_set_state (GTK_WIDGET (button), new_state);
335 gtk_widget_queue_draw (GTK_WIDGET (button));
336 }
337 }
338
339 return FALSE;
340 }
341
342
343 /*
344 * Standard Gtk+ function.
345 */
346 static gint Gtk_ext_button_leave_notify (GtkWidget *widget,
347 GdkEventCrossing *event)
348 {
349 GtkButton *button;
350 GtkExtButton *ext_button;
351 GtkWidget *event_widget;
352
353 button = GTK_BUTTON (widget);
354 event_widget = gtk_get_event_widget ((GdkEvent*) event);
355
356 if ((event_widget == widget) &&
357 (event->detail != GDK_NOTIFY_INFERIOR)) {
358 button->in_button = FALSE;
359
360 ext_button = GTK_EXT_BUTTON (widget);
361
362 /* If a men is attached for this mouse button, we keep the button widget
363 * inset. */
364 if (ext_button->pressed_button == 0 ||
365 ext_button->action[ext_button->pressed_button -1]
366 == GTK_EXT_BUTTON_COMMAND) {
367 /* Otherwise, normal behavior. */
368 if (GTK_WIDGET_STATE (button) != GTK_STATE_NORMAL) {
369 gtk_widget_set_state (GTK_WIDGET (button), GTK_STATE_NORMAL);
370 gtk_widget_queue_draw (GTK_WIDGET (button));
371 }
372 }
373 }
374
375 return FALSE;
376 }
377
378 /*
379 * This function is called, when a popped up menu is hidden again, to reset
380 * the state of the button.
381 */
382 static void Gtk_ext_button_menu_hidden (GtkExtButton *ext_button)
383 {
384 GtkButton *button;
385
386 g_return_if_fail (ext_button->menu_signal_id != -1);
387 g_return_if_fail (ext_button->active_menu != NULL);
388 gtk_signal_disconnect (GTK_OBJECT (ext_button->active_menu),
389 ext_button->menu_signal_id);
390 ext_button->menu_signal_id = -1;
391 ext_button->active_menu = NULL;
392
393 gtk_grab_remove (GTK_WIDGET (ext_button));
394
395 button = GTK_BUTTON (ext_button);
396 button->in_button = FALSE;
397 button->button_down = FALSE;
398 ext_button->pressed_button = 0;
399
400 gtk_widget_set_state (GTK_WIDGET (button), GTK_STATE_NORMAL);
401 gtk_widget_queue_draw (GTK_WIDGET (button));
402 }
403
404
405 /*
406 * Set no action for the specific mouse button. This is the default.
407 */
408 void a_Gtk_ext_button_set_inactive (GtkExtButton *button,
409 gint button_no)
410 {
411 g_return_if_fail (button_no >= 1 && button_no <= 3);
412 button->action[button_no - 1] = GTK_EXT_BUTTON_INACTIVE;
413 }
414
415
416 /*
417 * Make the button behave like normal command button.
418 */
419 void a_Gtk_ext_button_set_command (GtkExtButton *button,
420 gint button_no)
421 {
422 g_return_if_fail (button_no >= 1 && button_no <= 3);
423 button->action[button_no - 1] = GTK_EXT_BUTTON_COMMAND;
424 }
425
426
427 /*
428 * Attach a fixed menu to the button, which is popped up, when the user
429 * presses the respective button.
430 */
431 void a_Gtk_ext_button_attach_menu (GtkExtButton *button,
432 gint button_no,
433 GtkMenu *menu)
434 {
435 g_return_if_fail (button_no >= 1 && button_no <= 3);
436 button->action[button_no - 1] = GTK_EXT_BUTTON_MENU;
437 button->action_data[button_no - 1].menu = menu;
438 }
439
440 /*
441 * Attach a dynamically created menu to the button, which is popped up, when
442 * the user presses the respective button.
443 *
444 * When the respective button has been pressed, the creator function is called
445 * with the button, and the value of the argument data, the return value must
446 * be the menu, which is then popped up.
447 */
448 void a_Gtk_ext_button_attach_menu_creator (GtkExtButton *button,
449 gint button_no,
450 GtkExtButtonMenuCreator *creator,
451 gpointer data)
452 {
453 g_return_if_fail (button_no >= 1 && button_no <= 3);
454 button->action[button_no - 1] = GTK_EXT_BUTTON_MENU_CREATOR;
455 button->action_data[button_no - 1].creator.func = creator;
456 button->action_data[button_no - 1].creator.data = data;
457 }
0 #ifndef __GTK_EXT_BUTTON_H__
1 #define __GTK_EXT_BUTTON_H__
2
3 #include <gtk/gtkbutton.h>
4 #include <gtk/gtkmenu.h>
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 #define GTK_EXT_BUTTON(obj) (GTK_CHECK_CAST ((obj), \
12 a_Gtk_ext_button_get_type (), \
13 GtkExtButton))
14 #define GTK_EXT_BUTTON_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
15 a_Gtk_ext_button_get_type (), \
16 GtkExtButtonClass))
17 #define GTK_IS_EXT_BUTTON(obj) (GTK_CHECK_TYPE ((obj), \
18 a_Gtk_ext_button_get_type ()))
19
20
21 typedef struct _GtkExtButton GtkExtButton;
22 typedef struct _GtkExtButtonClass GtkExtButtonClass;
23
24 typedef GtkMenu* (GtkExtButtonMenuCreator)(GtkExtButton *button,
25 gpointer data);
26
27 struct _GtkExtButton
28 {
29 GtkButton button;
30
31 /*
32 * What actually happens, when the user presses on the specific button.
33 */
34 enum {
35 GTK_EXT_BUTTON_INACTIVE, /* Nothing happens. */
36 GTK_EXT_BUTTON_COMMAND, /* Behaves like a normal command button. */
37 GTK_EXT_BUTTON_MENU, /* Pops up a fixed menu. */
38 GTK_EXT_BUTTON_MENU_CREATOR /* Pops up a menu, which may be created by
39 * a passed function. */
40 } action[3];
41
42 union
43 {
44 GtkMenu *menu;
45 struct
46 {
47 GtkExtButtonMenuCreator *func;
48 gpointer data;
49 } creator;
50 } action_data[3];
51
52 gint menu_signal_id;
53 guint pressed_button;
54 GtkMenu *active_menu;
55 };
56
57 struct _GtkExtButtonClass
58 {
59 GtkButtonClass parent_class;
60
61 void (*clicked1) (GtkButton *button);
62 void (*clicked2) (GtkButton *button);
63 void (*clicked3) (GtkButton *button);
64 };
65
66
67 guint a_Gtk_ext_button_get_type (void);
68 GtkWidget *a_Gtk_ext_button_new (void);
69 GtkWidget *a_Gtk_ext_button_new_with_label (const gchar *label);
70
71 void a_Gtk_ext_button_set_inactive (GtkExtButton *button,
72 gint button_no);
73 void a_Gtk_ext_button_set_command (GtkExtButton *button,
74 gint button_no);
75 void a_Gtk_ext_button_attach_menu (GtkExtButton *button,
76 gint button_no,
77 GtkMenu *menu);
78 void a_Gtk_ext_button_attach_menu_creator (GtkExtButton *button,
79 gint button_no,
80 GtkExtButtonMenuCreator
81 *creator,
82 gpointer data);
83
84 #ifdef __cplusplus
85 }
86 #endif /* __cplusplus */
87
88
89 #endif /* __GTK_EXT_BUTTON_H__ */
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This widget, which is a variant of GtkMenu, works together with
13 * GtkExtMenuItem. Together, they add the possibility to react on different
14 * mouse buttons, for activating the menu items.
15 *
16 * If an item is activated by the mouse, the signals "activate1", "activate2",
17 * and "activate3" are emitted. The signal "activate" should still be regarded,
18 * since it is emitted (by the base classes), when an item was activated by
19 * other input devices, e.g. the keyboard.
20 *
21 * Some notes about the implementation: Normally, mouse events are handled
22 * by GtkMenu, not by GtkMenuItem. GtkMenu will call gtk_widget_activate(),
23 * which emits the activate signal. For GtkMenuItem, this is "activate".
24 *
25 * GtkExtMenu changes dealing with release events a bit: if the affected
26 * menu item is a GtkExtMenuItem, it is told to handle this event specially.
27 * The GtkExtMenuItem will simply change its activate signal temporally.
28 *
29 * (This is a bit ugly, but still cleaner than listening to the low-level
30 * events, like "button-release-event".)
31 */
32
33 #include "gtk_ext_menu.h"
34 #include "gtk_ext_menu_item.h"
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkmain.h>
37 #include <gtk/gtksignal.h>
38
39 static void Gtk_ext_menu_class_init (GtkExtMenuClass *klass);
40 static void Gtk_ext_menu_init (GtkExtMenu *menu);
41
42 static gint Gtk_ext_menu_button_release (GtkWidget *widget,
43 GdkEventButton *event);
44
45 static GtkMenuClass *parent_class = NULL;
46
47
48 /*
49 * Standard Gtk+ function.
50 */
51 GtkType a_Gtk_ext_menu_get_type (void)
52 {
53 static GtkType type = 0;
54
55 if (!type) {
56 GtkTypeInfo info = {
57 "GtkExtMenu",
58 sizeof (GtkExtMenu),
59 sizeof (GtkExtMenuClass),
60 (GtkClassInitFunc) Gtk_ext_menu_class_init,
61 (GtkObjectInitFunc) Gtk_ext_menu_init,
62 (GtkArgSetFunc) NULL,
63 (GtkArgGetFunc) NULL,
64 (GtkClassInitFunc)NULL
65 };
66
67 type = gtk_type_unique (gtk_menu_get_type (), &info);
68 }
69
70 return type;
71 }
72
73
74 /*
75 * Standard Gtk+ function.
76 */
77 static void Gtk_ext_menu_class_init (GtkExtMenuClass *klass)
78 {
79 GtkWidgetClass *gtk_widget_class;
80
81 parent_class = gtk_type_class (gtk_menu_get_type ());
82
83 gtk_widget_class = (GtkWidgetClass*) klass;
84 gtk_widget_class->button_release_event = Gtk_ext_menu_button_release;
85 }
86
87
88 /*
89 * Standard Gtk+ function.
90 */
91 static void Gtk_ext_menu_init (GtkExtMenu *menu)
92 {
93 }
94
95
96 /*
97 * Create a new GtkExtMenu.
98 */
99 GtkWidget* a_Gtk_ext_menu_new (void)
100 {
101 return gtk_type_new (a_Gtk_ext_menu_get_type ());
102 }
103
104
105 /*
106 * Copied from Gtk+ 1.2.10, file "menu_shell.c", function
107 * gtk_menu_shell_is_item.
108 */
109 static gint Gtk_ext_menu_is_item (GtkMenuShell *menu_shell,
110 GtkWidget *child)
111 {
112 GtkWidget *parent;
113
114 g_return_val_if_fail (menu_shell != NULL, FALSE);
115 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
116 g_return_val_if_fail (child != NULL, FALSE);
117
118 parent = child->parent;
119 while (parent && GTK_IS_MENU_SHELL (parent)) {
120 if (parent == (GtkWidget*) menu_shell)
121 return TRUE;
122 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
123 }
124
125 return FALSE;
126 }
127
128 /*
129 * Copied from Gtk+ 1.2.10, file "menu_shell.c", function
130 * gtk_menu_shell_get_item.
131 */
132 static GtkWidget* Gtk_ext_menu_get_item (GtkMenuShell *menu_shell,
133 GdkEvent *event)
134 {
135 GtkWidget *menu_item;
136
137 menu_item = gtk_get_event_widget ((GdkEvent*) event);
138
139 while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
140 menu_item = menu_item->parent;
141
142 if (menu_item && Gtk_ext_menu_is_item (menu_shell, menu_item))
143 return menu_item;
144 else
145 return NULL;
146 }
147
148
149 /*
150 * Standard Gtk+ function.
151 */
152 static gint Gtk_ext_menu_button_release (GtkWidget *widget,
153 GdkEventButton *event)
154 {
155 GtkWidget *menu_item;
156 gint return_value;
157
158 menu_item = Gtk_ext_menu_get_item (GTK_MENU_SHELL (widget),
159 (GdkEvent*) event);
160 if (menu_item != NULL && GTK_IS_EXT_MENU_ITEM (menu_item))
161 p_Gtk_ext_menu_item_prepare_button_release (GTK_EXT_MENU_ITEM(menu_item),
162 event);
163 return_value =
164 GTK_WIDGET_CLASS(parent_class)->button_release_event (widget, event);
165 if (menu_item != NULL && GTK_IS_EXT_MENU_ITEM (menu_item))
166 p_Gtk_ext_menu_item_finish_button_release (GTK_EXT_MENU_ITEM (menu_item),
167 event);
168 return return_value;
169 }
0 #ifndef __GTK_EXT_MENU_H__
1 #define __GTK_EXT_MENU_H__
2
3 #include <gtk/gtkmenu.h>
4
5 #ifdef __cplusplus
6 extern "C" {
7
8 #endif /* __cplusplus */
9
10 #define GTK_EXT_MENU(obj) (GTK_CHECK_CAST ((obj), \
11 a_Gtk_ext_menu_get_type (), GtkExtMenu))
12 #define GTK_EXT_MENU_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
13 a_Gtk_ext_menu_get_type (), \
14 GtkExtMenuClass))
15 #define GTK_IS_EXT_MENU(obj) (GTK_CHECK_TYPE ((obj), \
16 a_Gtk_ext_menu_get_type ()))
17
18 typedef struct _GtkExtMenu GtkExtMenu;
19 typedef struct _GtkExtMenuClass GtkExtMenuClass;
20
21 struct _GtkExtMenu
22 {
23 GtkMenu menu;
24 };
25
26 struct _GtkExtMenuClass
27 {
28 GtkMenuClass parent_class;
29 };
30
31 GtkType a_Gtk_ext_menu_get_type (void);
32 GtkWidget* a_Gtk_ext_menu_new (void);
33
34 #ifdef __cplusplus
35 }
36 #endif /* __cplusplus */
37
38 #endif /* __GTK_EXT_MENU_H__ */
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * See comments at the beginning of "gtk_ext_menu.c".
13 */
14
15 #include "gtk_ext_menu_item.h"
16 #include <gtk/gtksignal.h>
17 #include <gtk/gtkaccellabel.h>
18
19 static void Gtk_ext_menu_item_class_init (GtkExtMenuItemClass *klass);
20 static void Gtk_ext_menu_item_init (GtkExtMenuItem *item);
21
22 static gint std_activate_signal;
23 static gint activate_signals[3];
24
25 /*
26 * Standard Gtk+ function.
27 */
28 GtkType a_Gtk_ext_menu_item_get_type (void)
29 {
30 static GtkType type = 0;
31
32 if (!type) {
33 GtkTypeInfo info = {
34 "GtkExtMenuItem",
35 sizeof (GtkExtMenuItem),
36 sizeof (GtkExtMenuItemClass),
37 (GtkClassInitFunc) Gtk_ext_menu_item_class_init,
38 (GtkObjectInitFunc) Gtk_ext_menu_item_init,
39 (GtkArgSetFunc) NULL,
40 (GtkArgGetFunc) NULL,
41 (GtkClassInitFunc)NULL
42 };
43
44 type = gtk_type_unique (gtk_menu_item_get_type (), &info);
45 }
46
47 return type;
48 }
49
50 /*
51 * Standard Gtk+ function.
52 */
53 static void Gtk_ext_menu_item_class_init (GtkExtMenuItemClass *klass)
54 {
55 GtkObjectClass *object_class;
56
57 object_class = (GtkObjectClass*) klass;
58 activate_signals[0] =
59 gtk_signal_new ("activate1",
60 GTK_RUN_FIRST | GTK_RUN_ACTION,
61 object_class->type,
62 GTK_SIGNAL_OFFSET (GtkExtMenuItemClass, activate1),
63 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
64 activate_signals[1] =
65 gtk_signal_new ("activate2",
66 GTK_RUN_FIRST | GTK_RUN_ACTION,
67 object_class->type,
68 GTK_SIGNAL_OFFSET (GtkExtMenuItemClass, activate2),
69 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
70 activate_signals[2] =
71 gtk_signal_new ("activate3",
72 GTK_RUN_FIRST | GTK_RUN_ACTION,
73 object_class->type,
74 GTK_SIGNAL_OFFSET (GtkExtMenuItemClass, activate3),
75 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
76 gtk_object_class_add_signals (object_class, activate_signals, 3);
77
78 klass->activate1 = NULL;
79 klass->activate2 = NULL;
80 klass->activate3 = NULL;
81
82 std_activate_signal =
83 GTK_WIDGET_CLASS(gtk_type_class (gtk_menu_item_get_type ()))
84 ->activate_signal;
85 }
86
87
88 /*
89 * Standard Gtk+ function.
90 */
91 static void Gtk_ext_menu_item_init (GtkExtMenuItem *item)
92 {
93 }
94
95 /*
96 * Create a new, empty GtkExtMenuItem.
97 */
98 GtkWidget *a_Gtk_ext_menu_item_new (void)
99 {
100 return gtk_type_new (a_Gtk_ext_menu_item_get_type ());
101 }
102
103 /*
104 * Create a GtkExtMenuItem with a label as child.
105 */
106 GtkWidget *a_Gtk_ext_menu_item_new_with_label (const gchar *label)
107 {
108 GtkWidget *menu_item;
109 GtkWidget *accel_label;
110
111 menu_item = a_Gtk_ext_menu_item_new ();
112 accel_label = gtk_accel_label_new (label);
113 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
114
115 gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
116 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
117 gtk_widget_show (accel_label);
118
119 return menu_item;
120 }
121
122 /*
123 * This method is called by GtkExtMenu, before handling a button release event.
124 * See comments there.
125 */
126 void p_Gtk_ext_menu_item_prepare_button_release (GtkExtMenuItem *item,
127 GdkEventButton *event)
128 {
129 if (event->button >= 1 && event->button <= 3)
130 GTK_WIDGET_CLASS(GTK_OBJECT(item)->klass)->activate_signal =
131 activate_signals[event->button - 1];
132 }
133
134 /*
135 * This method is called by GtkExtMenu, after handling a button release event.
136 * See comments there.
137 */
138 void p_Gtk_ext_menu_item_finish_button_release (GtkExtMenuItem *item,
139 GdkEventButton *event)
140 {
141 GTK_WIDGET_CLASS(GTK_OBJECT(item)->klass)->activate_signal =
142 std_activate_signal;
143 }
0 #ifndef __GTK_EXT_MENU_ITEM_H__
1 #define __GTK_EXT_MENU_ITEM_H__
2
3 #include <gtk/gtkmenuitem.h>
4
5 #ifdef __cplusplus
6 extern "C" {
7
8 #endif /* __cplusplus */
9
10 #define GTK_EXT_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), \
11 a_Gtk_ext_menu_item_get_type (), \
12 GtkExtMenuItem))
13 #define GTK_EXT_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
14 a_Gtk_ext_menu_item_get_type (), \
15 GtkExtMenuItemClass))
16 #define GTK_IS_EXT_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), \
17 a_Gtk_ext_menu_item_get_type ()))
18
19 typedef struct _GtkExtMenuItem GtkExtMenuItem;
20 typedef struct _GtkExtMenuItemClass GtkExtMenuItemClass;
21
22 struct _GtkExtMenuItem
23 {
24 GtkMenuItem menu_item;
25 };
26
27 struct _GtkExtMenuItemClass
28 {
29 GtkMenuItemClass parent_class;
30
31 void (*activate1) (GtkExtMenuItem *menu_item);
32 void (*activate2) (GtkExtMenuItem *menu_item);
33 void (*activate3) (GtkExtMenuItem *menu_item);
34 };
35
36 GtkType a_Gtk_ext_menu_item_get_type (void);
37 GtkWidget* a_Gtk_ext_menu_item_new (void);
38 GtkWidget* a_Gtk_ext_menu_item_new_with_label (const gchar *label);
39
40 void p_Gtk_ext_menu_item_prepare_button_release (GtkExtMenuItem *item,
41 GdkEventButton *event);
42 void p_Gtk_ext_menu_item_finish_button_release (GtkExtMenuItem *item,
43 GdkEventButton *event);
44
45 #ifdef __cplusplus
46 }
47 #endif /* __cplusplus */
48
49 #endif /* __GTK_EXT_MENU_ITEM_H__ */
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This is a widget with a very special purpose, for displaying titles within
13 * menues. It is set to inactive (so it cannot be focussed), and the text is
14 * centered horizontally.
15 *
16 * BTW, it is simple to change the look of menu titles, by putting the
17 * following into your ~/.gtkrc file:
18 *
19 * style "menu-title" = "default" {
20 * font = "-*-helvetica-medium-o-*-*-10-*-*-*-*-*-*-*"
21 * }
22 *
23 * widget "*GtkMenuTitle" style "menu-title"
24 */
25 #include "gtk_menu_title.h"
26
27 #define BORDER_SPACING 3
28
29 static void Gtk_menu_title_class_init (GtkMenuTitleClass *klass);
30 static void Gtk_menu_title_init (GtkMenuTitle *menu_title);
31
32 static void Gtk_menu_title_destroy (GtkObject *object);
33 static void Gtk_menu_title_size_request (GtkWidget *widget,
34 GtkRequisition *requisition);
35 static void Gtk_menu_title_draw (GtkWidget *widget,
36 GdkRectangle *area);
37 static gint Gtk_menu_title_expose_event (GtkWidget *widget,
38 GdkEventExpose *event);
39
40 static GtkMenuItemClass *parent_class = NULL;
41
42
43 /*
44 * Standard Gtk+ function.
45 */
46 GtkType a_Gtk_menu_title_get_type (void)
47 {
48 static GtkType type = 0;
49
50 if (!type) {
51 GtkTypeInfo info = {
52 "GtkMenuTitle",
53 sizeof (GtkMenuTitle),
54 sizeof (GtkMenuTitleClass),
55 (GtkClassInitFunc) Gtk_menu_title_class_init,
56 (GtkObjectInitFunc) Gtk_menu_title_init,
57 (GtkArgSetFunc) NULL,
58 (GtkArgGetFunc) NULL,
59 (GtkClassInitFunc)NULL
60 };
61
62 type = gtk_type_unique (gtk_menu_item_get_type (), &info);
63 }
64
65 return type;
66 }
67
68
69 /*
70 * Standard Gtk+ function.
71 */
72 static void Gtk_menu_title_class_init (GtkMenuTitleClass *klass)
73 {
74 GtkWidgetClass *widget_class;
75 GtkObjectClass *object_class;
76
77 parent_class = (GtkMenuItemClass*) klass;
78
79 object_class = (GtkObjectClass*) klass;
80 object_class->destroy = Gtk_menu_title_destroy;
81
82 widget_class = (GtkWidgetClass*) klass;
83 widget_class->size_request = Gtk_menu_title_size_request;
84 widget_class->draw = Gtk_menu_title_draw;
85 widget_class->expose_event = Gtk_menu_title_expose_event;
86 }
87
88
89 /*
90 * Standard Gtk+ function.
91 */
92 static void Gtk_menu_title_init (GtkMenuTitle *menu_title)
93 {
94 menu_title->label = NULL;
95 }
96
97
98 /*
99 * Return a new GtkMenuTitle.
100 */
101 GtkWidget* a_Gtk_menu_title_new (const char *label)
102 {
103 GtkWidget *widget;
104
105 widget = gtk_type_new (a_Gtk_menu_title_get_type ());
106 GTK_MENU_TITLE(widget)->label = label ? g_strdup (label) : NULL;
107 gtk_widget_set_sensitive (widget, FALSE);
108 return widget;
109 }
110
111
112 /*
113 * Standard Gtk+ function.
114 */
115 static void Gtk_menu_title_destroy (GtkObject *object)
116 {
117 GtkMenuTitle *menu_title;
118
119 menu_title = GTK_MENU_TITLE (object);
120 if (menu_title->label)
121 g_free (menu_title->label);
122 }
123
124
125 /*
126 * Standard Gtk+ function.
127 */
128 static void Gtk_menu_title_size_request (GtkWidget *widget,
129 GtkRequisition *requisition)
130 {
131 GtkMenuTitle *menu_title;
132
133 menu_title = GTK_MENU_TITLE (widget);
134 requisition->width = 2 * (GTK_CONTAINER (widget)->border_width +
135 widget->style->klass->xthickness +
136 BORDER_SPACING);
137 requisition->height = 2 * (GTK_CONTAINER (widget)->border_width +
138 widget->style->klass->ythickness);
139
140 if (menu_title->label) {
141 requisition->width +=
142 gdk_string_width (widget->style->font, GTK_MENU_TITLE(widget)->label);
143 requisition->height +=
144 widget->style->font->ascent + widget->style->font->descent;
145 }
146 }
147
148 /*
149 * Standard Gtk+ function.
150 */
151 static void Gtk_menu_title_paint (GtkWidget *widget,
152 GdkRectangle *area)
153 {
154 GtkMenuTitle *menu_title;
155 gint x, y;
156
157 menu_title = GTK_MENU_TITLE (widget);
158 if (menu_title->label) {
159 x =
160 (widget->allocation.width - gdk_string_width (widget->style->font,
161 menu_title->label)) / 2;
162 y =
163 GTK_CONTAINER (widget)->border_width +
164 widget->style->klass->ythickness
165 + widget->style->font->ascent;
166
167 /* We do not use widget->state, but instead GTK_STATE_NORMAL, since
168 * otherwise, the text would be rendered gray. */
169 gdk_draw_string (widget->window, widget->style->font,
170 widget->style->fg_gc[GTK_STATE_NORMAL], x, y,
171 menu_title->label);
172 }
173 }
174
175 /*
176 * Standard Gtk+ function.
177 */
178 static void Gtk_menu_title_draw (GtkWidget *widget,
179 GdkRectangle *area)
180 {
181 if (GTK_WIDGET_DRAWABLE (widget))
182 Gtk_menu_title_paint (widget, area);
183 }
184
185
186 /*
187 * Standard Gtk+ function.
188 */
189 static gint Gtk_menu_title_expose_event (GtkWidget *widget,
190 GdkEventExpose *event)
191 {
192 if (GTK_WIDGET_DRAWABLE (widget))
193 Gtk_menu_title_paint (widget, &event->area);
194 return TRUE;
195 }
0 #ifndef __GTK_MENU_TITLE_H__
1 #define __GTK_MENU_TITLE_H__
2
3 #include <gtk/gtkmenuitem.h>
4
5 #ifdef __cplusplus
6 extern "C" {
7
8 #endif /* __cplusplus */
9
10 #define GTK_MENU_TITLE(obj) (GTK_CHECK_CAST ((obj), \
11 a_Gtk_menu_title_get_type(), \
12 GtkMenuTitle))
13 #define GTK_MENU_TITLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
14 a_Gtk_menu_title_get_type(), \
15 GtkMenuTitleClass))
16 #define GTK_IS_MENU_TITLE (GTK_CHECK_TYPE ((obj), \
17 a_Gtk_menu_title_get_type()))
18
19 typedef struct _GtkMenuTitle GtkMenuTitle;
20 typedef struct _GtkMenuTitleClass GtkMenuTitleClass;
21
22 struct _GtkMenuTitle
23 {
24 GtkMenuItem gtk_menu_item;
25 char *label;
26 };
27
28 struct _GtkMenuTitleClass
29 {
30 GtkMenuItemClass parent_class;
31 };
32
33 GtkType a_Gtk_menu_title_get_type (void);
34 GtkWidget* a_Gtk_menu_title_new (const char *label);
35
36 #ifdef __cplusplus
37 }
38 #endif /* __cplusplus */
39
40 #endif /* __GTK_MENU_TITLE_H__ */
0 /*
1 * File: history.c
2 *
3 * Copyright (C) 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Linear history (it also provides indexes for the navigation stack)
13 */
14
15 #include "list.h"
16 #include "history.h"
17
18
19 typedef struct {
20 DilloUrl *url;
21 gchar *title;
22 } H_Item;
23
24
25 /* Global history list */
26 static H_Item *history = NULL;
27 static gint history_size = 0; /* [1 based] */
28 static gint history_size_max = 16;
29
30
31 /*
32 * Add a new H_Item at the end of the history list
33 * (taking care of not making a duplicate entry)
34 */
35 int a_History_add_url(DilloUrl *url)
36 {
37 gint i, idx;
38
39 for (i = 0; i < history_size; ++i)
40 if (a_Url_cmp(history[i].url, url) == 0)
41 return i;
42
43 idx = history_size;
44 a_List_add(history, history_size, history_size_max);
45 history[idx].url = a_Url_dup(url);
46 history[idx].title = NULL;
47 ++history_size;
48 return idx;
49 }
50
51 /*
52 * Set the page-title for a given URL (by idx)
53 * (this is known when the first chunks of HTML data arrive)
54 */
55 int a_History_set_title(gint idx, const gchar *title)
56 {
57 g_return_val_if_fail(idx >= 0 && idx < history_size, 0);
58
59 g_free(history[idx].title);
60 history[idx].title = g_strdup(title);
61 return 1;
62 }
63
64 /*
65 * Return the DilloUrl camp (by index)
66 */
67 DilloUrl *a_History_get_url(gint idx)
68 {
69 g_return_val_if_fail(idx >= 0 && idx < history_size, NULL);
70
71 return history[idx].url;
72 }
73
74 /*
75 * Return the title camp (by index)
76 */
77 const gchar *a_History_get_title(gint idx)
78 {
79 g_return_val_if_fail(idx >= 0 && idx < history_size, NULL);
80
81 if (history[idx].title)
82 return history[idx].title;
83 else
84 return URL_STR_(history[idx].url);
85 }
86
87 /*
88 * Return the title camp (by url)
89 * ('force' returns URL_STR when there's no title)
90 */
91 const gchar *a_History_get_title_by_url(DilloUrl *url, gint force)
92 {
93 gint i;
94
95 g_return_val_if_fail(url != NULL, NULL);
96
97 for (i = 0; i < history_size; ++i)
98 if (a_Url_cmp(url, history[i].url) == 0)
99 break;
100
101 if (i < history_size && history[i].title)
102 return history[i].title;
103 else if (force)
104 return URL_STR_(url);
105 return NULL;
106 }
107
108
109 /*
110 * Free all the memory used by this module
111 */
112 void a_History_free()
113 {
114 gint i;
115
116 for (i = 0; i < history_size; ++i) {
117 a_Url_free(history[i].url);
118 g_free(history[i].title);
119 }
120 g_free(history);
121 }
0
1 #ifndef __DILLO_HISTORY_H__
2 #define __DILLO_HISTORY_H__
3
4 #include <glib.h>
5
6 #include "url.h"
7
8
9 #ifdef __cplusplus
10 extern "C" {
11 #endif /* __cplusplus */
12
13 int a_History_add_url(DilloUrl *url);
14 int a_History_set_title(gint idx, const gchar *title);
15 DilloUrl *a_History_get_url(gint idx);
16 const gchar *a_History_get_title(gint idx);
17 const gchar *a_History_get_title_by_url(DilloUrl *url, gint force);
18 void a_History_free(void);
19
20
21 #ifdef __cplusplus
22 }
23 #endif /* __cplusplus */
24
25 #endif /* __DILLO_HISTORY_H__ */
0 /*
1 * File: html.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 * Copyright (C) 2000-2004 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * Dillo HTML parsing routines
15 */
16
17 /* Undefine if you want to unroll tables. For instance for PDAs */
18 #define USE_TABLES
19
20 /* Define to 1 to ignore white space immediately after an open tag,
21 * and immediately before a close tag. */
22 #define SGML_SPCDEL 0
23
24
25 #include <ctype.h> /* for isspace and tolower */
26 #include <string.h> /* for memcpy and memmove */
27 #include <stdlib.h>
28 #include <stdio.h> /* for sprintf */
29 #include <math.h> /* for rint */
30 #include <errno.h>
31
32 #include <gtk/gtk.h>
33
34 #include "msg.h"
35 #include "list.h"
36 #include "binaryconst.h"
37 #include "colors.h"
38 #include "dillo.h"
39 #include "history.h"
40 #include "nav.h"
41 #include "menu.h"
42 #include "commands.h"
43 #include "dw.h" /* for Dw_cursor_hand */
44
45 #include "dw_gtk_viewport.h"
46 #include "dw_gtk_scrolled_window.h"
47 #include "dw_widget.h"
48 #include "dw_page.h"
49 #include "dw_bullet.h"
50 #include "dw_button.h"
51 #include "dw_hruler.h"
52 #include "dw_embed_gtk.h"
53 #include "dw_table.h"
54 #include "dw_table_cell.h"
55 #include "dw_list_item.h"
56 #include "dw_style.h"
57 #include "interface.h"
58 #include "progressbar.h"
59 #include "prefs.h"
60 #include "misc.h"
61 #include "capi.h"
62 #include "html.h"
63
64 #define DEBUG_LEVEL 10
65 #include "debug.h"
66
67 typedef void (*TagOpenFunct) (DilloHtml *Html, char *Tag, gint Tagsize);
68 typedef void (*TagCloseFunct) (DilloHtml *Html, gint TagIdx);
69
70 #define TAB_SIZE 8
71
72 /*
73 * Forward declarations
74 */
75 static const char *Html_get_attr(DilloHtml *html,
76 const char *tag,
77 gint tagsize,
78 const char *attrname);
79 static const char *Html_get_attr2(DilloHtml *html,
80 const char *tag,
81 gint tagsize,
82 const char *attrname,
83 DilloHtmlTagParsingFlags flags);
84 static char *Html_get_attr_wdef(DilloHtml *html,
85 const char *tag,
86 gint tagsize,
87 const char *attrname,
88 const char *def);
89 static void Html_add_widget(DilloHtml *html, DwWidget *widget,
90 char *width_str, char *height_str,
91 DwStyle *style_attrs);
92 static gint Html_write_raw(DilloHtml *html, char *buf, gint bufsize, gint Eof);
93 static void Html_write(DilloHtml *html, char *Buf, gint BufSize, gint Eof);
94 static void Html_close(DilloHtml *html, gint ClientKey);
95 static void Html_callback(int Op, CacheClient_t *Client);
96 static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url);
97 static void Html_tag_open_input(DilloHtml *html, char *tag, gint tagsize);
98 static void Html_add_input(DilloHtmlForm *form,
99 DilloHtmlInputType type,
100 GtkWidget *widget,
101 const char *name,
102 const char *init_str,
103 DilloHtmlSelect *select,
104 gboolean init_val);
105 static void Html_submit_form(GtkWidget *submit, DilloHtmlLB *html_lb,
106 gint click_x, gint click_y);
107 static void Html_reset_form(GtkWidget *reset, DilloHtmlLB *html_lb);
108 static gint Html_tag_index(char *tag);
109
110 /* exported function */
111 DwWidget *a_Html_text(const char *Type, void *P, CA_Callback_t *Call,
112 void **Data);
113
114
115 /*
116 * Local Data
117 */
118
119 /* The following array of font sizes has to be _strictly_ crescent */
120 static const gint FontSizes[] = {8, 10, 12, 14, 18, 24};
121 static const gint FontSizesNum = 6;
122 static const gint FontSizesBase = 2;
123
124 /* Parsing table structure */
125 typedef struct {
126 gchar *name; /* element name */
127 unsigned char Flags; /* flags (explained near the table data) */
128 gchar EndTag; /* Is it Required, Optional or Forbidden */
129 guchar TagLevel; /* Used to heuristically parse bad HTML */
130 TagOpenFunct open; /* Open function */
131 TagCloseFunct close; /* Close function */
132 } TagInfo;
133 static const TagInfo Tags[];
134
135 /*
136 * Return the line number of the tag being processed by the parser.
137 */
138 static gint Html_get_line_number(DilloHtml *html)
139 {
140 gint i, ofs, line;
141 const char *p = html->Start_Buf;
142
143 g_return_val_if_fail(p != NULL, -1);
144
145 ofs = html->CurrTagOfs;
146 line = html->OldTagLine;
147 for (i = html->OldTagOfs; i < ofs; ++i)
148 if (p[i] == '\n')
149 ++line;
150 html->OldTagOfs = html->CurrTagOfs;
151 html->OldTagLine = line;
152 return line;
153 }
154
155 /*
156 * Collect HTML error strings inside the linkblock.
157 */
158 static void Html_msg(DilloHtml *html, const char *format, ... )
159 {
160 va_list argp;
161 gchar buf[512];
162
163 g_snprintf(buf, 512, "HTML warning: line %d, ",
164 Html_get_line_number(html));
165 g_string_append(html->linkblock->page_bugs, buf);
166 va_start(argp, format);
167 g_vsnprintf(buf, 512, format, argp);
168 va_end(argp);
169 g_string_append(html->linkblock->page_bugs, buf);
170 a_Interface_bug_meter_update(html->bw,
171 ++html->linkblock->num_page_bugs);
172 }
173
174 /*
175 * Wrapper for a_Url_new that adds an error detection message.
176 * (if use_base_url is TRUE, html->linkblock->base_url is used)
177 */
178 static DilloUrl *Html_url_new(DilloHtml *html,
179 const gchar *url_str, const gchar *base_url,
180 gint flags, gint32 posx, gint32 posy,
181 gint use_base_url)
182 {
183 DilloUrl *url;
184 gint n_ic, n_ic_spc;
185
186 url = a_Url_new(
187 url_str,
188 (use_base_url) ? base_url : URL_STR_(html->linkblock->base_url),
189 flags, posx, posy);
190 if ((n_ic = URL_ILLEGAL_CHARS(url)) != 0) {
191 const char *suffix = (n_ic) > 1 ? "s" : "";
192 n_ic_spc = URL_ILLEGAL_CHARS_SPC(url);
193 if (n_ic == n_ic_spc) {
194 MSG_HTML("URL has %d illegal character%s [%d space%s]\n",
195 n_ic, suffix, n_ic_spc, suffix);
196 } else if (n_ic_spc == 0) {
197 MSG_HTML("URL has %d illegal character%s [%d in (00-1F or 7F)]\n",
198 n_ic, suffix, n_ic);
199 } else {
200 MSG_HTML("URL has %d illegal character%s "
201 "[%d space%s and %d in (00-1F or 7F)]\n",
202 n_ic, suffix, n_ic_spc, n_ic_spc ? "s" : "", n_ic-n_ic_spc);
203 }
204 }
205 return url;
206 }
207
208 /*
209 * Set callback function and callback data for "html/text" MIME type.
210 */
211 DwWidget *a_Html_text(const char *Type, void *P, CA_Callback_t *Call,
212 void **Data)
213 {
214 DilloWeb *web = P;
215 DilloHtml *html = Html_new(web->bw, web->url);
216
217 *Data = (void *) html;
218 *Call = (CA_Callback_t) Html_callback;
219
220 return html->dw;
221 }
222
223 /*
224 * We'll make the linkblock first to get it out of the way.
225 */
226 static DilloHtmlLB *Html_lb_new(BrowserWindow *bw, const DilloUrl *url)
227 {
228 DilloHtmlLB *html_lb = g_new(DilloHtmlLB, 1);
229
230 html_lb->bw = bw;
231 html_lb->base_url = a_Url_dup(url);
232 html_lb->num_forms_max = 1;
233 html_lb->num_forms = 0;
234 html_lb->forms = NULL;
235
236 html_lb->num_links_max = 1;
237 html_lb->num_links = 0;
238 html_lb->links = NULL;
239 a_Dw_image_map_list_init(&html_lb->maps);
240
241 html_lb->link_color = prefs.link_color;
242 html_lb->visited_color = prefs.visited_color;
243
244 html_lb->num_page_bugs = 0;
245 html_lb->page_bugs = g_string_new("");
246
247 return html_lb;
248 }
249
250 /*
251 * Free the memory used by the linkblock
252 */
253 static void Html_lb_free(void *lb)
254 {
255 gint i, j, k;
256 DilloHtmlForm *form;
257 DilloHtmlLB *html_lb = lb;
258
259 DEBUG_MSG(3, "Html_lb_free\n");
260
261 a_Url_free(html_lb->base_url);
262
263 for (i = 0; i < html_lb->num_forms; i++) {
264 form = &html_lb->forms[i];
265 a_Url_free(form->action);
266 for (j = 0; j < form->num_inputs; j++) {
267 g_free(form->inputs[j].name);
268 g_free(form->inputs[j].init_str);
269
270 if (form->inputs[j].type == DILLO_HTML_INPUT_SELECT ||
271 form->inputs[j].type == DILLO_HTML_INPUT_SEL_LIST) {
272 for (k = 0; k < form->inputs[j].select->num_options; k++) {
273 g_free(form->inputs[j].select->options[k].value);
274 }
275 g_free(form->inputs[j].select->options);
276 g_free(form->inputs[j].select);
277 }
278 }
279 g_free(form->inputs);
280 }
281 g_free(html_lb->forms);
282
283 for (i = 0; i < html_lb->num_links; i++)
284 if (html_lb->links[i])
285 a_Url_free(html_lb->links[i]);
286 g_free(html_lb->links);
287
288 a_Dw_image_map_list_free(&html_lb->maps);
289
290 g_string_free(html_lb->page_bugs, TRUE);
291
292 g_free(html_lb);
293 }
294
295
296 /*
297 * Set the URL data for image maps.
298 */
299 static void Html_set_link_coordinates(DilloHtmlLB *lb,
300 gint link, gint x, gint y)
301 {
302 gchar data[64];
303
304 if (x != -1) {
305 g_snprintf(data, 64, "?%d,%d", x, y);
306 a_Url_set_ismap_coords(lb->links[link], data);
307 }
308 }
309
310 /*
311 * Handle the status function generated by the dw scroller,
312 * and show the url in the browser status-bar.
313 */
314 static void Html_handle_status(DwWidget *widget, gint link, gint x, gint y,
315 DilloHtmlLB *lb)
316 {
317 DilloUrl *url;
318
319 url = (link == -1) ? NULL : lb->links[link];
320 if (url) {
321 Html_set_link_coordinates(lb, link, x, y);
322 a_Interface_msg(lb->bw, "%s",
323 URL_ALT_(url) ? URL_ALT_(url) : URL_STR_(url));
324 a_Dw_widget_set_cursor (widget, Dw_cursor_hand);
325 lb->bw->status_is_link = 1;
326
327 } else {
328 if (lb->bw->status_is_link)
329 a_Interface_msg(lb->bw, "");
330 a_Dw_widget_set_cursor (widget, NULL);
331 }
332 }
333
334 /*
335 * Popup the link menu ("link_pressed" callback of the page)
336 */
337 static gboolean Html_link_menu(DwWidget *widget, gint link, gint x, gint y,
338 GdkEventButton *event, DilloHtmlLB *lb)
339 {
340 DwWidget *widget_at_cursor;
341 gboolean show_oi = FALSE;
342
343 if (event->button == 3) {
344 Html_set_link_coordinates(lb, link, x, y);
345 a_Menu_popup_set_url(lb->bw, lb->links[link]);
346
347 /* if we've got an image, prepare the image popup */
348 widget_at_cursor =
349 a_Dw_gtk_scrolled_window_widget_at_viewport_point(
350 GTK_DW_SCROLLED_WINDOW (lb->bw->docwin), event->x, event->y);
351 if (widget_at_cursor && DW_IS_IMAGE (widget_at_cursor)) {
352 DwImage *image = DW_IMAGE (widget_at_cursor);
353 /* test image->url (it may have not started to arrive yet!) */
354 if (image->url) {
355 /* use the second URL for this popup */
356 gtk_object_set_data(GTK_OBJECT (lb->bw->menu_popup.over_image),
357 "url2", GINT_TO_POINTER(2));
358 a_Menu_popup_set_url2(lb->bw, image->url);
359 show_oi = TRUE;
360 }
361 }
362 a_Menu_popup_ol_show_oi(lb->bw, show_oi);
363
364 gtk_menu_popup(GTK_MENU(lb->bw->menu_popup.over_link), NULL, NULL,
365 NULL, NULL, event->button, event->time);
366 return TRUE;
367 }
368
369 return FALSE;
370 }
371
372
373 /*
374 * Activate a link ("link_clicked" callback of the page)
375 */
376 static gboolean Html_link_clicked(DwWidget *widget, gint link, gint x, gint y,
377 GdkEventButton *event, DilloHtmlLB *lb)
378 {
379 Html_set_link_coordinates(lb, link, x, y);
380 if (event->button == 1)
381 a_Nav_push(lb->bw, lb->links[link]);
382 else if (event->button == 2) {
383 a_Nav_push_nw(lb->bw, lb->links[link]);
384 } else {
385 return FALSE;
386 }
387
388 if (DW_IS_PAGE (widget))
389 a_Dw_page_change_link_color (DW_PAGE (widget), link, lb->visited_color);
390
391 return TRUE;
392 }
393
394 /*
395 * Popup the image menu ("button_press_event" callback of image)
396 */
397 static gboolean Html_image_menu(DwWidget *widget,
398 gint32 x, gint32 y, GdkEventButton *event,
399 BrowserWindow *bw)
400 {
401 DwImage *image = DW_IMAGE (widget);
402 if (event->button == 3 && image->url) {
403 a_Menu_popup_set_url(bw, image->url);
404 a_Menu_popup_clear_url2(bw->menu_popup.over_image);
405
406 gtk_menu_popup(GTK_MENU(bw->menu_popup.over_image), NULL, NULL,
407 NULL, NULL, event->button, event->time);
408 return TRUE;
409 }
410
411 return FALSE;
412 }
413
414 /*
415 * Popup the page menu ("button_press_event" callback of the viewport)
416 */
417 static int Html_page_menu(GtkWidget *viewport, GdkEventButton *event,
418 BrowserWindow *bw)
419 {
420 gpointer bug_pix;
421
422 if (event->button == 3) {
423 /* set the working URL */
424 a_Menu_popup_set_url(bw, a_History_get_url(NAV_TOP(bw)));
425 /* set "View page Bugs" sensitivity */
426 bug_pix = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "bug");
427 gtk_widget_set_sensitive(bw->viewbugs_menuitem,
428 GTK_WIDGET_VISIBLE(GTK_WIDGET(bug_pix)));
429 gtk_menu_popup(GTK_MENU(bw->menu_popup.over_page), NULL, NULL,
430 NULL, NULL, event->button, event->time);
431 return TRUE;
432 } else
433 return FALSE;
434 }
435
436 /*
437 * Connect all signals of a page or an image.
438 */
439 static void Html_connect_signals(DilloHtml *html, GtkObject *widget)
440 {
441 gtk_signal_connect (widget, "link_entered",
442 GTK_SIGNAL_FUNC(Html_handle_status),
443 (gpointer)html->linkblock);
444 gtk_signal_connect (widget, "link_pressed", GTK_SIGNAL_FUNC(Html_link_menu),
445 (gpointer)html->linkblock);
446 gtk_signal_connect (widget, "link_clicked",
447 GTK_SIGNAL_FUNC(Html_link_clicked),
448 (gpointer)html->linkblock);
449 }
450
451
452 /*
453 * Create a new link in the linkblock, set it as the url's parent
454 * and return the index.
455 */
456 static gint Html_set_new_link(DilloHtml *html, DilloUrl **url)
457 {
458 gint nl;
459
460 nl = html->linkblock->num_links;
461 a_List_add(html->linkblock->links, nl, html->linkblock->num_links_max);
462 html->linkblock->links[nl] = (*url) ? *url : NULL;
463 return html->linkblock->num_links++;
464 }
465
466
467 /*
468 * Check an integer value to be inside a range.
469 * Return: 'n' if valid, 'def' if not.
470 */
471 static int Html_check_int(int n, int min, int max, int def)
472 {
473 return (n >= min && n <= max) ? n : def;
474 }
475
476 /*
477 * Allocate and insert form information into the Html linkblock
478 */
479 static gint Html_form_new(DilloHtmlLB *html_lb,
480 DilloHtmlMethod method,
481 const DilloUrl *action,
482 DilloHtmlEnc enc)
483 {
484 gint nf;
485
486 a_List_add(html_lb->forms, html_lb->num_forms, html_lb->num_forms_max);
487
488 nf = html_lb->num_forms;
489 html_lb->forms[nf].method = method;
490 html_lb->forms[nf].action = a_Url_dup(action);
491 html_lb->forms[nf].enc = enc;
492 html_lb->forms[nf].num_inputs = 0;
493 html_lb->forms[nf].num_inputs_max = 4;
494 html_lb->forms[nf].inputs = NULL;
495 html_lb->forms[nf].num_entry_fields = 0;
496 html_lb->forms[nf].num_submit_buttons = 0;
497 html_lb->num_forms++;
498
499 _MSG("Html_form_new: action=%s nform=%d\n", action, nf);
500 return nf;
501 }
502
503
504 /*
505 * Change one toplevel attribute. var should be an identifier. val is
506 * only evaluated once, so you can safely use a function call for it.
507 */
508 #define HTML_SET_TOP_ATTR(html, var, val) \
509 do { \
510 DwStyle style_attrs, *old_style; \
511 \
512 old_style = (html)->stack[(html)->stack_top].style; \
513 style_attrs = *old_style; \
514 style_attrs.var = (val); \
515 (html)->stack[(html)->stack_top].style = \
516 a_Dw_style_new (&style_attrs, (html)->bw->main_window->window); \
517 a_Dw_style_unref (old_style); \
518 } while (FALSE)
519
520 /*
521 * Set the font at the top of the stack. BImask specifies which
522 * attributes in BI should be changed.
523 */
524 static void Html_set_top_font(DilloHtml *html, gchar *name, gint size,
525 gint BI, gint BImask)
526 {
527 DwStyleFont font_attrs;
528
529 font_attrs = *html->stack[(html)->stack_top].style->font;
530 if ( name )
531 font_attrs.name = name;
532 if ( size )
533 font_attrs.size = size;
534 if ( BImask & 1 )
535 font_attrs.weight = (BI & 1) ? 700 : 400;
536 if ( BImask & 2 )
537 font_attrs.style = (BI & 2) ?
538 (prefs.use_oblique ?
539 DW_STYLE_FONT_STYLE_OBLIQUE : DW_STYLE_FONT_STYLE_ITALIC) :
540 DW_STYLE_FONT_STYLE_NORMAL;
541
542 HTML_SET_TOP_ATTR (html, font, a_Dw_style_font_new (&font_attrs));
543 }
544
545 /*
546 * Evaluates the ALIGN attribute (left|center|right|justify) and
547 * sets the style at the top of the stack.
548 */
549 static void Html_tag_set_align_attr(DilloHtml *html, char *tag, gint tagsize)
550 {
551 const char *align, *charattr;
552
553 if ((align = Html_get_attr(html, tag, tagsize, "align"))) {
554 if (g_strcasecmp (align, "left") == 0)
555 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_LEFT);
556 else if (g_strcasecmp (align, "right") == 0)
557 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_RIGHT);
558 else if (g_strcasecmp (align, "center") == 0)
559 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_CENTER);
560 else if (g_strcasecmp (align, "justify") == 0)
561 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_JUSTIFY);
562 else if (g_strcasecmp (align, "char") == 0) {
563 /* todo: Actually not supported for <p> etc. */
564 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_STRING);
565 if ((charattr = Html_get_attr(html, tag, tagsize, "char"))) {
566 if (charattr[0] == 0)
567 /* todo: ALIGN=" ", and even ALIGN="&32;" will reult in
568 * an empty string (don't know whether the latter is
569 * correct, has to be clarified with the specs), so
570 * that for empty strings, " " is assumed. */
571 HTML_SET_TOP_ATTR (html, text_align_char, ' ');
572 else
573 HTML_SET_TOP_ATTR (html, text_align_char, charattr[0]);
574 } else
575 /* todo: Examine LANG attr of <html>. */
576 HTML_SET_TOP_ATTR (html, text_align_char, '.');
577 }
578 }
579 }
580
581 /*
582 * Evaluates the VALIGN attribute (top|bottom|middle|baseline) and
583 * sets the style in style_attrs. Returns TRUE when set.
584 */
585 static gboolean Html_tag_set_valign_attr(DilloHtml *html, char *tag,
586 gint tagsize, DwStyle *style_attrs)
587 {
588 const char *attr;
589
590 if ((attr = Html_get_attr(html, tag, tagsize, "valign"))) {
591 if (g_strcasecmp (attr, "top") == 0)
592 style_attrs->valign = DW_STYLE_VALIGN_TOP;
593 else if (g_strcasecmp (attr, "bottom") == 0)
594 style_attrs->valign = DW_STYLE_VALIGN_BOTTOM;
595 else if (g_strcasecmp (attr, "baseline") == 0)
596 style_attrs->valign = DW_STYLE_VALIGN_BASELINE;
597 else
598 style_attrs->valign = DW_STYLE_VALIGN_MIDDLE;
599 return TRUE;
600 } else
601 return FALSE;
602 }
603
604
605 /*
606 * Add a new DwPage into the current DwPage, for indentation.
607 * left and right are the horizontal indentation amounts, space is the
608 * vertical space around the block.
609 */
610 static void Html_add_indented_widget(DilloHtml *html, DwWidget *page,
611 int left, int right, int space)
612 {
613 DwStyle style_attrs, *style;
614
615 style_attrs = *html->stack[html->stack_top].style;
616
617 a_Dw_style_box_set_val(&style_attrs.margin, 0);
618 a_Dw_style_box_set_val(&style_attrs.border_width, 0);
619 a_Dw_style_box_set_val(&style_attrs.padding, 0);
620
621 /* Activate this for debugging */
622 #if 0
623 a_Dw_style_box_set_val(&style_attrs.border_width, 1);
624 a_Dw_style_box_set_border_color
625 (&style_attrs,
626 a_Dw_style_shaded_color_new(style_attrs.color->color_val,
627 html->bw->main_window->window));
628 a_Dw_style_box_set_border_style(&style_attrs, DW_STYLE_BORDER_DASHED);
629 #endif
630
631 style_attrs.margin.left = left;
632 style_attrs.margin.right = right;
633 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
634
635 a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style);
636 a_Dw_page_add_widget (DW_PAGE (html->dw), page, style);
637 a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style);
638 html->stack[html->stack_top].page = html->dw = page;
639 html->stack[html->stack_top].hand_over_break = TRUE;
640 a_Dw_style_unref (style);
641
642 /* Handle it when the user clicks on a link */
643 Html_connect_signals(html, GTK_OBJECT(page));
644 }
645
646 /*
647 * Create and add a new indented DwPage to the current DwPage
648 */
649 static void Html_add_indented(DilloHtml *html, int left, int right, int space)
650 {
651 DwWidget *page = a_Dw_page_new ();
652 Html_add_indented_widget (html, page, left, right, space);
653 }
654
655 /*
656 * Given a font_size, this will return the correct 'level'.
657 * (or the closest, if the exact level isn't found).
658 */
659 static gint Html_fontsize_to_level(gint fontsize)
660 {
661 gint i, level;
662 gdouble normalized_size = fontsize / prefs.font_factor,
663 approximation = FontSizes[FontSizesNum-1] + 1;
664
665 for (i = level = 0; i < FontSizesNum; i++)
666 if (approximation >= fabs(normalized_size - FontSizes[i])) {
667 approximation = fabs(normalized_size - FontSizes[i]);
668 level = i;
669 } else {
670 break;
671 }
672
673 return level;
674 }
675
676 /*
677 * Given a level of a font, this will return the correct 'size'.
678 */
679 static gint Html_level_to_fontsize(gint level)
680 {
681 level = MAX(0, level);
682 level = MIN(FontSizesNum - 1, level);
683
684 return rint(FontSizes[level]*prefs.font_factor);
685 }
686
687 /*
688 * Miscelaneous initializations for a DwPage
689 */
690 static void Html_set_dwpage(DilloHtml *html)
691 {
692 DwWidget *widget;
693 DwPage *page;
694 DwStyle style_attrs;
695 DwStyleFont font;
696
697 g_return_if_fail (html->dw == NULL);
698
699 widget = a_Dw_page_new ();
700 page = DW_PAGE (widget);
701 html->dw = html->stack[0].page = widget;
702
703 /* Create a dummy font, attribute, and tag for the bottom of the stack. */
704 font.name = prefs.vw_fontname; /* Helvetica */
705 font.size = Html_level_to_fontsize(FontSizesBase);
706 font.weight = 400;
707 font.style = DW_STYLE_FONT_STYLE_NORMAL;
708
709 a_Dw_style_init_values (&style_attrs, html->bw->main_window->window);
710 style_attrs.font = a_Dw_style_font_new (&font);
711 style_attrs.color = a_Dw_style_color_new (prefs.text_color,
712 html->bw->main_window->window);
713 html->stack[0].style = a_Dw_style_new (&style_attrs,
714 html->bw->main_window->window);
715
716 html->stack[0].table_cell_style = NULL;
717
718 /* Handle it when the user clicks on a link */
719 Html_connect_signals(html, GTK_OBJECT(widget));
720
721 gtk_signal_connect_while_alive (
722 GTK_OBJECT(GTK_BIN(html->bw->docwin)->child), "button_press_event",
723 GTK_SIGNAL_FUNC(Html_page_menu), (gpointer)html->bw, GTK_OBJECT (page));
724
725 /* Connect the "bug meter" button-press to the linkblock */
726 gtk_signal_connect_while_alive(
727 GTK_OBJECT (html->bw->status_bug_meter), "clicked",
728 GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback),
729 (gpointer)html->linkblock, GTK_OBJECT (page));
730 gtk_signal_connect_while_alive(
731 GTK_OBJECT (html->bw->status_bug_meter), "clicked1",
732 GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback),
733 (gpointer)html->linkblock, GTK_OBJECT (page));
734 /* also connect with the "View page Bugs" menuitem */
735 gtk_signal_connect_while_alive(
736 GTK_OBJECT (html->bw->viewbugs_menuitem), "activate",
737 GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback),
738 (gpointer)html->linkblock, GTK_OBJECT (page));
739
740 /* Destroy the linkblock when the DwPage is destroyed */
741 gtk_signal_connect_object(GTK_OBJECT(page), "destroy",
742 GTK_SIGNAL_FUNC(Html_lb_free),
743 (gpointer)html->linkblock);
744 }
745
746 /*
747 * Create and initialize a new DilloHtml structure
748 */
749 static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url)
750 {
751 DilloHtml *html;
752
753 html = g_new(DilloHtml, 1);
754
755 html->Start_Buf = NULL;
756 html->Start_Ofs = 0;
757 html->CurrTagOfs = 0;
758 html->OldTagOfs = 0;
759 html->OldTagLine = 1;
760
761 html->DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */
762 html->DocTypeVersion = 0.0f;
763
764 html->dw = NULL;
765 html->bw = bw;
766 html->linkblock = Html_lb_new(bw, url);
767
768 html->stack_max = 16;
769 html->stack_top = 0;
770 html->stack = g_new(DilloHtmlState, html->stack_max);
771 html->stack[0].tag_name = g_strdup("none");
772 html->stack[0].style = NULL;
773 html->stack[0].table_cell_style = NULL;
774 html->stack[0].parse_mode = DILLO_HTML_PARSE_MODE_INIT;
775 html->stack[0].table_mode = DILLO_HTML_TABLE_MODE_NONE;
776 html->stack[0].cell_text_align_set = FALSE;
777 html->stack[0].list_type = HTML_LIST_NONE; /* no <ul> or <ol> open */
778 html->stack[0].list_number = 0;
779 html->stack[0].tag_idx = -1; /* MUST not be used */
780 html->stack[0].page = NULL;
781 html->stack[0].table = NULL;
782 html->stack[0].ref_list_item = NULL;
783 html->stack[0].current_bg_color = prefs.bg_color;
784 html->stack[0].hand_over_break = FALSE;
785
786 html->Stash = g_string_new("");
787 html->StashSpace = FALSE;
788
789 html->SPCBuf = NULL;
790
791 html->pre_column = 0;
792 html->PreFirstChar = FALSE;
793 html->PrevWasCR = FALSE;
794 html->PrevWasOpenTag = FALSE;
795 html->SPCPending = FALSE;
796 html->InVisitedLink = FALSE;
797 html->ReqTagClose = FALSE;
798 html->CloseOneTag = FALSE;
799 html->TagSoup = TRUE;
800 html->NameVal = NULL;
801
802 html->Num_HTML = html->Num_HEAD = html->Num_BODY = html->Num_TITLE = 0;
803
804 html->InFlags = 0;
805
806 html->attr_data = g_string_sized_new(1024);
807
808 Html_set_dwpage(html);
809
810 return html;
811 }
812
813 /*
814 * Initialize the stash buffer
815 */
816 static void Html_stash_init(DilloHtml *html)
817 {
818 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_STASH;
819 html->StashSpace = FALSE;
820 g_string_truncate(html->Stash, 0);
821 }
822
823 /* Entities list from the HTML 4.01 DTD */
824 typedef struct {
825 char *entity;
826 int isocode;
827 } Ent_t;
828
829 #define NumEnt 252
830 static const Ent_t Entities[NumEnt] = {
831 {"AElig",0306}, {"Aacute",0301}, {"Acirc",0302}, {"Agrave",0300},
832 {"Alpha",01621},{"Aring",0305}, {"Atilde",0303}, {"Auml",0304},
833 {"Beta",01622}, {"Ccedil",0307}, {"Chi",01647}, {"Dagger",020041},
834 {"Delta",01624},{"ETH",0320}, {"Eacute",0311}, {"Ecirc",0312},
835 {"Egrave",0310},{"Epsilon",01625},{"Eta",01627}, {"Euml",0313},
836 {"Gamma",01623},{"Iacute",0315}, {"Icirc",0316}, {"Igrave",0314},
837 {"Iota",01631}, {"Iuml",0317}, {"Kappa",01632}, {"Lambda",01633},
838 {"Mu",01634}, {"Ntilde",0321}, {"Nu",01635}, {"OElig",0522},
839 {"Oacute",0323},{"Ocirc",0324}, {"Ograve",0322}, {"Omega",01651},
840 {"Omicron",01637},{"Oslash",0330},{"Otilde",0325},{"Ouml",0326},
841 {"Phi",01646}, {"Pi",01640}, {"Prime",020063},{"Psi",01650},
842 {"Rho",01641}, {"Scaron",0540}, {"Sigma",01643}, {"THORN",0336},
843 {"Tau",01644}, {"Theta",01630}, {"Uacute",0332}, {"Ucirc",0333},
844 {"Ugrave",0331},{"Upsilon",01645},{"Uuml",0334}, {"Xi",01636},
845 {"Yacute",0335},{"Yuml",0570}, {"Zeta",01626}, {"aacute",0341},
846 {"acirc",0342}, {"acute",0264}, {"aelig",0346}, {"agrave",0340},
847 {"alefsym",020465},{"alpha",01661},{"amp",38}, {"and",021047},
848 {"ang",021040}, {"aring",0345}, {"asymp",021110},{"atilde",0343},
849 {"auml",0344}, {"bdquo",020036},{"beta",01662}, {"brvbar",0246},
850 {"bull",020042},{"cap",021051}, {"ccedil",0347}, {"cedil",0270},
851 {"cent",0242}, {"chi",01707}, {"circ",01306}, {"clubs",023143},
852 {"cong",021105},{"copy",0251}, {"crarr",020665},{"cup",021052},
853 {"curren",0244},{"dArr",020723}, {"dagger",020040},{"darr",020623},
854 {"deg",0260}, {"delta",01664}, {"diams",023146},{"divide",0367},
855 {"eacute",0351},{"ecirc",0352}, {"egrave",0350}, {"empty",021005},
856 {"emsp",020003},{"ensp",020002}, {"epsilon",01665},{"equiv",021141},
857 {"eta",01667}, {"eth",0360}, {"euml",0353}, {"euro",020254},
858 {"exist",021003},{"fnof",0622}, {"forall",021000},{"frac12",0275},
859 {"frac14",0274},{"frac34",0276}, {"frasl",020104},{"gamma",01663},
860 {"ge",021145}, {"gt",62}, {"hArr",020724}, {"harr",020624},
861 {"hearts",023145},{"hellip",020046},{"iacute",0355},{"icirc",0356},
862 {"iexcl",0241}, {"igrave",0354}, {"image",020421},{"infin",021036},
863 {"int",021053}, {"iota",01671}, {"iquest",0277}, {"isin",021010},
864 {"iuml",0357}, {"kappa",01672}, {"lArr",020720}, {"lambda",01673},
865 {"lang",021451},{"laquo",0253}, {"larr",020620}, {"lceil",021410},
866 {"ldquo",020034},{"le",021144}, {"lfloor",021412},{"lowast",021027},
867 {"loz",022712}, {"lrm",020016}, {"lsaquo",020071},{"lsquo",020030},
868 {"lt",60}, {"macr",0257}, {"mdash",020024},{"micro",0265},
869 {"middot",0267},{"minus",021022},{"mu",01674}, {"nabla",021007},
870 {"nbsp",32}, {"ndash",020023},{"ne",021140}, {"ni",021013},
871 {"not",0254}, {"notin",021011},{"nsub",021204}, {"ntilde",0361},
872 {"nu",01675}, {"oacute",0363}, {"ocirc",0364}, {"oelig",0523},
873 {"ograve",0362},{"oline",020076},{"omega",01711}, {"omicron",01677},
874 {"oplus",021225},{"or",021050}, {"ordf",0252}, {"ordm",0272},
875 {"oslash",0370},{"otilde",0365}, {"otimes",021227},{"ouml",0366},
876 {"para",0266}, {"part",021002}, {"permil",020060},{"perp",021245},
877 {"phi",01706}, {"pi",01700}, {"piv",01726}, {"plusmn",0261},
878 {"pound",0243}, {"prime",020062},{"prod",021017}, {"prop",021035},
879 {"psi",01710}, {"quot",34}, {"rArr",020722}, {"radic",021032},
880 {"rang",021452},{"raquo",0273}, {"rarr",020622}, {"rceil",021411},
881 {"rdquo",020035},{"real",020434},{"reg",0256}, {"rfloor",021413},
882 {"rho",01701}, {"rlm",020017}, {"rsaquo",020072},{"rsquo",020031},
883 {"sbquo",020032},{"scaron",0541},{"sdot",021305}, {"sect",0247},
884 {"shy",0255}, {"sigma",01703}, {"sigmaf",01702},{"sim",021074},
885 {"spades",023140},{"sub",021202},{"sube",021206}, {"sum",021021},
886 {"sup",021203}, {"sup1",0271}, {"sup2",0262}, {"sup3",0263},
887 {"supe",021207},{"szlig",0337}, {"tau",01704}, {"there4",021064},
888 {"theta",01670},{"thetasym",01721},{"thinsp",020011},{"thorn",0376},
889 {"tilde",01334},{"times",0327}, {"trade",020442},{"uArr",020721},
890 {"uacute",0372},{"uarr",020621}, {"ucirc",0373}, {"ugrave",0371},
891 {"uml",0250}, {"upsih",01722}, {"upsilon",01705},{"uuml",0374},
892 {"weierp",020430},{"xi",01676}, {"yacute",0375}, {"yen",0245},
893 {"yuml",0377}, {"zeta",01666}, {"zwj",020015}, {"zwnj",020014}
894 };
895
896
897 /*
898 * Comparison function for binary search
899 */
900 static int Html_entity_comp(const void *a, const void *b)
901 {
902 return strcmp(((Ent_t *)a)->entity, ((Ent_t *)b)->entity);
903 }
904
905 /*
906 * Binary search of 'key' in entity list
907 */
908 static int Html_entity_search(char *key)
909 {
910 Ent_t *res, EntKey;
911
912 EntKey.entity = key;
913 res = bsearch(&EntKey, Entities, NumEnt, sizeof(Ent_t), Html_entity_comp);
914 if ( res )
915 return (res - Entities);
916 return -1;
917 }
918
919 /*
920 * Switch a few UCS encodings to latin1.
921 */
922 static gint Html_try_ucs2latin1(gint isocode)
923 {
924 gint ret;
925 switch (isocode) {
926 case 0x2018:
927 case 0x2019: ret = '\''; break;
928 case 0x201c:
929 case 0x201d: ret = '"'; break;
930 case 0x2013:
931 case 0x2014: ret = '-'; break;
932 case 0x2039: ret = '<'; break;
933 case 0x203a: ret = '>'; break;
934 case 0x2022: ret = 176; break;
935 default: ret = -1; break;
936 }
937 return ret;
938 }
939
940 /*
941 * Switch a few 'undefined for HTML' ASCII encodings to latin1.
942 */
943 static gint Html_try_ascii2latin1(gint isocode)
944 {
945 gint ret;
946 switch (isocode) {
947 case 145:
948 case 146: ret = '\''; break;
949 case 147:
950 case 148: ret = '"'; break;
951 case 149: ret = 176; break;
952 case 150:
953 case 151: ret = '-'; break;
954 default: ret = isocode; break;
955 }
956 return ret;
957 }
958
959 /*
960 * Given an entity, return the ISO-Latin1 character code.
961 * Returns a negative value (error code) if not a valid entity.
962 *
963 * The first character *token is assumed to be == '&'
964 *
965 * For valid entities, *entsize is set to the length of the parsed entity.
966 */
967 static gint Html_parse_entity(DilloHtml *html, const gchar *token,
968 gint toksize, gint *entsize)
969 {
970 gint isocode, i;
971 gchar *tok, *s, c;
972
973 token++;
974 tok = s = toksize ? g_strndup(token, (guint)toksize) : g_strdup(token);
975
976 isocode = -1;
977
978 if (*s == '#') {
979 /* numeric character reference */
980 errno = 0;
981 if (*++s == 'x' || *s == 'X') {
982 if (isxdigit(*++s)) {
983 /* strtol with base 16 accepts leading "0x" - we don't */
984 if (*s == '0' && s[1] == 'x') {
985 s++;
986 isocode = 0;
987 } else {
988 isocode = strtol(s, &s, 16);
989 }
990 }
991 } else if (isdigit(*s)) {
992 isocode = strtol(s, &s, 10);
993 }
994
995 if (!isocode || errno || isocode > 0x7fffffffL) {
996 /* this catches null bytes, errors and codes >=2^31 */
997 MSG_HTML("numeric character reference out of range\n");
998 isocode = -2;
999 }
1000
1001 if (isocode != -1) {
1002 if (*s == ';')
1003 s++;
1004 else if (prefs.show_extra_warnings)
1005 MSG_HTML("numeric character reference without trailing ';'\n");
1006 }
1007
1008 } else if (isalpha(*s)) {
1009 /* character entity reference */
1010 while (isalnum(*++s) || strchr(":_.-", *s));
1011 c = *s;
1012 *s = 0;
1013
1014 if (c != ';' || (i = Html_entity_search(tok)) == -1) {
1015 if ((html->DocType == DT_HTML && html->DocTypeVersion == 4.01f) ||
1016 html->DocType == DT_XHTML)
1017 MSG_HTML("undefined character entity '%s'\n", tok);
1018 isocode = -3;
1019 } else
1020 isocode = Entities[i].isocode;
1021
1022 if (c == ';')
1023 s++;
1024 else if (prefs.show_extra_warnings)
1025 MSG_HTML("character entity reference without trailing ';'\n");
1026 }
1027
1028 *entsize = s-tok+1;
1029 g_free(tok);
1030
1031 if (isocode >= 128 && isocode <= 159) {
1032 MSG_HTML("code positions 128-159 are not defined for ISO Latin-1\n");
1033 isocode = Html_try_ascii2latin1(isocode);
1034 } else if (isocode > 255)
1035 /* Try a few UCS translations to Latin1 */
1036 isocode = Html_try_ucs2latin1(isocode);
1037 else if (isocode == -1 && prefs.show_extra_warnings)
1038 MSG_HTML("literal '&'\n");
1039
1040 return isocode;
1041 }
1042
1043 /*
1044 * Convert all the entities in a token to plain ISO character codes. Takes
1045 * a token and its length, and returns a newly allocated string.
1046 */
1047 static char *
1048 Html_parse_entities(DilloHtml *html, gchar *token, gint toksize)
1049 {
1050 gchar *esc_set = "&\xE2\xC2";
1051 gchar *new_str;
1052 gint i, j, isocode, entsize;
1053
1054 new_str = g_strndup(token, toksize);
1055 if (new_str[strcspn(new_str, esc_set)] == 0)
1056 return new_str;
1057
1058 for (i = j = 0; i < toksize; i++) {
1059 if (token[i] == '&' &&
1060 (isocode = Html_parse_entity(html, token+i,
1061 toksize-i, &entsize)) >= 0) {
1062 new_str[j++] = (gchar) isocode;
1063 i += entsize-1;
1064
1065 } else if (token[i] == '\xE2' && token[i+1] == '\x80' && i+2 < toksize){
1066 /* Hack: for parsing some UTF-8 characters into latin1 */
1067 switch (token[i+2]) {
1068 case '\x94':
1069 new_str[j++] = '-';
1070 new_str[j++] = '-';
1071 break;
1072 case '\x98':
1073 case '\x99':
1074 new_str[j++] = '\'';
1075 break;
1076 case '\x9C':
1077 case '\x9D':
1078 new_str[j++] = '"';
1079 break;
1080 case '\xA2':
1081 new_str[j++] = '*';
1082 new_str[j++] = ' ';
1083 break;
1084 default: /* unhandled */
1085 new_str[j++] = '\xE2';
1086 break;
1087 }
1088 i += 2;
1089
1090 } else if (token[i] == '\xC2' && token[i+1] == '\xA0') {
1091 /* Hack: for parsing some UTF-8 characters into latin1 */
1092 new_str[j++] = ' ';
1093 ++i;
1094
1095 } else {
1096 new_str[j++] = token[i];
1097 }
1098 }
1099 new_str[j] = '\0';
1100 return new_str;
1101 }
1102
1103 /*
1104 * Parse spaces
1105 *
1106 */
1107 static void Html_process_space(DilloHtml *html, char *space, gint spacesize)
1108 {
1109 gint i, offset;
1110 DilloHtmlParseMode parse_mode = html->stack[html->stack_top].parse_mode;
1111
1112 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ) {
1113 html->StashSpace = (html->Stash->len > 0);
1114 html->SPCPending = FALSE;
1115
1116 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
1117 char *Pword = g_strndup(space, spacesize);
1118 g_string_append(html->Stash, Pword);
1119 g_free(Pword);
1120 html->SPCPending = FALSE;
1121
1122 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_PRE ) {
1123 /* re-scan the string for characters that cause line breaks */
1124 for (i = 0; i < spacesize; i++) {
1125 /* Support for "\r", "\n" and "\r\n" line breaks (skips the first) */
1126 if (!html->PreFirstChar &&
1127 (space[i] == '\r' || (space[i] == '\n' && !html->PrevWasCR))) {
1128 a_Dw_page_add_linebreak(DW_PAGE (html->dw),
1129 html->stack[(html)->stack_top].style);
1130 html->pre_column = 0;
1131 }
1132 html->PreFirstChar = FALSE;
1133
1134 /* cr and lf should not be rendered -- they appear as a break */
1135 switch (space[i]) {
1136 case '\r':
1137 case '\n':
1138 break;
1139 case '\t':
1140 if (prefs.show_extra_warnings)
1141 MSG_HTML("TAB character inside <PRE>\n");
1142 offset = TAB_SIZE - html->pre_column % TAB_SIZE;
1143 a_Dw_page_add_text(DW_PAGE (html->dw),
1144 g_strnfill(offset, ' '),
1145 html->stack[html->stack_top].style);
1146 html->pre_column += offset;
1147 break;
1148 default:
1149 a_Dw_page_add_text(DW_PAGE (html->dw),
1150 g_strndup(space + i, 1),
1151 html->stack[html->stack_top].style);
1152 html->pre_column++;
1153 break;
1154 }
1155
1156 html->PrevWasCR = (space[i] == '\r');
1157 }
1158 html->SPCPending = FALSE;
1159
1160 } else {
1161 if (SGML_SPCDEL && html->PrevWasOpenTag) {
1162 /* SGML_SPCDEL ignores white space inmediately after an open tag */
1163 html->SPCPending = FALSE;
1164 } else {
1165 g_free(html->SPCBuf);
1166 html->SPCBuf = g_strndup(space, spacesize);
1167 html->SPCPending = TRUE;
1168 }
1169
1170 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY )
1171 html->StashSpace = (html->Stash->len > 0);
1172 }
1173 }
1174
1175 /*
1176 * Handles putting the word into its proper place
1177 * > STASH and VERBATIM --> html->Stash
1178 * > otherwise it goes through a_Dw_page_add_text()
1179 *
1180 * Entities are parsed (or not) according to parse_mode.
1181 */
1182 static void Html_process_word(DilloHtml *html, char *word, gint size)
1183 {
1184 gint i, start;
1185 gchar *Pword;
1186 DilloHtmlParseMode parse_mode = html->stack[html->stack_top].parse_mode;
1187
1188 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
1189 parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY ) {
1190 if ( html->StashSpace ) {
1191 g_string_append_c(html->Stash, ' ');
1192 html->StashSpace = FALSE;
1193 }
1194 Pword = Html_parse_entities(html, word, size);
1195 g_string_append(html->Stash, Pword);
1196 g_free(Pword);
1197
1198 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
1199 /* word goes in untouched, it is not processed here. */
1200 Pword = g_strndup(word, size);
1201 g_string_append(html->Stash, Pword);
1202 g_free(Pword);
1203 }
1204
1205 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
1206 parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
1207 /* skip until the closing instructions */
1208
1209 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_PRE ) {
1210 /* all this overhead is to catch white-space entities */
1211 Pword = Html_parse_entities(html, word, size);
1212 for (start = i = 0; Pword[i]; start = i)
1213 if (isspace(Pword[i])) {
1214 while (Pword[++i] && isspace(Pword[i]));
1215 Html_process_space(html, Pword + start, i - start);
1216 } else {
1217 while (Pword[++i] && !isspace(Pword[i]));
1218 a_Dw_page_add_text(DW_PAGE (html->dw),
1219 g_strndup(Pword + start, i - start),
1220 html->stack[html->stack_top].style);
1221 html->pre_column += i - start;
1222 html->PreFirstChar = FALSE;
1223 }
1224 g_free(Pword);
1225
1226 } else {
1227 /* add pending space if present */
1228 if (html->SPCPending && (!SGML_SPCDEL || !html->PrevWasOpenTag))
1229 /* SGML_SPCDEL ignores space after an open tag */
1230 a_Dw_page_add_space(DW_PAGE (html->dw),
1231 html->stack[html->stack_top].style);
1232
1233 /* actually white-space entities inside the word could be
1234 * collapsed (except &nbsp;), but that's too much overhead
1235 * for a very rare case of ill-formed HTML --Jcid */
1236
1237 Pword = Html_parse_entities(html, word, size);
1238 g_strdelimit(Pword, "\t\f\n\r", ' ');
1239 a_Dw_page_add_text(DW_PAGE (html->dw),
1240 Pword,
1241 html->stack[html->stack_top].style);
1242 }
1243
1244 html->PrevWasOpenTag = FALSE;
1245 html->SPCPending = FALSE;
1246 }
1247
1248 /*
1249 * Does the tag in tagstr (e.g. "p") match the tag in the tag, tagsize
1250 * structure, with the initial < skipped over (e.g. "P align=center>")
1251 */
1252 static gboolean Html_match_tag(const char *tagstr, char *tag, gint tagsize)
1253 {
1254 gint i;
1255
1256 for (i = 0; i < tagsize && tagstr[i] != '\0'; i++) {
1257 if (tolower(tagstr[i]) != tolower(tag[i]))
1258 return FALSE;
1259 }
1260 /* The test for '/' is for xml compatibility: "empty/>" will be matched. */
1261 if (i < tagsize && (isspace(tag[i]) || tag[i] == '>' || tag[i] == '/'))
1262 return TRUE;
1263 return FALSE;
1264 }
1265
1266 /*
1267 * This function is called after popping the stack, to
1268 * handle nested DwPage widgets.
1269 */
1270 static void Html_eventually_pop_dw(DilloHtml *html)
1271 {
1272 /* This function is called after popping from the stack, so the
1273 * relevant hand_over_break is at html->stack_top + 1. */
1274 if (html->dw != html->stack[html->stack_top].page) {
1275 if (html->stack[html->stack_top + 1].hand_over_break)
1276 a_Dw_page_hand_over_break(DW_PAGE(html->dw),
1277 html->stack[(html)->stack_top].style);
1278 a_Dw_page_flush(DW_PAGE(html->dw));
1279 html->dw = html->stack[html->stack_top].page;
1280 }
1281 }
1282
1283 /*
1284 * Push the tag (copying attributes from the top of the stack)
1285 */
1286 static void Html_push_tag(DilloHtml *html, gint tag_idx)
1287 {
1288 char *tagstr;
1289 gint n_items;
1290
1291 /* Save the element's name (no parameters) into tagstr. */
1292 tagstr = g_strdup(Tags[tag_idx].name);
1293
1294 n_items = html->stack_top + 1;
1295 a_List_add(html->stack, n_items, html->stack_max);
1296 /* We'll copy the former stack item and just change the tag and its index
1297 * instead of copying all fields except for tag. --Jcid */
1298 html->stack[n_items] = html->stack[n_items - 1];
1299 html->stack[n_items].tag_name = tagstr;
1300 html->stack[n_items].tag_idx = tag_idx;
1301 html->stack_top = n_items;
1302 /* proper memory management, may be unref'd later */
1303 a_Dw_style_ref (html->stack[html->stack_top].style);
1304 if (html->stack[html->stack_top].table_cell_style)
1305 a_Dw_style_ref (html->stack[html->stack_top].table_cell_style);
1306 html->dw = html->stack[html->stack_top].page;
1307 }
1308
1309 /*
1310 * Push the tag (used to force en element with optional open into the stack)
1311 * Note: now it's the same as Html_push_tag(), but things may change...
1312 */
1313 static void Html_force_push_tag(DilloHtml *html, gint tag_idx)
1314 {
1315 Html_push_tag(html, tag_idx);
1316 }
1317
1318 /*
1319 * Pop the top tag in the stack
1320 */
1321 static void Html_real_pop_tag(DilloHtml *html)
1322 {
1323 a_Dw_style_unref (html->stack[html->stack_top].style);
1324 if (html->stack[html->stack_top].table_cell_style)
1325 a_Dw_style_unref (html->stack[html->stack_top].table_cell_style);
1326 g_free(html->stack[html->stack_top--].tag_name);
1327 Html_eventually_pop_dw(html);
1328 }
1329
1330 /*
1331 * Default close function for tags.
1332 * (conditional cleanup of the stack)
1333 * There're several ways of doing it. Considering the HTML 4.01 spec
1334 * which defines optional close tags, and the will to deliver useful diagnose
1335 * messages for bad-formed HTML, it'll go as follows:
1336 * 1.- Search the stack for the first tag that requires a close tag.
1337 * 2.- If it matches, clean all the optional-close tags in between.
1338 * 3.- Cleanup the matching tag. (on error, give a warning message)
1339 *
1340 * If 'w3c_mode' is NOT enabled:
1341 * 1.- Search the stack for a matching tag based on tag level.
1342 * 2.- If it exists, clean all the tags in between.
1343 * 3.- Cleanup the matching tag. (on error, give a warning message)
1344 */
1345 static void Html_tag_cleanup_at_close(DilloHtml *html, gint TagIdx)
1346 {
1347 gint w3c_mode = !prefs.w3c_plus_heuristics;
1348 gint stack_idx, cmp = 1;
1349 gint new_idx = TagIdx;
1350
1351 if (html->CloseOneTag) {
1352 Html_real_pop_tag(html);
1353 html->CloseOneTag = FALSE;
1354 return;
1355 }
1356
1357 /* Look for the candidate tag to close */
1358 stack_idx = html->stack_top;
1359 while (stack_idx &&
1360 (cmp = (new_idx != html->stack[stack_idx].tag_idx)) &&
1361 ((w3c_mode &&
1362 Tags[html->stack[stack_idx].tag_idx].EndTag == 'O') ||
1363 (!w3c_mode &&
1364 Tags[html->stack[stack_idx].tag_idx].TagLevel <
1365 Tags[new_idx].TagLevel))) {
1366 --stack_idx;
1367 }
1368
1369 /* clean, up to the matching tag */
1370 if (cmp == 0 && stack_idx > 0) {
1371 /* There's a valid matching tag in the stack */
1372 while (html->stack_top >= stack_idx) {
1373 gint toptag_idx = html->stack[html->stack_top].tag_idx;
1374 /* Warn when we decide to close an open tag (for !w3c_mode) */
1375 if (html->stack_top > stack_idx &&
1376 Tags[toptag_idx].EndTag != 'O')
1377 MSG_HTML(" - forcing close of open tag: <%s>\n",
1378 Tags[toptag_idx].name);
1379
1380 /* Close this and only this tag */
1381 html->CloseOneTag = TRUE;
1382 Tags[toptag_idx].close (html, toptag_idx);
1383 }
1384
1385 } else {
1386 MSG_HTML("unexpected closing tag: </%s>. -- expected </%s>\n",
1387 Tags[new_idx].name, html->stack[stack_idx].tag_name);
1388 }
1389 }
1390
1391 /*
1392 * Cleanup (conditional), and Pop the tag (if it matches)
1393 */
1394 static void Html_pop_tag(DilloHtml *html, gint TagIdx)
1395 {
1396 Html_tag_cleanup_at_close(html, TagIdx);
1397 }
1398
1399 /*
1400 * Some parsing routines.
1401 */
1402
1403 /*
1404 * Used by Html_parse_length
1405 */
1406 static DwStyleLength Html_parse_length_or_multi_length (const gchar *attr,
1407 gchar **endptr)
1408 {
1409 DwStyleLength l;
1410 double v;
1411 gchar *end;
1412
1413 v = strtod (attr, &end);
1414 switch (*end) {
1415 case '%':
1416 end++;
1417 l = DW_STYLE_CREATE_PER_LENGTH (v / 100);
1418 break;
1419
1420 case '*':
1421 end++;
1422 l = DW_STYLE_CREATE_REL_LENGTH (v);
1423 break;
1424 /*
1425 The "px" suffix seems not allowed by HTML4.01 SPEC.
1426 case 'p':
1427 if (end[1] == 'x')
1428 end += 2;
1429 */
1430 default:
1431 l = DW_STYLE_CREATE_ABS_LENGTH ((gint)v);
1432 break;
1433 }
1434
1435 if (endptr)
1436 *endptr = end;
1437 return l;
1438 }
1439
1440
1441 /*
1442 * Returns a length or a percentage, or DW_STYLE_UNDEF_LENGTH in case
1443 * of an error, or if attr is NULL.
1444 */
1445 static DwStyleLength Html_parse_length (DilloHtml *html, const gchar *attr)
1446 {
1447 DwStyleLength l;
1448 gchar *end;
1449
1450 l = Html_parse_length_or_multi_length (attr, &end);
1451 if (DW_STYLE_IS_REL_LENGTH (l))
1452 /* not allowed as &Length; */
1453 return DW_STYLE_LENGTH_AUTO;
1454 else {
1455 /* allow only whitespaces */
1456 if (*end && !isspace (*end)) {
1457 MSG_HTML("Garbage after length: %s\n", attr);
1458 return DW_STYLE_LENGTH_AUTO;
1459 }
1460 }
1461
1462 return l;
1463 }
1464
1465 /*
1466 * Parse a color attribute.
1467 * Return value: parsed color, or default_color (+ error msg) on error.
1468 */
1469 static gint32
1470 Html_color_parse(DilloHtml *html, const char *subtag, gint32 default_color)
1471 {
1472 gint err = 1;
1473 gint32 color = a_Color_parse(subtag, default_color, &err);
1474
1475 if (err) {
1476 MSG_HTML("color is not in \"#RRGGBB\" format\n");
1477 }
1478 return color;
1479 }
1480
1481 /*
1482 * Check that 'val' is composed of characters inside [A-Za-z0-9:_.-]
1483 * Note: ID can't have entities, but this check is enough (no '&').
1484 * Return value: 1 if OK, 0 otherwise.
1485 */
1486 static gint
1487 Html_check_name_val(DilloHtml *html, const char *val, const char *attrname)
1488 {
1489 gint i;
1490
1491 for (i = 0; val[i]; ++i)
1492 if (!(isalnum(val[i]) || strchr(":_.-", val[i])))
1493 break;
1494
1495 if (val[i] || !isalpha(val[0]))
1496 MSG_HTML("'%s' value is not of the form "
1497 "[A-Za-z][A-Za-z0-9:_.-]*\n", attrname);
1498
1499 return !(val[i]);
1500 }
1501
1502 /*
1503 * Handle DOCTYPE declaration
1504 *
1505 * Follows the convention that HTML 4.01
1506 * doctypes which include a full w3c DTD url are treated as
1507 * standards-compliant, but 4.01 without the url and HTML 4.0 and
1508 * earlier are not. XHTML doctypes are always standards-compliant
1509 * whether or not an url is present.
1510 *
1511 * Note: I'm not sure about this convention. The W3C validator
1512 * recognizes the "HTML Level" with or without the URL. The convention
1513 * comes from mozilla (see URLs below), but Dillo doesn't have the same
1514 * rendering modes, so it may be better to chose another behaviour. --Jcid
1515 *
1516 * http://www.mozilla.org/docs/web-developer/quirks/doctypes.html
1517 * http://lists.auriga.wearlab.de/pipermail/dillo-dev/2004-October/002300.html
1518 *
1519 * This is not a full DOCTYPE parser, just enough for what Dillo uses.
1520 */
1521 static void Html_parse_doctype(DilloHtml *html, char *tag, gint tagsize)
1522 {
1523 char *HTML_sig = "<!DOCTYPE HTML PUBLIC ";
1524 char *HTML20 = "-//IETF//DTD HTML//EN";
1525 char *HTML32 = "-//W3C//DTD HTML 3.2";
1526 char *HTML40 = "-//W3C//DTD HTML 4.0";
1527 char *HTML401 = "-//W3C//DTD HTML 4.01";
1528 char *HTML401_url = "http://www.w3.org/TR/html4/";
1529 char *XHTML1 = "-//W3C//DTD XHTML 1.0";
1530 char *XHTML1_url = "http://www.w3.org/TR/xhtml1/DTD/";
1531 char *XHTML11 = "-//W3C//DTD XHTML 1.1";
1532 char *XHTML11_url = "http://www.w3.org/TR/xhtml11/DTD/";
1533
1534 int i, quote;
1535 char *p, *ntag = g_strndup(tag, tagsize);
1536
1537 /* Tag sanitization: Collapse whitespace between tokens
1538 * and replace '\n' and '\r' with ' ' inside quoted strings. */
1539 for (i = 0, p = ntag; *p; ++p) {
1540 if (isspace(*p)) {
1541 for (ntag[i++] = ' '; isspace(p[1]); ++p);
1542 } else if ((quote = *p) == '"' || *p == '\'') {
1543 for (ntag[i++] = *p++; (ntag[i++] = *p) && *p != quote; ++p) {
1544 if (*p == '\n' || *p == '\r')
1545 ntag[i - 1] = ' ';
1546 p += (p[0] == '\r' && p[1] == '\n') ? 1 : 0;
1547 }
1548 } else {
1549 ntag[i++] = *p;
1550 }
1551 if (!*p)
1552 break;
1553 }
1554 ntag[i] = 0;
1555
1556 _MSG("New: {%s}\n", ntag);
1557
1558 /* The default DT_NONE type is TagSoup */
1559 if (!g_strncasecmp(ntag, HTML_sig, strlen(HTML_sig))) {
1560 p = ntag + strlen(HTML_sig) + 1;
1561 if (!strncmp(p, HTML401, strlen(HTML401)) &&
1562 a_Misc_stristr(p + strlen(HTML401), HTML401_url)) {
1563 html->DocType = DT_HTML;
1564 html->DocTypeVersion = 4.01f;
1565 } else if (!strncmp(p, XHTML1, strlen(XHTML1)) &&
1566 a_Misc_stristr(p + strlen(XHTML1), XHTML1_url)) {
1567 html->DocType = DT_XHTML;
1568 html->DocTypeVersion = 1.0f;
1569 } else if (!strncmp(p, XHTML11, strlen(XHTML11)) &&
1570 a_Misc_stristr(p + strlen(XHTML11), XHTML11_url)) {
1571 html->DocType = DT_XHTML;
1572 html->DocTypeVersion = 1.1f;
1573 } else if (!strncmp(p, HTML40, strlen(HTML40))) {
1574 html->DocType = DT_HTML;
1575 html->DocTypeVersion = 4.0f;
1576 } else if (!strncmp(p, HTML32, strlen(HTML32))) {
1577 html->DocType = DT_HTML;
1578 html->DocTypeVersion = 3.2f;
1579 } else if (!strncmp(p, HTML20, strlen(HTML20))) {
1580 html->DocType = DT_HTML;
1581 html->DocTypeVersion = 2.0f;
1582 }
1583 }
1584
1585 g_free(ntag);
1586 }
1587
1588 /*
1589 * Handle open HTML element
1590 */
1591 static void Html_tag_open_html(DilloHtml *html, char *tag, gint tagsize)
1592 {
1593 if (!(html->InFlags & IN_HTML))
1594 html->InFlags |= IN_HTML;
1595 ++html->Num_HTML;
1596
1597 if (html->Num_HTML > 1) {
1598 MSG_HTML("HTML element was already open\n");
1599 }
1600 }
1601
1602 /*
1603 * Handle close HTML element
1604 */
1605 static void Html_tag_close_html(DilloHtml *html, gint TagIdx)
1606 {
1607 /* todo: may add some checks here */
1608 if (html->Num_HTML == 1) {
1609 /* beware of pages with multiple HTML close tags... :-P */
1610 html->InFlags &= ~IN_HTML;
1611 }
1612 Html_pop_tag(html, TagIdx);
1613 }
1614
1615 /*
1616 * Handle open HEAD element
1617 */
1618 static void Html_tag_open_head(DilloHtml *html, char *tag, gint tagsize)
1619 {
1620 if (html->InFlags & IN_BODY) {
1621 MSG_HTML("HEAD element must go before the BODY section\n");
1622 html->ReqTagClose = TRUE;
1623 return;
1624 }
1625
1626 if (!(html->InFlags & IN_HEAD))
1627 html->InFlags |= IN_HEAD;
1628 ++html->Num_HEAD;
1629
1630 if (html->Num_HEAD > 1) {
1631 MSG_HTML("HEAD element was already open\n");
1632 }
1633 }
1634
1635 /*
1636 * Handle close HEAD element
1637 * Note: as a side effect of Html_test_section() this function is called
1638 * twice when the head element is closed implicitly.
1639 */
1640 static void Html_tag_close_head(DilloHtml *html, gint TagIdx)
1641 {
1642 if (html->InFlags & IN_HEAD) {
1643 if (html->Num_TITLE == 0)
1644 MSG_HTML("HEAD section lacks the TITLE element\n");
1645
1646 html->InFlags &= ~IN_HEAD;
1647 }
1648 Html_pop_tag(html, TagIdx);
1649 }
1650
1651 /*
1652 * Handle open TITLE
1653 * calls stash init, where the title string will be stored
1654 */
1655 static void Html_tag_open_title(DilloHtml *html, char *tag, gint tagsize)
1656 {
1657 ++html->Num_TITLE;
1658 Html_stash_init(html);
1659 }
1660
1661 /*
1662 * Handle close TITLE
1663 * set page-title in the browser window and in the history.
1664 */
1665 static void Html_tag_close_title(DilloHtml *html, gint TagIdx)
1666 {
1667 if (html->InFlags & IN_HEAD) {
1668 /* title is only valid inside HEAD */
1669 a_Interface_set_page_title(html->linkblock->bw, html->Stash->str);
1670 a_History_set_title(NAV_TOP(html->linkblock->bw), html->Stash->str);
1671 } else {
1672 MSG_HTML("the TITLE element must be inside the HEAD section\n");
1673 }
1674 Html_pop_tag(html, TagIdx);
1675 }
1676
1677 /*
1678 * Handle open SCRIPT
1679 * initializes stash, where the embedded code will be stored.
1680 * MODE_VERBATIM is used because MODE_STASH catches entities.
1681 */
1682 static void Html_tag_open_script(DilloHtml *html, char *tag, gint tagsize)
1683 {
1684 Html_stash_init(html);
1685 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
1686 }
1687
1688 /*
1689 * Handle close SCRIPT
1690 */
1691 static void Html_tag_close_script(DilloHtml *html, gint TagIdx)
1692 {
1693 /* eventually the stash will be sent to an interpreter for parsing */
1694 Html_pop_tag(html, TagIdx);
1695 }
1696
1697 /*
1698 * Handle open STYLE
1699 * store the contents to the stash where (in the future) the style
1700 * sheet interpreter can get it.
1701 */
1702 static void Html_tag_open_style(DilloHtml *html, char *tag, gint tagsize)
1703 {
1704 Html_stash_init(html);
1705 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
1706 }
1707
1708 /*
1709 * Handle close STYLE
1710 */
1711 static void Html_tag_close_style(DilloHtml *html, gint TagIdx)
1712 {
1713 /* eventually the stash will be sent to an interpreter for parsing */
1714 Html_pop_tag(html, TagIdx);
1715 }
1716
1717 /*
1718 * <BODY>
1719 */
1720 static void Html_tag_open_body(DilloHtml *html, char *tag, gint tagsize)
1721 {
1722 const char *attrbuf;
1723 DwPage *page;
1724 DwStyle style_attrs, *style;
1725 gint32 color;
1726
1727 if (!(html->InFlags & IN_BODY))
1728 html->InFlags |= IN_BODY;
1729 ++html->Num_BODY;
1730
1731 if (html->Num_BODY > 1) {
1732 MSG_HTML("BODY element was already open\n");
1733 return;
1734 }
1735 if (html->InFlags & IN_HEAD) {
1736 /* if we're here, it's bad XHTML, no need to recover */
1737 MSG_HTML("unclosed HEAD element\n");
1738 }
1739
1740 page = DW_PAGE (html->dw);
1741
1742 if (!prefs.force_my_colors) {
1743 if ((attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1744 color = Html_color_parse(html, attrbuf, prefs.bg_color);
1745 if ( (color == 0xffffff && !prefs.allow_white_bg) ||
1746 prefs.force_my_colors )
1747 color = prefs.bg_color;
1748
1749 style_attrs = *html->dw->style;
1750 style_attrs.background_color =
1751 a_Dw_style_color_new (color, html->bw->main_window->window);
1752 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1753 a_Dw_widget_set_style (html->dw, style);
1754 a_Dw_style_unref (style);
1755 html->stack[html->stack_top].current_bg_color = color;
1756 }
1757
1758 if ((attrbuf = Html_get_attr(html, tag, tagsize, "text"))) {
1759 color = Html_color_parse(html, attrbuf, prefs.text_color);
1760 HTML_SET_TOP_ATTR
1761 (html, color,
1762 a_Dw_style_color_new (color, html->bw->main_window->window));
1763 }
1764
1765 if ((attrbuf = Html_get_attr(html, tag, tagsize, "link")))
1766 html->linkblock->link_color = Html_color_parse(html, attrbuf,
1767 prefs.link_color);
1768
1769 if ((attrbuf = Html_get_attr(html, tag, tagsize, "vlink")))
1770 html->linkblock->visited_color =
1771 Html_color_parse(html, attrbuf, prefs.visited_color);
1772
1773 if (prefs.contrast_visited_color) {
1774 /* get a color that has a "safe distance" from text, link and bg */
1775 html->linkblock->visited_color =
1776 a_Color_vc(html->linkblock->visited_color,
1777 html->stack[html->stack_top].style->color->color_val,
1778 html->linkblock->link_color,
1779 html->stack[html->stack_top].current_bg_color);
1780 }
1781 }
1782
1783 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_BODY;
1784 }
1785
1786 /*
1787 * BODY
1788 */
1789 static void Html_tag_close_body(DilloHtml *html, gint TagIdx)
1790 {
1791 if (html->Num_BODY == 1) {
1792 /* some tag soup pages use multiple BODY tags... */
1793 html->InFlags &= ~IN_BODY;
1794 }
1795 Html_pop_tag(html, TagIdx);
1796 }
1797
1798 /*
1799 * <P>
1800 * todo: what's the point between adding the parbreak before and
1801 * after the push?
1802 */
1803 static void Html_tag_open_p(DilloHtml *html, char *tag, gint tagsize)
1804 {
1805 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
1806 html->stack[(html)->stack_top].style);
1807 Html_tag_set_align_attr (html, tag, tagsize);
1808 }
1809
1810 /*
1811 * <TABLE>
1812 */
1813 static void Html_tag_open_table(DilloHtml *html, char *tag, gint tagsize)
1814 {
1815 #ifdef USE_TABLES
1816 DwWidget *table;
1817 DwStyle style_attrs, *tstyle, *old_style;
1818 const char *attrbuf;
1819 gint32 border = 0, cellspacing = 1, cellpadding = 2, bgcolor;
1820 #endif
1821
1822 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
1823 html->stack[(html)->stack_top].style);
1824
1825 #ifdef USE_TABLES
1826 if ((attrbuf = Html_get_attr(html, tag, tagsize, "border")))
1827 border = *attrbuf ? Html_check_int(strtol(attrbuf,NULL,10), 0,100,1) : 1;
1828 if ((attrbuf = Html_get_attr(html, tag, tagsize, "cellspacing")))
1829 cellspacing = Html_check_int(strtol(attrbuf, NULL, 10), 0, 100, 1);
1830 if ((attrbuf = Html_get_attr(html, tag, tagsize, "cellpadding")))
1831 cellpadding = Html_check_int(strtol(attrbuf, NULL, 10), 0, 100, 2);
1832
1833 /* The style for the table */
1834 style_attrs = *html->stack[html->stack_top].style;
1835
1836 /* When dillo was started with the --debug-rendering option, there
1837 * is always a border around the table. */
1838 if (dillo_dbg_rendering)
1839 a_Dw_style_box_set_val (&style_attrs.border_width, MIN (border, 1));
1840 else
1841 a_Dw_style_box_set_val (&style_attrs.border_width, border);
1842
1843 a_Dw_style_box_set_border_color
1844 (&style_attrs,
1845 a_Dw_style_shaded_color_new (
1846 html->stack[html->stack_top].current_bg_color,
1847 html->bw->main_window->window));
1848 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_OUTSET);
1849 style_attrs.border_spacing = cellspacing;
1850
1851 if ((attrbuf = Html_get_attr(html, tag, tagsize, "width"))) {
1852 int dw_len = Html_parse_length (html, attrbuf);
1853 int len = strtol(attrbuf, NULL, 10);
1854 if ((DW_STYLE_IS_PER_LENGTH(dw_len) &&
1855 Html_check_int(len, 0, 100, -1) != -1) ||
1856 (DW_STYLE_IS_ABS_LENGTH(dw_len) &&
1857 Html_check_int(len, 0, 5000, -1) != -1) ||
1858 (DW_STYLE_IS_REL_LENGTH(dw_len) &&
1859 Html_check_int(len, 0, 100, -1) != -1)) {
1860 style_attrs.width = dw_len;
1861 }
1862 }
1863
1864 if ((attrbuf = Html_get_attr(html, tag, tagsize, "align"))) {
1865 if (g_strcasecmp (attrbuf, "left") == 0)
1866 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_LEFT;
1867 else if (g_strcasecmp (attrbuf, "right") == 0)
1868 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_RIGHT;
1869 else if (g_strcasecmp (attrbuf, "center") == 0)
1870 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_CENTER;
1871 }
1872
1873 if (!prefs.force_my_colors &&
1874 (attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1875 bgcolor = Html_color_parse(html, attrbuf, -1);
1876 if (bgcolor != -1) {
1877 if (bgcolor == 0xffffff && !prefs.allow_white_bg)
1878 bgcolor = prefs.bg_color;
1879 html->stack[html->stack_top].current_bg_color = bgcolor;
1880 style_attrs.background_color =
1881 a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
1882 }
1883 }
1884
1885 tstyle = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1886
1887 /* The style for the cells */
1888 style_attrs = *html->stack[html->stack_top].style;
1889 /* When dillo was started with the --debug-rendering option, there
1890 * is always a border around the cells. */
1891 if (dillo_dbg_rendering)
1892 a_Dw_style_box_set_val (&style_attrs.border_width, 1);
1893 else
1894 a_Dw_style_box_set_val (&style_attrs.border_width, border ? 1 : 0);
1895
1896 a_Dw_style_box_set_val (&style_attrs.padding, cellpadding);
1897 a_Dw_style_box_set_border_color (&style_attrs, tstyle->border_color.top);
1898 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_INSET);
1899
1900 old_style = html->stack[html->stack_top].table_cell_style;
1901 html->stack[html->stack_top].table_cell_style =
1902 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1903 if (old_style)
1904 a_Dw_style_unref (old_style);
1905
1906 table = a_Dw_table_new ();
1907 a_Dw_page_add_widget (DW_PAGE (html->dw), table, tstyle);
1908 a_Dw_style_unref (tstyle);
1909
1910 html->stack[html->stack_top].table_mode = DILLO_HTML_TABLE_MODE_TOP;
1911 html->stack[html->stack_top].cell_text_align_set = FALSE;
1912 html->stack[html->stack_top].table = table;
1913 #endif
1914 }
1915
1916
1917 /*
1918 * used by <TD> and <TH>
1919 */
1920 static void Html_tag_open_table_cell(DilloHtml *html, char *tag, gint tagsize,
1921 DwStyleTextAlignType text_align)
1922 {
1923 #ifdef USE_TABLES
1924 DwWidget *col_page;
1925 gint colspan = 1, rowspan = 1;
1926 const char *attrbuf;
1927 DwStyle style_attrs, *style, *old_style;
1928 gint32 bgcolor;
1929 gboolean new_style;
1930
1931 switch (html->stack[html->stack_top].table_mode) {
1932 case DILLO_HTML_TABLE_MODE_NONE:
1933 MSG_HTML("<td> or <th> outside <table>\n");
1934 return;
1935
1936 case DILLO_HTML_TABLE_MODE_TOP:
1937 MSG_HTML("<td> or <th> outside <tr>\n");
1938 /* a_Dw_table_add_cell takes care that dillo does not crash. */
1939 /* continues */
1940 case DILLO_HTML_TABLE_MODE_TR:
1941 case DILLO_HTML_TABLE_MODE_TD:
1942 /* todo: check errors? */
1943 if ((attrbuf = Html_get_attr(html, tag, tagsize, "colspan")))
1944 colspan = Html_check_int(strtol(attrbuf, NULL, 10), 0, 1000, 1);
1945 if ((attrbuf = Html_get_attr(html, tag, tagsize, "rowspan")))
1946 rowspan = Html_check_int(strtol(attrbuf, NULL, 10), 0, 1000, 1);
1947
1948 /* text style */
1949 old_style = html->stack[html->stack_top].style;
1950 style_attrs = *old_style;
1951 if (!html->stack[html->stack_top].cell_text_align_set)
1952 style_attrs.text_align = text_align;
1953 if (Html_get_attr(html, tag, tagsize, "nowrap"))
1954 style_attrs.white_space = DW_STYLE_WHITE_SPACE_NOWRAP;
1955 else
1956 style_attrs.white_space = DW_STYLE_WHITE_SPACE_NORMAL;
1957
1958 html->stack[html->stack_top].style =
1959 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1960 a_Dw_style_unref (old_style);
1961 Html_tag_set_align_attr (html, tag, tagsize);
1962
1963 /* cell style */
1964 style_attrs = *html->stack[html->stack_top].table_cell_style;
1965 new_style = FALSE;
1966
1967 if ((attrbuf = Html_get_attr(html, tag, tagsize, "width"))) {
1968 style_attrs.width = Html_parse_length (html, attrbuf);
1969 new_style = TRUE;
1970 }
1971
1972 if (Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs))
1973 new_style = TRUE;
1974
1975 if (!prefs.force_my_colors &&
1976 (attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1977 bgcolor = Html_color_parse(html, attrbuf, -1);
1978 if (bgcolor != -1) {
1979 if (bgcolor == 0xffffff && !prefs.allow_white_bg)
1980 bgcolor = prefs.bg_color;
1981
1982 new_style = TRUE;
1983 style_attrs.background_color =
1984 a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
1985 html->stack[html->stack_top].current_bg_color = bgcolor;
1986 }
1987 }
1988
1989 if (html->stack[html->stack_top].style->text_align
1990 == DW_STYLE_TEXT_ALIGN_STRING)
1991 col_page = a_Dw_table_cell_new
1992 (a_Dw_table_get_cell_ref
1993 (DW_TABLE (html->stack[html->stack_top].table)));
1994 else
1995 col_page = a_Dw_page_new ();
1996
1997 if (new_style) {
1998 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1999 a_Dw_widget_set_style (col_page, style);
2000 a_Dw_style_unref (style);
2001 } else
2002 a_Dw_widget_set_style (col_page,
2003 html->stack[html->stack_top].table_cell_style);
2004
2005 a_Dw_table_add_cell (DW_TABLE (html->stack[html->stack_top].table),
2006 col_page, colspan, rowspan);
2007 html->stack[html->stack_top].page = html->dw = col_page;
2008
2009 /* Handle it when the user clicks on a link */
2010 Html_connect_signals(html, GTK_OBJECT(col_page));
2011 break;
2012
2013 default:
2014 /* compiler happiness */
2015 break;
2016 }
2017
2018 html->stack[html->stack_top].table_mode = DILLO_HTML_TABLE_MODE_TD;
2019 #endif
2020 }
2021
2022
2023 /*
2024 * <TD>
2025 */
2026 static void Html_tag_open_td(DilloHtml *html, char *tag, gint tagsize)
2027 {
2028 Html_tag_open_table_cell (html, tag, tagsize, DW_STYLE_TEXT_ALIGN_LEFT);
2029 }
2030
2031
2032 /*
2033 * <TH>
2034 */
2035 static void Html_tag_open_th(DilloHtml *html, char *tag, gint tagsize)
2036 {
2037 Html_set_top_font(html, NULL, 0, 1, 1);
2038 Html_tag_open_table_cell (html, tag, tagsize, DW_STYLE_TEXT_ALIGN_CENTER);
2039 }
2040
2041
2042 /*
2043 * <TR>
2044 */
2045 static void Html_tag_open_tr(DilloHtml *html, char *tag, gint tagsize)
2046 {
2047 const char *attrbuf;
2048 DwStyle style_attrs, *style, *old_style;
2049 gint32 bgcolor;
2050
2051 #ifdef USE_TABLES
2052 switch (html->stack[html->stack_top].table_mode) {
2053 case DILLO_HTML_TABLE_MODE_NONE:
2054 _MSG("Invalid HTML syntax: <tr> outside <table>\n");
2055 return;
2056
2057 case DILLO_HTML_TABLE_MODE_TOP:
2058 case DILLO_HTML_TABLE_MODE_TR:
2059 case DILLO_HTML_TABLE_MODE_TD:
2060 style = NULL;
2061
2062 if (!prefs.force_my_colors &&
2063 (attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
2064 bgcolor = Html_color_parse(html, attrbuf, -1);
2065 if (bgcolor != -1) {
2066 if (bgcolor == 0xffffff && !prefs.allow_white_bg)
2067 bgcolor = prefs.bg_color;
2068
2069 style_attrs = *html->stack[html->stack_top].style;
2070 style_attrs.background_color =
2071 a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
2072 style =
2073 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2074 html->stack[html->stack_top].current_bg_color = bgcolor;
2075 }
2076 }
2077
2078 a_Dw_table_add_row (DW_TABLE (html->stack[html->stack_top].table),
2079 style);
2080 if (style)
2081 a_Dw_style_unref (style);
2082
2083 if (Html_get_attr (html, tag, tagsize, "align")) {
2084 html->stack[html->stack_top].cell_text_align_set = TRUE;
2085 Html_tag_set_align_attr (html, tag, tagsize);
2086 }
2087
2088 style_attrs = *html->stack[html->stack_top].table_cell_style;
2089 if (Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs)) {
2090 old_style = html->stack[html->stack_top].table_cell_style;
2091 html->stack[html->stack_top].table_cell_style =
2092 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2093 a_Dw_style_unref (old_style);
2094 } else
2095
2096 break;
2097
2098 default:
2099 break;
2100 }
2101
2102 html->stack[html->stack_top].table_mode = DILLO_HTML_TABLE_MODE_TR;
2103 #else
2104 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
2105 html->stack[(html)->stack_top].style);
2106 #endif
2107 }
2108
2109 /*
2110 * <FRAME>, <IFRAME>
2111 * todo: This is just a temporary fix while real frame support
2112 * isn't finished. Imitates lynx/w3m's frames.
2113 */
2114 static void Html_tag_open_frame (DilloHtml *html, gchar *tag, gint tagsize)
2115 {
2116 const char *attrbuf;
2117 gchar *src, *buf;
2118 DilloUrl *url;
2119 DwPage *page;
2120 DwStyle style_attrs, *link_style;
2121 DwWidget *bullet;
2122 gint buf_size;
2123
2124 page = DW_PAGE(html->dw);
2125
2126 if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "src")) )
2127 return;
2128
2129 if (!(url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0)))
2130 return;
2131
2132 src = g_strdup(attrbuf);
2133
2134 style_attrs = *(html->stack[html->stack_top].style);
2135
2136 if (a_Capi_get_buf(url, &buf, &buf_size)) /* visited frame */
2137 style_attrs.color = a_Dw_style_color_new
2138 (html->linkblock->visited_color, html->bw->main_window->window);
2139 else /* unvisited frame */
2140 style_attrs.color = a_Dw_style_color_new
2141 (html->linkblock->link_color, html->bw->main_window->window);
2142
2143 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
2144 style_attrs.x_link = Html_set_new_link(html, &url);
2145 link_style = a_Dw_style_new (&style_attrs,
2146 html->bw->main_window->window);
2147
2148 a_Dw_page_add_parbreak(page, 5, html->stack[(html)->stack_top].style);
2149
2150 /* The bullet will be assigned the current list style, which should
2151 * be "disc" by default, but may in very weird pages be different.
2152 * Anyway, there should be no harm. */
2153 bullet = a_Dw_bullet_new();
2154 a_Dw_page_add_widget(page, bullet, html->stack[html->stack_top].style);
2155 a_Dw_page_add_space(page, html->stack[html->stack_top].style);
2156
2157 if (tolower(tag[1]) == 'i') {
2158 /* IFRAME usually comes with very long advertising/spying URLS,
2159 * to not break rendering we will force name="IFRAME" */
2160 a_Dw_page_add_text(page, g_strdup("IFRAME"), link_style);
2161
2162 } else {
2163 /* FRAME:
2164 * If 'name' tag is present use it, if not use 'src' value */
2165 if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "name")) ) {
2166 a_Dw_page_add_text(page, g_strdup(src), link_style);
2167 } else {
2168 a_Dw_page_add_text(page, g_strdup(attrbuf), link_style);
2169 }
2170 }
2171
2172 a_Dw_page_add_parbreak(page, 5, html->stack[(html)->stack_top].style);
2173
2174 a_Dw_style_unref(link_style);
2175 g_free(src);
2176 }
2177
2178 /*
2179 * <FRAMESET>
2180 * todo: This is just a temporary fix while real frame support
2181 * isn't finished. Imitates lynx/w3m's frames.
2182 */
2183 static void Html_tag_open_frameset (DilloHtml *html, gchar *tag, gint tagsize)
2184 {
2185 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2186 html->stack[(html)->stack_top].style);
2187 a_Dw_page_add_text(DW_PAGE(html->dw), g_strdup("--FRAME--"),
2188 html->stack[html->stack_top].style);
2189 Html_add_indented(html, 40, 0, 5);
2190 }
2191
2192 /*
2193 * <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
2194 */
2195 static void Html_tag_open_h(DilloHtml *html, char *tag, gint tagsize)
2196 {
2197 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2198 html->stack[(html)->stack_top].style);
2199
2200 /* todo: combining these two would be slightly faster */
2201 Html_set_top_font(html, prefs.vw_fontname,
2202 Html_level_to_fontsize(FontSizesNum - (tag[2] - '0')),
2203 1, 3);
2204 Html_tag_set_align_attr (html, tag, tagsize);
2205
2206 /* First finalize unclosed H tags (we test if already named anyway) */
2207 a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
2208 a_Menu_pagemarks_add(html->bw, DW_PAGE (html->dw),
2209 html->stack[html->stack_top].style, (tag[2] - '0'));
2210 Html_stash_init(html);
2211 html->stack[html->stack_top].parse_mode =
2212 DILLO_HTML_PARSE_MODE_STASH_AND_BODY;
2213 }
2214
2215 /*
2216 * Handle close: <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
2217 */
2218 static void Html_tag_close_h(DilloHtml *html, gint TagIdx)
2219 {
2220 a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
2221 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2222 html->stack[(html)->stack_top].style);
2223 Html_pop_tag(html, TagIdx);
2224 }
2225
2226 /*
2227 * <BIG> | <SMALL>
2228 */
2229 static void Html_tag_open_big_small(DilloHtml *html, char *tag, gint tagsize)
2230 {
2231 gint level;
2232
2233 level =
2234 Html_fontsize_to_level(html->stack[html->stack_top].style->font->size) +
2235 ((g_strncasecmp(tag+1, "big", 3)) ? -1 : 1);
2236 Html_set_top_font(html, NULL, Html_level_to_fontsize(level), 0, 0);
2237 }
2238
2239 /*
2240 * <BR>
2241 */
2242 static void Html_tag_open_br(DilloHtml *html, char *tag, gint tagsize)
2243 {
2244 a_Dw_page_add_linebreak(DW_PAGE (html->dw),
2245 html->stack[(html)->stack_top].style);
2246 }
2247
2248 /*
2249 * <BUTTON>
2250 */
2251 static void Html_tag_open_button(DilloHtml *html, char *tag, gint tagsize)
2252 {
2253 /*
2254 * Buttons are rendered on one line, this is (at several levels) a
2255 * bit simpler. May be changed in the future.
2256 */
2257 DwStyle style_attrs, *style;
2258 DwWidget *button, *page;
2259 DilloHtmlForm *form;
2260 DilloHtmlLB *html_lb;
2261 DilloHtmlInputType inp_type;
2262 gchar *name, *value, *type;
2263
2264 /* Render the button */
2265 style_attrs = *html->stack[html->stack_top].style;
2266
2267 a_Dw_style_box_set_val(&style_attrs.margin, 0);
2268 a_Dw_style_box_set_val(&style_attrs.border_width, 0);
2269 a_Dw_style_box_set_val(&style_attrs.padding, 0);
2270 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2271 button = a_Dw_button_new (DW_USES_HINTS, TRUE);
2272
2273 /* The new button is not set button-insensitive, since nested buttons
2274 * (if they are anyway allowed, todo: search in spec) should all be
2275 * activatable. */
2276 a_Dw_widget_set_button_sensitive (button, TRUE);
2277
2278 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5, style);
2279 a_Dw_page_add_widget (DW_PAGE (html->dw), button, style);
2280 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5, style);
2281 a_Dw_style_unref (style);
2282
2283 a_Dw_style_box_set_val(&style_attrs.margin, 5);
2284 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2285 page = a_Dw_page_new ();
2286 a_Dw_widget_set_style (page, style);
2287 a_Dw_style_unref (style);
2288 a_Dw_container_add (DW_CONTAINER (button), page);
2289 a_Dw_widget_set_button_sensitive (DW_WIDGET (page), FALSE);
2290 a_Dw_style_box_set_val(&style_attrs.margin, 0);
2291
2292 a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
2293
2294 html->stack[html->stack_top].page = html->dw = page;
2295
2296 /* Handle it when the user clicks on a link */
2297 Html_connect_signals(html, GTK_OBJECT(page));
2298
2299 /* Connect it to the form */
2300 html_lb = html->linkblock;
2301 form = &(html_lb->forms[html_lb->num_forms - 1]);
2302
2303 type = Html_get_attr_wdef(html, tag, tagsize, "type", "");
2304
2305 if (strcmp(type, "submit") == 0) {
2306 inp_type = DILLO_HTML_INPUT_BUTTON_SUBMIT;
2307 gtk_signal_connect(GTK_OBJECT(button), "clicked",
2308 GTK_SIGNAL_FUNC(Html_submit_form), html_lb);
2309 } else if (strcmp(type, "reset") == 0) {
2310 inp_type = DILLO_HTML_INPUT_BUTTON_RESET;
2311 gtk_signal_connect(GTK_OBJECT(button), "clicked",
2312 GTK_SIGNAL_FUNC(Html_reset_form), html_lb);
2313 } else
2314 return;
2315
2316 value = Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
2317 name = Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
2318
2319 Html_add_input(form, inp_type, (GtkWidget*)button, name, value,
2320 NULL, FALSE);
2321
2322 g_free(type);
2323 g_free(name);
2324 g_free(value);
2325 }
2326
2327
2328 /*
2329 * <FONT>
2330 */
2331 static void Html_tag_open_font(DilloHtml *html, char *tag, gint tagsize)
2332 {
2333 #if 1
2334 DwStyle style_attrs, *old_style;
2335 /*DwStyleFont font;*/
2336 const char *attrbuf;
2337 gint32 color;
2338
2339 if (!prefs.force_my_colors) {
2340 old_style = html->stack[html->stack_top].style;
2341 style_attrs = *old_style;
2342
2343 if ((attrbuf = Html_get_attr(html, tag, tagsize, "color"))) {
2344 if (prefs.contrast_visited_color && html->InVisitedLink) {
2345 color = html->linkblock->visited_color;
2346 } else {
2347 /* use the tag-specified color */
2348 color = Html_color_parse(
2349 html, attrbuf, style_attrs.color->color_val);
2350 style_attrs.color = a_Dw_style_color_new
2351 (color, html->bw->main_window->window);
2352 }
2353 }
2354
2355 #if 0
2356 if ((attrbuf = Html_get_attr(html, tag, tagsize, "face"))) {
2357 font = *( style_attrs.font );
2358 font.name = attrbuf;
2359 style_attrs.font = a_Dw_style_font_new_from_list (&font);
2360 }
2361 #endif
2362
2363 html->stack[html->stack_top].style =
2364 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2365 a_Dw_style_unref (old_style);
2366 }
2367
2368 #endif
2369 }
2370
2371 /*
2372 * <ABBR>
2373 */
2374 static void Html_tag_open_abbr(DilloHtml *html, char *tag, gint tagsize)
2375 {
2376 DwTooltip *tooltip;
2377 const char *attrbuf;
2378
2379 if ((attrbuf = Html_get_attr(html, tag, tagsize, "title"))) {
2380 tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
2381 HTML_SET_TOP_ATTR(html, x_tooltip, tooltip);
2382 }
2383 }
2384
2385 /*
2386 * <B>
2387 */
2388 static void Html_tag_open_b(DilloHtml *html, char *tag, gint tagsize)
2389 {
2390 Html_set_top_font(html, NULL, 0, 1, 1);
2391 }
2392
2393 /*
2394 * <STRONG>
2395 */
2396 static void Html_tag_open_strong(DilloHtml *html, char *tag, gint tagsize)
2397 {
2398 Html_set_top_font(html, NULL, 0, 1, 1);
2399 }
2400
2401 /*
2402 * <I>
2403 */
2404 static void Html_tag_open_i(DilloHtml *html, char *tag, gint tagsize)
2405 {
2406 Html_set_top_font(html, NULL, 0, 2, 2);
2407 }
2408
2409 /*
2410 * <EM>
2411 */
2412 static void Html_tag_open_em(DilloHtml *html, char *tag, gint tagsize)
2413 {
2414 Html_set_top_font(html, NULL, 0, 2, 2);
2415 }
2416
2417 /*
2418 * <CITE>
2419 */
2420 static void Html_tag_open_cite(DilloHtml *html, char *tag, gint tagsize)
2421 {
2422 Html_set_top_font(html, NULL, 0, 2, 2);
2423 }
2424
2425 /*
2426 * <CENTER>
2427 */
2428 static void Html_tag_open_center(DilloHtml *html, char *tag, gint tagsize)
2429 {
2430 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
2431 html->stack[(html)->stack_top].style);
2432 HTML_SET_TOP_ATTR(html, text_align, DW_STYLE_TEXT_ALIGN_CENTER);
2433 }
2434
2435 /*
2436 * <ADDRESS>
2437 */
2438 static void Html_tag_open_address(DilloHtml *html, char *tag, gint tagsize)
2439 {
2440 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2441 html->stack[(html)->stack_top].style);
2442 Html_set_top_font(html, NULL, 0, 2, 2);
2443 }
2444
2445 /*
2446 * <TT>
2447 */
2448 static void Html_tag_open_tt(DilloHtml *html, char *tag, gint tagsize)
2449 {
2450 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
2451 }
2452
2453 /*
2454 * Read image-associated tag attributes,
2455 * create new image and add it to the html page (if add is TRUE).
2456 */
2457 static DilloImage *Html_add_new_image(DilloHtml *html, char *tag,
2458 gint tagsize, DwStyle *style_attrs,
2459 gboolean add)
2460 {
2461 const int MAX_W = 6000, MAX_H = 6000;
2462
2463 DilloImage *Image;
2464 char *width_ptr, *height_ptr, *alt_ptr;
2465 const char *attrbuf;
2466 DwStyleLength s_w, s_h;
2467 int space, w = 0, h = 0;
2468
2469 if (prefs.show_tooltip &&
2470 (attrbuf = Html_get_attr(html, tag, tagsize, "title")))
2471 style_attrs->x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
2472
2473 alt_ptr = Html_get_attr_wdef(html, tag, tagsize, "alt", NULL);
2474 width_ptr = Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
2475 height_ptr = Html_get_attr_wdef(html, tag, tagsize, "height", NULL);
2476 // Check for malicious values
2477 // TODO: the same for percentage and relative lengths.
2478 if (width_ptr) {
2479 s_w = Html_parse_length (html, width_ptr);
2480 w = (DW_STYLE_IS_ABS_LENGTH(s_w)) ? DW_STYLE_ABS_LENGTH_VAL(s_w) : 0;
2481 }
2482 if (height_ptr) {
2483 s_h = Html_parse_length (html, height_ptr);
2484 h = (DW_STYLE_IS_ABS_LENGTH(s_h)) ? DW_STYLE_ABS_LENGTH_VAL(s_h) : 0;
2485 }
2486 if (w < 0 || h < 0 || abs(w*h) > MAX_W * MAX_H) {
2487 g_free(width_ptr);
2488 g_free(height_ptr);
2489 width_ptr = height_ptr = NULL;
2490 MSG("Html_add_new_image: suspicious image size request %dx%d\n", w, h);
2491 }
2492
2493 /* todo: we should scale the image respecting its ratio.
2494 * As the image size is not known at this time, maybe a flag
2495 * can be set to scale it later.
2496 if ((width_ptr && !height_ptr) || (height_ptr && !width_ptr))
2497 [...]
2498 */
2499
2500 /* Spacing to the left and right */
2501 if ((attrbuf = Html_get_attr(html, tag, tagsize, "hspace"))) {
2502 space = strtol(attrbuf, NULL, 10);
2503
2504 if (space > 0)
2505 style_attrs->margin.left = style_attrs->margin.right = space;
2506 }
2507
2508 /* Spacing at the top and bottom */
2509 if ((attrbuf = Html_get_attr(html, tag, tagsize, "vspace"))) {
2510 space = strtol(attrbuf, NULL, 10);
2511
2512 if (space > 0)
2513 style_attrs->margin.top = style_attrs->margin.bottom = space;
2514 }
2515
2516 /* Add a new image widget to this page */
2517 if ((Image = a_Image_new(0, 0, alt_ptr,
2518 html->stack[html->stack_top].current_bg_color)))
2519 if (add)
2520 Html_add_widget(html, DW_WIDGET(Image->dw), width_ptr, height_ptr,
2521 style_attrs);
2522
2523 g_free(width_ptr);
2524 g_free(height_ptr);
2525 g_free(alt_ptr);
2526 return Image;
2527 }
2528
2529 /*
2530 * Tell cache to retrieve image
2531 */
2532 static void Html_load_image(DilloHtml *html, DilloUrl *url, DilloImage *Image)
2533 {
2534 DilloWeb *Web;
2535 gint ClientKey;
2536 /* Fill a Web structure for the cache query */
2537 Web = a_Web_new(url);
2538 Web->bw = html->bw;
2539 Web->Image = Image;
2540 Web->flags |= WEB_Image;
2541 /* Request image data from the cache */
2542 if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
2543 a_Interface_add_client(html->bw, ClientKey, 0);
2544 a_Interface_add_url(html->bw, url, WEB_Image);
2545 }
2546 }
2547
2548 /*
2549 * Create a new Image struct and request the image-url to the cache
2550 * (If it either hits or misses, is not relevant here; that's up to the
2551 * cache functions)
2552 */
2553 static void Html_tag_open_img(DilloHtml *html, char *tag, gint tagsize)
2554 {
2555 DilloImage *Image;
2556 DilloUrl *url, *usemap_url;
2557 DwPage *page;
2558 DwStyle style_attrs;
2559 const char *attrbuf;
2560 gint border;
2561
2562 /* This avoids loading images. Useful for viewing suspicious HTML email. */
2563 if (URL_FLAGS(html->linkblock->base_url) & URL_SpamSafe)
2564 return;
2565
2566 if (!(attrbuf = Html_get_attr(html, tag, tagsize, "src")) ||
2567 !(url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0)))
2568 return;
2569
2570 page = DW_PAGE (html->dw);
2571
2572
2573 usemap_url = NULL;
2574 if ((attrbuf = Html_get_attr(html, tag, tagsize, "usemap")))
2575 /* todo: usemap URLs outside of the document are not used. */
2576 usemap_url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
2577
2578 style_attrs = *html->stack[html->stack_top].style;
2579
2580 if (html->stack[html->stack_top].style->x_link != -1 ||
2581 usemap_url != NULL) {
2582 /* Images within links */
2583 border = 1;
2584 if ((attrbuf = Html_get_attr(html, tag, tagsize, "border")))
2585 border = strtol (attrbuf, NULL, 10);
2586
2587 if (html->stack[html->stack_top].style->x_link != -1)
2588 /* In this case we can use the text color */
2589 a_Dw_style_box_set_border_color
2590 (&style_attrs,
2591 a_Dw_style_shaded_color_new (style_attrs.color->color_val,
2592 html->bw->main_window->window));
2593 else
2594 a_Dw_style_box_set_border_color
2595 (&style_attrs,
2596 a_Dw_style_shaded_color_new (html->linkblock->link_color,
2597 html->bw->main_window->window));
2598
2599 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_SOLID);
2600 a_Dw_style_box_set_val (&style_attrs.border_width, border);
2601 }
2602
2603 Image = Html_add_new_image(html, tag, tagsize, &style_attrs, TRUE);
2604 Html_connect_signals(html, GTK_OBJECT(Image->dw));
2605 gtk_signal_connect_after(GTK_OBJECT(Image->dw), "button_press_event",
2606 GTK_SIGNAL_FUNC(Html_image_menu), html->bw);
2607
2608 /* Image maps */
2609 if (Html_get_attr(html, tag, tagsize, "ismap")) {
2610 /* BUG: if several ISMAP images follow each other without
2611 * being separated with a word, only the first one is ISMAPed
2612 */
2613 a_Dw_image_set_ismap (Image->dw);
2614 _MSG(" Html_tag_open_img: server-side map (ISMAP)\n");
2615 } else if (html->stack[html->stack_top].style->x_link != -1 &&
2616 usemap_url == NULL)
2617 /* For simple links, we have to suppress the "image_pressed" signal.
2618 * This is overridden for USEMAP images. */
2619 a_Dw_widget_set_button_sensitive (DW_WIDGET (Image->dw), FALSE);
2620
2621 if (usemap_url) {
2622 a_Dw_image_set_usemap (Image->dw, &html->linkblock->maps, usemap_url);
2623 a_Url_free (usemap_url);
2624 }
2625
2626 Html_load_image(html, url, Image);
2627 a_Url_free(url);
2628 }
2629
2630 /*
2631 * <map>
2632 */
2633 static void Html_tag_open_map(DilloHtml *html, char *tag, gint tagsize)
2634 {
2635 char *hash_name;
2636 const char *attrbuf;
2637 DilloUrl *url;
2638
2639 if (html->InFlags & IN_MAP) {
2640 MSG_HTML("nested <map>\n");
2641 } else {
2642 if ((attrbuf = Html_get_attr(html, tag, tagsize, "name"))) {
2643 hash_name = g_strdup_printf("#%s", attrbuf);
2644 url = Html_url_new(html, hash_name, NULL, 0, 0, 0, 0);
2645 a_Dw_image_map_list_add_map (&html->linkblock->maps, url);
2646 a_Url_free (url);
2647 g_free(hash_name);
2648 }
2649 html->InFlags |= IN_MAP;
2650 }
2651 }
2652
2653 /*
2654 * Handle close <MAP>
2655 */
2656 static void Html_tag_close_map(DilloHtml *html, gint TagIdx)
2657 {
2658 html->InFlags &= ~IN_MAP;
2659 Html_pop_tag(html, TagIdx);
2660 }
2661
2662 /*
2663 * Read coords in a string and fill a GdkPoint array
2664 */
2665 static int Html_read_coords(DilloHtml *html, const char *str, GdkPoint *array)
2666 {
2667 gint i, toggle, pending, coord;
2668 const char *tail = str;
2669 char *newtail = NULL;
2670
2671 i = 0;
2672 toggle = 0;
2673 pending = 1;
2674 while ( pending ) {
2675 coord = strtol(tail, &newtail, 10);
2676 if (toggle) {
2677 array[i].y = coord;
2678 array[++i].x = 0;
2679 toggle = 0;
2680 } else {
2681 array[i].x = coord;
2682 array[i].y = -1;
2683 toggle = 1;
2684 }
2685 if (!*newtail || (coord == 0 && newtail == tail)) {
2686 pending = 0;
2687 } else {
2688 if (*newtail != ',') {
2689 MSG_HTML("usemap coords MUST be separated with ','\n");
2690 }
2691 tail = newtail + 1;
2692 }
2693 }
2694
2695 return i;
2696 }
2697
2698 /*
2699 * <AREA>
2700 */
2701 static void Html_tag_open_area(DilloHtml *html, char *tag, gint tagsize)
2702 {
2703 /* todo: point must be a dynamic array */
2704 GdkPoint point[1024];
2705 DilloUrl* url;
2706 const char *attrbuf;
2707 gint type = DW_IMAGE_MAP_SHAPE_RECT;
2708 gint nbpoints, link = -1;
2709
2710 if ( (attrbuf = Html_get_attr(html, tag, tagsize, "shape")) ) {
2711 if ( g_strcasecmp(attrbuf, "rect") == 0 )
2712 type = DW_IMAGE_MAP_SHAPE_RECT;
2713 else if ( g_strcasecmp(attrbuf, "circle") == 0 )
2714 type = DW_IMAGE_MAP_SHAPE_CIRCLE;
2715 else if ( g_strncasecmp(attrbuf, "poly", 4) == 0 )
2716 type = DW_IMAGE_MAP_SHAPE_POLY;
2717 else
2718 type = DW_IMAGE_MAP_SHAPE_RECT;
2719 }
2720 /* todo: add support for coords in % */
2721 if ( (attrbuf = Html_get_attr(html, tag, tagsize, "coords")) ) {
2722 /* Is this a valid poly ?
2723 * rect = x0,y0,x1,y1 => 2
2724 * circle = x,y,r => 2
2725 * poly = x0,y0,x1,y1,x2,y2 minimum => 3 */
2726 nbpoints = Html_read_coords(html, attrbuf, point);
2727 } else
2728 return;
2729
2730 if ( Html_get_attr(html, tag, tagsize, "nohref") ) {
2731 link = -1;
2732 _MSG("nohref");
2733 }
2734
2735 if ((attrbuf = Html_get_attr(html, tag, tagsize, "href"))) {
2736 url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
2737 g_return_if_fail ( url != NULL );
2738 if ((attrbuf = Html_get_attr(html, tag, tagsize, "alt")))
2739 a_Url_set_alt(url, attrbuf);
2740
2741 link = Html_set_new_link(html, &url);
2742 }
2743
2744 a_Dw_image_map_list_add_shape(&html->linkblock->maps, type, link,
2745 point, nbpoints);
2746 }
2747
2748
2749 /*
2750 * Test and extract the link from a javascript instruction.
2751 */
2752 static const char* Html_get_javascript_link(DilloHtml *html)
2753 {
2754 size_t i;
2755 char ch, *p1, *p2;
2756 GString *Buf = html->attr_data;
2757
2758 if (g_strncasecmp("javascript", Buf->str, 10) == 0) {
2759 i = strcspn(Buf->str, "'\"");
2760 ch = Buf->str[i];
2761 if ((ch == '"' || ch == '\'') &&
2762 (p2 = strchr(Buf->str + i + 1 , ch))) {
2763 p1 = Buf->str + i;
2764 MSG_HTML("link depends on javascript()\n");
2765 g_string_truncate(Buf, p2 - Buf->str);
2766 g_string_erase(Buf, 0, p1 - Buf->str + 1);
2767 }
2768 }
2769 return Buf->str;
2770 }
2771
2772 /*
2773 * Register an anchor for this page.
2774 */
2775 static void Html_add_anchor(DilloHtml *html, const char *name)
2776 {
2777 _MSG("Registering ANCHOR: %s\n", name);
2778 if (!a_Dw_page_add_anchor(DW_PAGE(html->dw), name,
2779 html->stack[html->stack_top].style))
2780 MSG_HTML("Anchor names must be unique within the document\n");
2781 /* According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
2782 * differ only in case may not appear in the same document", but
2783 * "comparisons between fragment identifiers and anchor names must be
2784 * done by exact (case-sensitive) match." We ignore the case issue and
2785 * always test for exact matches. Moreover, what does uppercase mean
2786 * for Unicode characters outside the ASCII range? */
2787 }
2788
2789 /*
2790 * <A>
2791 */
2792 static void Html_tag_open_a(DilloHtml *html, char *tag, gint tagsize)
2793 {
2794 DwStyle style_attrs, *old_style;
2795 DilloUrl *url;
2796 const char *attrbuf;
2797 gchar *buf;
2798 gint buf_size;
2799
2800 /* todo: add support for MAP with A HREF */
2801 Html_tag_open_area(html, tag, tagsize);
2802
2803 if ((attrbuf = Html_get_attr(html, tag, tagsize, "href"))) {
2804 /* if it's a javascript link, extract the reference. */
2805 if (tolower(attrbuf[0]) == 'j')
2806 attrbuf = Html_get_javascript_link(html);
2807
2808 url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
2809 g_return_if_fail ( url != NULL );
2810
2811 old_style = html->stack[html->stack_top].style;
2812 style_attrs = *old_style;
2813
2814 if (a_Capi_get_buf(url, &buf, &buf_size)) {
2815 html->InVisitedLink = TRUE;
2816 style_attrs.color = a_Dw_style_color_new(
2817 html->linkblock->visited_color,
2818 /*
2819 a_Color_vc(html->linkblock->visited_color,
2820 html->stack[html->stack_top].style->color->color_val,
2821 html->linkblock->link_color,
2822 html->stack[html->stack_top].current_bg_color),
2823 */
2824 html->bw->main_window->window);
2825 } else {
2826 style_attrs.color = a_Dw_style_color_new
2827 (html->linkblock->link_color, html->bw->main_window->window);
2828 }
2829
2830 if ((attrbuf = Html_get_attr(html, tag, tagsize, "title")))
2831 style_attrs.x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
2832
2833 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
2834 style_attrs.x_link = Html_set_new_link(html, &url);
2835
2836 html->stack[html->stack_top].style =
2837 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2838 a_Dw_style_unref (old_style);
2839 }
2840
2841 if ((attrbuf = Html_get_attr(html, tag, tagsize, "name"))) {
2842 if (prefs.show_extra_warnings)
2843 Html_check_name_val(html, attrbuf, "name");
2844 /* html->NameVal is freed in Html_process_tag */
2845 html->NameVal = a_Url_decode_hex_str(attrbuf);
2846 Html_add_anchor(html, html->NameVal);
2847 }
2848 }
2849
2850 /*
2851 * <A> close function
2852 */
2853 static void Html_tag_close_a(DilloHtml *html, gint TagIdx)
2854 {
2855 html->InVisitedLink = FALSE;
2856 Html_pop_tag(html, TagIdx);
2857 }
2858
2859 /*
2860 * Insert underlined text in the page.
2861 */
2862 static void Html_tag_open_u(DilloHtml *html, char *tag, gint tagsize)
2863 {
2864 DwStyle *style;
2865 DwStyle style_attrs;
2866
2867 style = html->stack[html->stack_top].style;
2868 style_attrs = *style;
2869 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
2870 html->stack[html->stack_top].style =
2871 a_Dw_style_new(&style_attrs, html->bw->main_window->window);
2872 a_Dw_style_unref(style);
2873 }
2874
2875 /*
2876 * Insert strike-through text. Used by <S>, <STRIKE> and <DEL>.
2877 */
2878 static void Html_tag_open_strike(DilloHtml *html, char *tag, gint tagsize)
2879 {
2880 DwStyle *style;
2881 DwStyle style_attrs;
2882
2883 style = html->stack[html->stack_top].style;
2884 style_attrs = *style;
2885 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_LINE_THROUGH;
2886 html->stack[html->stack_top].style =
2887 a_Dw_style_new(&style_attrs, html->bw->main_window->window);
2888 a_Dw_style_unref(style);
2889 }
2890
2891 /*
2892 * <BLOCKQUOTE>
2893 */
2894 static void Html_tag_open_blockquote(DilloHtml *html, char *tag, gint tagsize)
2895 {
2896 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2897 html->stack[(html)->stack_top].style);
2898 Html_add_indented(html, 40, 40, 9);
2899 }
2900
2901 /*
2902 * Handle the <UL> tag.
2903 */
2904 static void Html_tag_open_ul(DilloHtml *html, char *tag, gint tagsize)
2905 {
2906 const char *attrbuf;
2907 DwStyleListStyleType list_style_type;
2908
2909 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2910 html->stack[(html)->stack_top].style);
2911 Html_add_indented(html, 40, 0, 9);
2912
2913 if ((attrbuf = Html_get_attr(html, tag, tagsize, "type"))) {
2914 /* list_style_type explicitly defined */
2915 if (g_strncasecmp(attrbuf, "disc", 4) == 0)
2916 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2917 else if (g_strncasecmp(attrbuf, "circle", 6) == 0)
2918 list_style_type = DW_STYLE_LIST_STYLE_TYPE_CIRCLE;
2919 else if (g_strncasecmp(attrbuf, "square", 6) == 0)
2920 list_style_type = DW_STYLE_LIST_STYLE_TYPE_SQUARE;
2921 else
2922 /* invalid value */
2923 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2924 } else {
2925 if (html->stack[html->stack_top].list_type == HTML_LIST_UNORDERED) {
2926 /* Nested <UL>'s. */
2927 /* --EG :: I changed the behavior here : types are cycling instead of
2928 * being forced to square. It's easier for mixed lists level counting.
2929 */
2930 switch (html->stack[html->stack_top].style->list_style_type) {
2931 case DW_STYLE_LIST_STYLE_TYPE_DISC:
2932 list_style_type = DW_STYLE_LIST_STYLE_TYPE_CIRCLE;
2933 break;
2934 case DW_STYLE_LIST_STYLE_TYPE_CIRCLE:
2935 list_style_type = DW_STYLE_LIST_STYLE_TYPE_SQUARE;
2936 break;
2937 case DW_STYLE_LIST_STYLE_TYPE_SQUARE:
2938 default: /* this is actually a bug */
2939 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2940 break;
2941 }
2942 } else {
2943 /* Either first <UL>, or a <OL> before. */
2944 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2945 }
2946 }
2947
2948 HTML_SET_TOP_ATTR(html, list_style_type, list_style_type);
2949 html->stack[html->stack_top].list_type = HTML_LIST_UNORDERED;
2950
2951 html->stack[html->stack_top].list_number = 0;
2952 html->stack[html->stack_top].ref_list_item = NULL;
2953 }
2954
2955 /*
2956 * Handle the <MENU> tag.
2957 * (Deprecated and almost the same as <UL>)
2958 */
2959 static void Html_tag_open_menu(DilloHtml *html, char *tag, gint tagsize)
2960 {
2961 DwStyleListStyleType list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2962
2963 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2964 html->stack[(html)->stack_top].style);
2965 Html_add_indented(html, 40, 0, 9);
2966 HTML_SET_TOP_ATTR(html, list_style_type, list_style_type);
2967 html->stack[html->stack_top].list_type = HTML_LIST_UNORDERED;
2968 html->stack[html->stack_top].list_number = 0;
2969 html->stack[html->stack_top].ref_list_item = NULL;
2970
2971 if (prefs.show_extra_warnings)
2972 MSG_HTML("it is strongly recommended using <UL> instead of <MENU>\n");
2973 }
2974
2975 /*
2976 * Handle the <OL> tag.
2977 */
2978 static void Html_tag_open_ol(DilloHtml *html, char *tag, gint tagsize)
2979 {
2980 const char *attrbuf;
2981 DwStyleListStyleType list_style_type;
2982 int n = 1;
2983
2984 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2985 html->stack[(html)->stack_top].style);
2986 Html_add_indented(html, 40, 0, 9);
2987
2988 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DECIMAL;
2989
2990 if ((attrbuf = Html_get_attr(html, tag, tagsize, "type"))) {
2991 if (*attrbuf == '1')
2992 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DECIMAL;
2993 else if (*attrbuf == 'a')
2994 list_style_type = DW_STYLE_LIST_STYLE_TYPE_LOWER_ALPHA;
2995 else if (*attrbuf == 'A')
2996 list_style_type = DW_STYLE_LIST_STYLE_TYPE_UPPER_ALPHA;
2997 else if (*attrbuf == 'i')
2998 list_style_type = DW_STYLE_LIST_STYLE_TYPE_LOWER_ROMAN;
2999 else if (*attrbuf == 'I')
3000 list_style_type = DW_STYLE_LIST_STYLE_TYPE_UPPER_ROMAN;
3001 }
3002
3003 HTML_SET_TOP_ATTR(html, list_style_type, list_style_type);
3004 html->stack[html->stack_top].list_type = HTML_LIST_ORDERED;
3005
3006 if ((attrbuf = Html_get_attr(html, tag, tagsize, "start")) &&
3007 (n = (int) strtol(attrbuf, NULL, 10)) < 0) {
3008 MSG_HTML( "illegal '-' character in START attribute; Starting from 0\n");
3009 n = 0;
3010 }
3011 html->stack[html->stack_top].list_number = n;
3012 html->stack[html->stack_top].ref_list_item = NULL;
3013 }
3014
3015 /*
3016 * Handle the <LI> tag.
3017 */
3018 static void Html_tag_open_li(DilloHtml *html, char *tag, gint tagsize)
3019 {
3020 DwWidget *bullet, *list_item, **ref_list_item;
3021 char str[64];
3022 const char *attrbuf;
3023 gint *list_number;
3024
3025 /* Get our parent tag's variables (used as state storage) */
3026 list_number = &html->stack[html->stack_top - 1].list_number;
3027 ref_list_item = &html->stack[html->stack_top - 1].ref_list_item;
3028
3029 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
3030 html->stack[(html)->stack_top].style);
3031
3032 switch (html->stack[html->stack_top].list_type) {
3033 case HTML_LIST_NONE:
3034 MSG_HTML("<li> outside <ul> or <ol>\n");
3035 list_item = a_Dw_list_item_new(NULL);
3036 Html_add_indented_widget(html, list_item, 0, 0, 0 /* or 1 */);
3037 bullet = a_Dw_bullet_new();
3038 a_Dw_list_item_init_with_widget(DW_LIST_ITEM(html->dw), bullet,
3039 html->stack[html->stack_top].style);
3040 break;
3041
3042 default:
3043 list_item = a_Dw_list_item_new((DwListItem*)*ref_list_item);
3044 Html_add_indented_widget(html, list_item, 0, 0, 0 /* or 1 */);
3045 *ref_list_item = list_item;
3046
3047 switch (html->stack[html->stack_top].list_type) {
3048 case HTML_LIST_UNORDERED:
3049 bullet = a_Dw_bullet_new();
3050 a_Dw_list_item_init_with_widget(DW_LIST_ITEM(html->dw), bullet,
3051 html->stack[html->stack_top].style);
3052 break;
3053
3054 case HTML_LIST_ORDERED:
3055 if ((attrbuf = Html_get_attr(html, tag, tagsize, "value")))
3056 *list_number = strtol(attrbuf, NULL, 10);
3057 if (*list_number < 0) {
3058 MSG_HTML(
3059 "illegal '-' character in VALUE attribute; Starting from 0\n");
3060 *list_number = 0;
3061 }
3062 a_Dw_style_numtostr
3063 (*list_number, str, 64,
3064 html->stack[html->stack_top].style->list_style_type);
3065 (*list_number)++;
3066 a_Dw_list_item_init_with_text(DW_LIST_ITEM (html->dw), g_strdup(str),
3067 html->stack[html->stack_top].style);
3068
3069 case HTML_LIST_NONE:
3070 /* make compiler happy */
3071 break;
3072 }
3073 }
3074 }
3075
3076 /*
3077 * <HR>
3078 */
3079 static void Html_tag_open_hr(DilloHtml *html, char *tag, gint tagsize)
3080 {
3081 DwWidget *hruler;
3082 DwStyle style_attrs;
3083 char *width_ptr;
3084 const char *attrbuf;
3085 gint32 size = 0;
3086
3087 width_ptr = Html_get_attr_wdef(html, tag, tagsize, "width", "100%");
3088
3089 style_attrs = *html->stack[html->stack_top].style;
3090
3091 if ((attrbuf = Html_get_attr(html, tag, tagsize, "size")))
3092 size = strtol(attrbuf, NULL, 10);
3093
3094 if ((attrbuf = Html_get_attr(html, tag, tagsize, "align"))) {
3095 if (g_strcasecmp (attrbuf, "left") == 0)
3096 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_LEFT;
3097 else if (g_strcasecmp (attrbuf, "right") == 0)
3098 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_RIGHT;
3099 else if (g_strcasecmp (attrbuf, "center") == 0)
3100 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_CENTER;
3101 }
3102
3103 /* todo: evaluate attribute */
3104 if (Html_get_attr(html, tag, tagsize, "noshade")) {
3105 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_SOLID);
3106 a_Dw_style_box_set_border_color
3107 (&style_attrs,
3108 a_Dw_style_shaded_color_new (style_attrs.color->color_val,
3109 html->bw->main_window->window));
3110 if (size < 1)
3111 size = 1;
3112 } else {
3113 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_INSET);
3114 a_Dw_style_box_set_border_color
3115 (&style_attrs,
3116 a_Dw_style_shaded_color_new
3117 (html->stack[html->stack_top].current_bg_color,
3118 html->bw->main_window->window));
3119 if (size < 2)
3120 size = 2;
3121 }
3122
3123 style_attrs.border_width.top =
3124 style_attrs.border_width.left = (size + 1) / 2;
3125 style_attrs.border_width.bottom =
3126 style_attrs.border_width.right = size / 2;
3127
3128 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5,
3129 html->stack[(html)->stack_top].style);
3130 hruler = a_Dw_hruler_new ();
3131 Html_add_widget(html, hruler, width_ptr, NULL, &style_attrs);
3132 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5,
3133 html->stack[(html)->stack_top].style);
3134 g_free(width_ptr);
3135 }
3136
3137 /*
3138 * <DL>
3139 */
3140 static void Html_tag_open_dl(DilloHtml *html, char *tag, gint tagsize)
3141 {
3142 /* may want to actually do some stuff here. */
3143 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3144 html->stack[(html)->stack_top].style);
3145 }
3146
3147 /*
3148 * <DT>
3149 */
3150 static void Html_tag_open_dt(DilloHtml *html, char *tag, gint tagsize)
3151 {
3152 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3153 html->stack[(html)->stack_top].style);
3154 Html_set_top_font(html, NULL, 0, 1, 1);
3155 }
3156
3157 /*
3158 * <DD>
3159 */
3160 static void Html_tag_open_dd(DilloHtml *html, char *tag, gint tagsize)
3161 {
3162 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3163 html->stack[(html)->stack_top].style);
3164 Html_add_indented(html, 40, 40, 9);
3165 }
3166
3167 /*
3168 * <PRE>
3169 */
3170 static void Html_tag_open_pre(DilloHtml *html, char *tag, gint tagsize)
3171 {
3172 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3173 html->stack[(html)->stack_top].style);
3174 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
3175
3176 /* Is the placement of this statement right? */
3177 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_PRE;
3178 HTML_SET_TOP_ATTR (html, white_space, DW_STYLE_WHITE_SPACE_PRE);
3179 html->pre_column = 0;
3180 html->PreFirstChar = TRUE;
3181 html->InFlags |= IN_PRE;
3182 }
3183
3184 /*
3185 * Custom close for <PRE>
3186 */
3187 static void Html_tag_close_pre(DilloHtml *html, gint TagIdx)
3188 {
3189 html->InFlags &= ~IN_PRE;
3190 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3191 html->stack[(html)->stack_top].style);
3192 Html_pop_tag(html, TagIdx);
3193 }
3194
3195 /*
3196 * Check whether a tag is in the "excluding" element set for PRE
3197 * Excl. Set = {IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, BASEFONT}
3198 */
3199 static gint Html_tag_pre_excludes(gint tag_idx)
3200 {
3201 char *es_set[] = {"img", "object", "applet", "big", "small", "sub", "sup",
3202 "font", "basefont", NULL};
3203 static gint ei_set[10], i;
3204
3205 /* initialize array */
3206 if (!ei_set[0])
3207 for (i = 0; es_set[i]; ++i)
3208 ei_set[i] = Html_tag_index(es_set[i]);
3209
3210 for (i = 0; ei_set[i]; ++i)
3211 if (tag_idx == ei_set[i])
3212 return 1;
3213 return 0;
3214 }
3215
3216 /*
3217 * Handle <FORM> tag
3218 */
3219 static void Html_tag_open_form(DilloHtml *html, char *tag, gint tagsize)
3220 {
3221 DilloUrl *action;
3222 DilloHtmlMethod method;
3223 DilloHtmlEnc enc;
3224 const char *attrbuf;
3225
3226 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3227 html->stack[(html)->stack_top].style);
3228
3229 if (html->InFlags & IN_FORM) {
3230 MSG_HTML("nested forms\n");
3231 return;
3232 }
3233 html->InFlags |= IN_FORM;
3234
3235 method = DILLO_HTML_METHOD_GET;
3236 if ((attrbuf = Html_get_attr(html, tag, tagsize, "method"))) {
3237 if (!g_strcasecmp(attrbuf, "post"))
3238 method = DILLO_HTML_METHOD_POST;
3239 /* todo: maybe deal with unknown methods? */
3240 }
3241 if ((attrbuf = Html_get_attr(html, tag, tagsize, "action")))
3242 action = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
3243 else
3244 action = a_Url_dup(html->linkblock->base_url);
3245 enc = DILLO_HTML_ENC_URLENCODING;
3246 if ( (attrbuf = Html_get_attr(html, tag, tagsize, "encoding")) ) {
3247 /* todo: maybe deal with unknown encodings? */
3248 }
3249 Html_form_new(html->linkblock, method, action, enc);
3250 a_Url_free(action);
3251 }
3252
3253 static void Html_tag_close_form(DilloHtml *html, gint TagIdx)
3254 {
3255 static gchar *SubmitTag =
3256 "<input type='submit' value='?Submit?' alt='dillo-generated-button'>";
3257 DilloHtmlForm *form;
3258 gint i;
3259
3260 if (html->InFlags & IN_FORM) {
3261 form = &(html->linkblock->forms[html->linkblock->num_forms - 1]);
3262
3263 /* If we don't have a submit button and the user desires one,
3264 let's add a custom one */
3265 if (form->num_submit_buttons == 0) {
3266 if (prefs.show_extra_warnings || form->num_entry_fields != 1)
3267 MSG_HTML("FORM lacks a Submit button\n");
3268 if (prefs.generate_submit) {
3269 MSG_HTML(" (added a submit button internally)\n");
3270 Html_tag_open_input(html, SubmitTag, strlen(SubmitTag));
3271 form->num_submit_buttons = 0;
3272 }
3273 }
3274
3275 /* Make buttons sensitive again */
3276 for (i = 0; i < form->num_inputs; i++) {
3277 /* Check for tricky HTML (e.g. <input type=image>) */
3278 if (!form->inputs[i].widget)
3279 continue;
3280 if (form->inputs[i].type == DILLO_HTML_INPUT_SUBMIT ||
3281 form->inputs[i].type == DILLO_HTML_INPUT_RESET) {
3282 gtk_widget_set_sensitive(form->inputs[i].widget, TRUE);
3283 } else if (form->inputs[i].type == DILLO_HTML_INPUT_IMAGE ||
3284 form->inputs[i].type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
3285 form->inputs[i].type == DILLO_HTML_INPUT_BUTTON_RESET) {
3286 a_Dw_button_set_sensitive(DW_BUTTON(form->inputs[i].widget), TRUE);
3287 }
3288 }
3289 }
3290 html->InFlags &= ~IN_FORM;
3291 html->InFlags &= ~IN_SELECT;
3292 html->InFlags &= ~IN_TEXTAREA;
3293 Html_pop_tag(html, TagIdx);
3294 }
3295
3296 /*
3297 * Handle <META>
3298 * We do not support http-equiv=refresh because it's non standard,
3299 * (the HTML 4.01 SPEC recommends explicitily to avoid it), and it
3300 * can be easily abused!
3301 *
3302 * More info at:
3303 * http://lists.w3.org/Archives/Public/www-html/2000Feb/thread.html#232
3304 *
3305 * todo: Note that we're sending custom HTML while still IN_HEAD. This
3306 * is a hackish way to put the message. A much cleaner approach is to
3307 * build a custom widget for it.
3308 */
3309 static void Html_tag_open_meta(DilloHtml *html, char *tag, gint tagsize)
3310 {
3311 const gchar *meta_template =
3312 "<table width='100%%'><tr><td bgcolor='#ee0000'>Warning:</td>\n"
3313 " <td bgcolor='#8899aa' width='100%%'>\n"
3314 " This page uses the NON-STANDARD meta refresh tag.<br> The HTML 4.01 SPEC\n"
3315 " (sec 7.4.4) recommends explicitly to avoid it.</td></tr>\n"
3316 " <tr><td bgcolor='#a0a0a0' colspan='2'>The author wanted you to go\n"
3317 " <a href='%s'>here</a>%s</td></tr></table><br>\n";
3318
3319 const gchar *equiv, *content;
3320 gchar *html_msg, delay_str[64];
3321 gint delay;
3322
3323 /* only valid inside HEAD */
3324 if (!(html->InFlags & IN_HEAD)) {
3325 MSG_HTML("META elements must be inside the HEAD section\n");
3326 return;
3327 }
3328
3329 if ((equiv = Html_get_attr(html, tag, tagsize, "http-equiv")) &&
3330 !g_strcasecmp(equiv, "refresh") &&
3331 (content = Html_get_attr(html, tag, tagsize, "content"))) {
3332
3333 /* Get delay, if present, and make a message with it */
3334 if ((delay = strtol(content, NULL, 0)))
3335 g_snprintf(delay_str, 64, " after %d second%s.",
3336 delay, (delay > 1) ? "s" : "");
3337 else
3338 sprintf(delay_str, ".");
3339
3340 /* Skip to anything after "URL=" */
3341 while (*content && *(content++) != '=');
3342
3343 /* Send a custom HTML message
3344 * todo: this is a hairy hack, It'd be much better to build a widget. */
3345 html_msg = g_strdup_printf(meta_template, content, delay_str);
3346 {
3347 DilloHtmlProcessingState SaveFlags = html->InFlags;
3348 html->InFlags = IN_BODY;
3349 html->TagSoup = FALSE;
3350 Html_write_raw(html, html_msg, strlen(html_msg), 0);
3351 html->TagSoup = TRUE;
3352 html->InFlags = SaveFlags;
3353 }
3354 g_free(html_msg);
3355 }
3356 }
3357
3358 /*
3359 * Set the history of the menu to be consistent with the active menuitem.
3360 */
3361 static void Html_select_set_history(DilloHtmlInput *input)
3362 {
3363 gint i;
3364
3365 for (i = 0; i < input->select->num_options; i++) {
3366 if (GTK_CHECK_MENU_ITEM(input->select->options[i].menuitem)->active) {
3367 gtk_option_menu_set_history(GTK_OPTION_MENU(input->widget), i);
3368 break;
3369 }
3370 }
3371 }
3372
3373 /*
3374 * Reset the input widget to the initial value.
3375 */
3376 static void Html_reset_input(DilloHtmlInput *input)
3377 {
3378 gint i;
3379
3380 switch (input->type) {
3381 case DILLO_HTML_INPUT_TEXT:
3382 case DILLO_HTML_INPUT_PASSWORD:
3383 gtk_entry_set_text(GTK_ENTRY(input->widget), input->init_str);
3384 break;
3385 case DILLO_HTML_INPUT_CHECKBOX:
3386 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(input->widget),
3387 input->init_val);
3388 break;
3389 case DILLO_HTML_INPUT_RADIO:
3390 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(input->widget),
3391 input->init_val);
3392 break;
3393 case DILLO_HTML_INPUT_SELECT:
3394 if (input->select != NULL) {
3395 /* this is in reverse order so that, in case more than one was
3396 * selected, we get the last one, which is consistent with handling
3397 * of multiple selected options in the layout code. */
3398 for (i = input->select->num_options - 1; i >= 0; i--) {
3399 if (input->select->options[i].init_val) {
3400 gtk_menu_item_activate(GTK_MENU_ITEM
3401 (input->select->options[i].menuitem));
3402 Html_select_set_history(input);
3403 break;
3404 }
3405 }
3406 }
3407 break;
3408 case DILLO_HTML_INPUT_SEL_LIST:
3409 if (!input->select)
3410 break;
3411 for (i = 0; i < input->select->num_options; i++) {
3412 if (input->select->options[i].init_val) {
3413 if (input->select->options[i].menuitem->state == GTK_STATE_NORMAL)
3414 gtk_list_select_child(GTK_LIST(input->select->menu),
3415 input->select->options[i].menuitem);
3416 } else {
3417 if (input->select->options[i].menuitem->state==GTK_STATE_SELECTED)
3418 gtk_list_unselect_child(GTK_LIST(input->select->menu),
3419 input->select->options[i].menuitem);
3420 }
3421 }
3422 break;
3423 case DILLO_HTML_INPUT_TEXTAREA:
3424 if (input->init_str != NULL) {
3425 int pos = 0;
3426 gtk_editable_delete_text(GTK_EDITABLE(input->widget), 0, -1);
3427 gtk_editable_insert_text(GTK_EDITABLE(input->widget), input->init_str,
3428 strlen(input->init_str), &pos);
3429 }
3430 break;
3431 default:
3432 break;
3433 }
3434 }
3435
3436
3437 /*
3438 * Add a new input to the form data structure, setting the initial
3439 * values.
3440 */
3441 static void Html_add_input(DilloHtmlForm *form,
3442 DilloHtmlInputType type,
3443 GtkWidget *widget,
3444 const char *name,
3445 const char *init_str,
3446 DilloHtmlSelect *select,
3447 gboolean init_val)
3448 {
3449 DilloHtmlInput *input;
3450
3451 _MSG("name=[%s] init_str=[%s] init_val=[%d]\n",
3452 name, init_str, init_val);
3453 a_List_add(form->inputs, form->num_inputs, form->num_inputs_max);
3454 input = &(form->inputs[form->num_inputs]);
3455 input->type = type;
3456 input->widget = widget;
3457 input->name = (name) ? g_strdup(name) : NULL;
3458 input->init_str = (init_str) ? g_strdup(init_str) : NULL;
3459 input->select = select;
3460 input->init_val = init_val;
3461 Html_reset_input(input);
3462
3463 /* some stats */
3464 if (type == DILLO_HTML_INPUT_PASSWORD ||
3465 type == DILLO_HTML_INPUT_TEXT ||
3466 type == DILLO_HTML_INPUT_TEXTAREA) {
3467 form->num_entry_fields++;
3468 } else if (type == DILLO_HTML_INPUT_SUBMIT ||
3469 type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
3470 type == DILLO_HTML_INPUT_IMAGE) {
3471 form->num_submit_buttons++;
3472 }
3473 form->num_inputs++;
3474 }
3475
3476
3477 /*
3478 * Given a GtkWidget, find the form that contains it.
3479 * Return value: form_index if successful, -1 otherwise.
3480 */
3481 static int Html_find_form(GtkWidget *reset, DilloHtmlLB *html_lb)
3482 {
3483 gint form_index;
3484 gint input_index;
3485 DilloHtmlForm *form;
3486
3487 for (form_index = 0; form_index < html_lb->num_forms; form_index++) {
3488 form = &(html_lb->forms[form_index]);
3489 for (input_index = 0; input_index < form->num_inputs; input_index++) {
3490 if (form->inputs[input_index].widget == reset) {
3491 return form_index;
3492 }
3493 }
3494 }
3495 return -1;
3496 }
3497
3498 /*
3499 * Reset all inputs in the form containing reset to their initial values.
3500 * In general, reset is the reset button for the form.
3501 */
3502 static void Html_reset_form(GtkWidget *reset, DilloHtmlLB *html_lb)
3503 {
3504 gint i, j;
3505 DilloHtmlForm *form;
3506
3507 if ( (i = Html_find_form(reset, html_lb)) != -1 ){
3508 form = &html_lb->forms[i];
3509 for ( j = 0; j < form->num_inputs; j++)
3510 Html_reset_input(&(form->inputs[j]));
3511 }
3512 }
3513
3514 /*
3515 * Urlencode 'val' and append it to 'str'
3516 */
3517 static void Html_urlencode_append(GString *str, const char *val)
3518 {
3519 gchar *enc_val = a_Url_encode_hex_str(val);
3520 g_string_append(str, enc_val);
3521 g_free(enc_val);
3522 }
3523
3524 /*
3525 * Append a name-value pair to an existing url.
3526 * (name and value are urlencoded before appending them)
3527 */
3528 static void
3529 Html_append_input(GString *url, const char *name, const char *value)
3530 {
3531 if (name != NULL) {
3532 Html_urlencode_append(url, name);
3533 g_string_append_c(url, '=');
3534 Html_urlencode_append(url, value);
3535 g_string_append_c(url, '&');
3536 }
3537 }
3538
3539 /*
3540 * Append a image button click position to an existing url.
3541 */
3542 static void Html_append_clickpos(GString *url, const char *name, int x, int y)
3543 {
3544 if (name) {
3545 Html_urlencode_append(url, name);
3546 g_string_sprintfa(url, ".x=%d&", x);
3547 Html_urlencode_append(url, name);
3548 g_string_sprintfa(url, ".y=%d&", y);
3549 } else
3550 g_string_sprintfa(url, "x=%d&y=%d&", x, y);
3551 }
3552
3553 /*
3554 * Submit the form containing the submit input by making a new query URL
3555 * and sending it with a_Nav_push.
3556 * (Called by GTK+)
3557 * click_x and click_y are used only by input images and are set only when
3558 * called by Html_image_clicked. GTK+ does NOT give these arguments.
3559 */
3560 static void Html_submit_form(GtkWidget *submit, DilloHtmlLB *html_lb,
3561 gint click_x, gint click_y)
3562 {
3563 gint i, input_index;
3564 DilloHtmlForm *form;
3565 DilloHtmlInput *input;
3566 DilloUrl *new_url;
3567 gchar *url_str, *action_str, *p, *text;
3568
3569 /* Search the form that generated the submit event */
3570 if ( (i = Html_find_form(submit, html_lb)) == -1 )
3571 return;
3572
3573 form = &html_lb->forms[i];
3574 if ((form->method == DILLO_HTML_METHOD_GET) ||
3575 (form->method == DILLO_HTML_METHOD_POST)) {
3576 GString *DataStr = g_string_sized_new(4096);
3577
3578 DEBUG_MSG(3,"Html_submit_form form->action=%s\n",URL_STR_(form->action));
3579
3580 for (input_index = 0; input_index < form->num_inputs; input_index++) {
3581 input = &(form->inputs[input_index]);
3582 switch (input->type) {
3583 case DILLO_HTML_INPUT_TEXT:
3584 case DILLO_HTML_INPUT_PASSWORD:
3585 Html_append_input(DataStr, input->name,
3586 gtk_entry_get_text(GTK_ENTRY(input->widget)));
3587 break;
3588 case DILLO_HTML_INPUT_CHECKBOX:
3589 case DILLO_HTML_INPUT_RADIO:
3590 if (GTK_TOGGLE_BUTTON(input->widget)->active &&
3591 input->name != NULL && input->init_str != NULL) {
3592 Html_append_input(DataStr, input->name, input->init_str);
3593 }
3594 break;
3595 case DILLO_HTML_INPUT_HIDDEN:
3596 Html_append_input(DataStr, input->name, input->init_str);
3597 break;
3598 case DILLO_HTML_INPUT_SELECT:
3599 for (i = 0; i < input->select->num_options; i++) {
3600 if (GTK_CHECK_MENU_ITEM(input->select->options[i].menuitem)->
3601 active) {
3602 Html_append_input(DataStr, input->name,
3603 input->select->options[i].value);
3604 break;
3605 }
3606 }
3607 break;
3608 case DILLO_HTML_INPUT_SEL_LIST:
3609 for (i = 0; i < input->select->num_options; i++) {
3610 if (input->select->options[i].menuitem->state ==
3611 GTK_STATE_SELECTED) {
3612 Html_append_input(DataStr, input->name,
3613 input->select->options[i].value);
3614 }
3615 }
3616 break;
3617 case DILLO_HTML_INPUT_TEXTAREA:
3618 text = gtk_editable_get_chars(GTK_EDITABLE (input->widget),0,-1);
3619 Html_append_input(DataStr, input->name, text);
3620 g_free(text);
3621 break;
3622 case DILLO_HTML_INPUT_INDEX:
3623 Html_urlencode_append(DataStr,
3624 gtk_entry_get_text(GTK_ENTRY(input->widget)));
3625 break;
3626 case DILLO_HTML_INPUT_IMAGE:
3627 if (input->widget == submit) {
3628 Html_append_input(DataStr, input->name, input->init_str);
3629 Html_append_clickpos(DataStr, input->name, click_x, click_y);
3630 }
3631 break;
3632 case DILLO_HTML_INPUT_SUBMIT:
3633 case DILLO_HTML_INPUT_BUTTON_SUBMIT:
3634 /* Only the button that triggered the submit. */
3635 if (input->widget == submit && form->num_submit_buttons > 0)
3636 Html_append_input(DataStr, input->name, input->init_str);
3637 break;
3638 default:
3639 break;
3640 } /* switch */
3641 } /* for (inputs) */
3642
3643 if ( DataStr->str[DataStr->len - 1] == '&' )
3644 g_string_truncate(DataStr, DataStr->len - 1);
3645
3646 /* form->action was previously resolved against base URL */
3647 action_str = g_strdup(URL_STR(form->action));
3648
3649 if (form->method == DILLO_HTML_METHOD_POST) {
3650 new_url = a_Url_new(action_str, NULL, 0, 0, 0);
3651 a_Url_set_data(new_url, DataStr->str);
3652 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Post);
3653 } else {
3654 /* remove <fragment> and <query> sections if present */
3655 if ((p = strchr(action_str, '#')))
3656 *p = 0;
3657 if ((p = strchr(action_str, '?')))
3658 *p = 0;
3659
3660 url_str = g_strconcat(action_str, "?", DataStr->str, NULL);
3661 new_url = a_Url_new(url_str, NULL, 0, 0, 0);
3662 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Get);
3663 g_free(url_str);
3664 }
3665
3666 a_Nav_push(html_lb->bw, new_url);
3667 g_free(action_str);
3668 g_string_free(DataStr, TRUE);
3669 a_Url_free(new_url);
3670 } else {
3671 MSG("Html_submit_form: Method unknown\n");
3672 }
3673
3674 /* now, make the rendered area have its focus back */
3675 gtk_widget_grab_focus(GTK_BIN(html_lb->bw->docwin)->child);
3676 }
3677
3678
3679 /*
3680 * Submit form if it has no submit button.
3681 * (Called by GTK+ when the user presses enter in a text entry within a form)
3682 */
3683 static void Html_enter_submit_form(GtkWidget *submit, DilloHtmlLB *html_lb)
3684 {
3685 gint i;
3686
3687 /* Search the form that generated the submit event */
3688 if ( (i = Html_find_form(submit, html_lb)) == -1 )
3689 return;
3690
3691 /* Submit on enterpress when there's a single text-entry only,
3692 * or if the user set enter to always submit */
3693 if ((html_lb->forms[i].num_entry_fields == 1) ||
3694 prefs.enterpress_forces_submit)
3695 Html_submit_form(submit, html_lb, 1, 1);
3696 }
3697
3698 /*
3699 * Call submit form, when input image has been clicked
3700 */
3701 static void Html_image_clicked(DwWidget *widget, gint x, gint y,
3702 DilloHtmlLB *lb)
3703 {
3704 _MSG("Hallo! (%d, %d, %p)\n", x, y, lb);
3705 Html_submit_form((GtkWidget*) widget, lb, x, y);
3706 }
3707
3708 /*
3709 * Create input image for the form
3710 */
3711 static DwWidget *Html_input_image(DilloHtml *html, char *tag, gint tagsize,
3712 DilloHtmlLB *html_lb, DilloUrl *action)
3713 {
3714 DilloImage *Image;
3715 DwWidget *button;
3716 DilloUrl *url = NULL;
3717 DwStyle style_attrs;
3718 const char *attrbuf;
3719
3720 if ((attrbuf = Html_get_attr(html, tag, tagsize, "src")) &&
3721 (url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0))) {
3722 button = a_Dw_button_new (0, FALSE);
3723 a_Dw_page_add_widget (DW_PAGE (html->dw), button,
3724 html->stack[html->stack_top].style);
3725 gtk_signal_connect(GTK_OBJECT(button), "clicked_at",
3726 GTK_SIGNAL_FUNC(Html_image_clicked), html_lb);
3727 a_Dw_button_set_sensitive(DW_BUTTON(button), FALSE);
3728
3729 /* create new image and add it to the button */
3730 if ((Image = Html_add_new_image(html, tag, tagsize, &style_attrs,
3731 FALSE))) {
3732 /* By suppressing the "image_pressed" signal, the events are sent
3733 * to the parent DwButton */
3734 a_Dw_widget_set_button_sensitive (DW_WIDGET (Image->dw), FALSE);
3735 a_Dw_widget_set_style(DW_WIDGET(Image->dw),
3736 html->stack[html->stack_top].style);
3737 a_Dw_container_add(DW_CONTAINER(button), DW_WIDGET(Image->dw));
3738 a_Dw_widget_set_cursor(DW_WIDGET(Image->dw), Dw_cursor_hand);
3739 Html_load_image(html, url, Image);
3740 a_Url_free(url);
3741 return button;
3742 }
3743 }
3744
3745 DEBUG_MSG(10, "Html_input_image: unable to create image submit.\n");
3746 a_Url_free(url);
3747 return NULL;
3748 }
3749
3750 /*
3751 * Add a new input to current form
3752 */
3753 static void Html_tag_open_input(DilloHtml *html, char *tag, gint tagsize)
3754 {
3755 DilloHtmlForm *form;
3756 DilloHtmlInputType inp_type;
3757 DilloHtmlLB *html_lb;
3758 DwWidget *embed_gtk;
3759 GtkWidget *widget = NULL;
3760 GSList *group;
3761 char *value, *name, *type, *init_str;
3762 const char *attrbuf, *label;
3763 gboolean init_val = FALSE;
3764 gint input_index;
3765
3766 if (!(html->InFlags & IN_FORM)) {
3767 MSG_HTML("input camp outside <form>\n");
3768 return;
3769 }
3770
3771 html_lb = html->linkblock;
3772 form = &(html_lb->forms[html_lb->num_forms - 1]);
3773
3774 /* Get 'value', 'name' and 'type' */
3775 value = Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
3776 name = Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
3777 type = Html_get_attr_wdef(html, tag, tagsize, "type", "");
3778
3779 init_str = NULL;
3780 if (!g_strcasecmp(type, "password")) {
3781 inp_type = DILLO_HTML_INPUT_PASSWORD;
3782 widget = gtk_entry_new();
3783 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
3784 if (value)
3785 init_str = g_strdup(Html_get_attr(html, tag, tagsize, "value"));
3786 } else if (!g_strcasecmp(type, "checkbox")) {
3787 inp_type = DILLO_HTML_INPUT_CHECKBOX;
3788 widget = gtk_check_button_new();
3789 init_val = (Html_get_attr(html, tag, tagsize, "checked") != NULL);
3790 init_str = (value) ? value : g_strdup("on");
3791 } else if (!g_strcasecmp(type, "radio")) {
3792 inp_type = DILLO_HTML_INPUT_RADIO;
3793 group = NULL;
3794 for (input_index = 0; input_index < form->num_inputs; input_index++) {
3795 if (form->inputs[input_index].type == DILLO_HTML_INPUT_RADIO &&
3796 (form->inputs[input_index].name &&
3797 !g_strcasecmp(form->inputs[input_index].name, name)) ){
3798 group = gtk_radio_button_group(GTK_RADIO_BUTTON
3799 (form->inputs[input_index].widget));
3800 form->inputs[input_index].init_val = TRUE;
3801 break;
3802 }
3803 }
3804 widget = gtk_radio_button_new(group);
3805
3806 init_val = (Html_get_attr(html, tag, tagsize, "checked") != NULL);
3807 init_str = (value) ? value : NULL;
3808 } else if (!g_strcasecmp(type, "hidden")) {
3809 inp_type = DILLO_HTML_INPUT_HIDDEN;
3810 if (value)
3811 init_str = g_strdup(Html_get_attr(html, tag, tagsize, "value"));
3812 } else if (!g_strcasecmp(type, "submit")) {
3813 inp_type = DILLO_HTML_INPUT_SUBMIT;
3814 init_str = (value) ? value : g_strdup("submit");
3815 widget = gtk_button_new_with_label(init_str);
3816 gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
3817 gtk_signal_connect(GTK_OBJECT(widget), "clicked",
3818 GTK_SIGNAL_FUNC(Html_submit_form), html_lb);
3819 } else if (!g_strcasecmp(type, "reset")) {
3820 inp_type = DILLO_HTML_INPUT_RESET;
3821 init_str = (value) ? value : g_strdup("Reset");
3822 widget = gtk_button_new_with_label(init_str);
3823 gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
3824 gtk_signal_connect(GTK_OBJECT(widget), "clicked",
3825 GTK_SIGNAL_FUNC(Html_reset_form), html_lb);
3826 } else if (!g_strcasecmp(type, "image")) {
3827 if (URL_FLAGS(html->linkblock->base_url) & URL_SpamSafe) {
3828 /* Don't request the image, make a text submit button instead */
3829 inp_type = DILLO_HTML_INPUT_SUBMIT;
3830 attrbuf = Html_get_attr(html, tag, tagsize, "alt");
3831 label = attrbuf ? attrbuf : value ? value : name ? name : "Submit";
3832 init_str = g_strdup(label);
3833 widget = gtk_button_new_with_label(init_str);
3834 gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
3835 gtk_signal_connect(GTK_OBJECT(widget), "clicked",
3836 GTK_SIGNAL_FUNC(Html_submit_form), html_lb);
3837 } else {
3838 inp_type = DILLO_HTML_INPUT_IMAGE;
3839 /* use a dw_image widget */
3840 widget = (GtkWidget*) Html_input_image(html, tag, tagsize,
3841 html_lb, form->action);
3842 init_str = value;
3843 }
3844 } else if (!g_strcasecmp(type, "file")) {
3845 /* todo: implement it! */
3846 inp_type = DILLO_HTML_INPUT_FILE;
3847 init_str = (value) ? value : NULL;
3848 MSG("An input of the type \"file\" wasn't rendered!\n");
3849 } else if (!g_strcasecmp(type, "button")) {
3850 inp_type = DILLO_HTML_INPUT_BUTTON;
3851 if (value) {
3852 init_str = value;
3853 widget = gtk_button_new_with_label(init_str);
3854 }
3855 } else {
3856 /* Text input, which also is the default */
3857 inp_type = DILLO_HTML_INPUT_TEXT;
3858 widget = gtk_entry_new();
3859
3860 init_str = (value) ? value : NULL;
3861 gtk_signal_connect(GTK_OBJECT(widget), "activate",
3862 GTK_SIGNAL_FUNC(Html_enter_submit_form),
3863 html_lb);
3864 }
3865
3866 Html_add_input(form, inp_type, widget, name,
3867 (init_str) ? init_str : "", NULL, init_val);
3868
3869 if (widget != NULL && inp_type != DILLO_HTML_INPUT_IMAGE) {
3870 if (inp_type == DILLO_HTML_INPUT_TEXT ||
3871 inp_type == DILLO_HTML_INPUT_PASSWORD) {
3872 /*
3873 * The following is necessary, because gtk_entry_button_press
3874 * returns FALSE, so the event would be delivered to the
3875 * GtkDwScrolledFrame, which then would be focused, instead of
3876 * the entry.
3877 */
3878 gtk_signal_connect_after(GTK_OBJECT(widget), "button_press_event",
3879 GTK_SIGNAL_FUNC(gtk_true), NULL);
3880
3881 /* Readonly or not? */
3882 gtk_entry_set_editable(
3883 GTK_ENTRY(widget),
3884 !(Html_get_attr(html, tag, tagsize, "readonly")));
3885
3886 /* Set width of the entry */
3887 if ((attrbuf = Html_get_attr(html, tag, tagsize, "size")))
3888 gtk_widget_set_usize(widget, (strtol(attrbuf, NULL, 10) + 1) *
3889 gdk_char_width(widget->style->font, '0'), 0);
3890
3891 /* Maximum length of the text in the entry */
3892 if ((attrbuf = Html_get_attr(html, tag, tagsize, "maxlength")))
3893 gtk_entry_set_max_length(GTK_ENTRY(widget),
3894 strtol(attrbuf, NULL, 10));
3895 }
3896 gtk_widget_show(widget);
3897
3898 embed_gtk = a_Dw_embed_gtk_new();
3899 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), widget);
3900 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
3901 html->stack[html->stack_top].style);
3902 }
3903
3904 g_free(type);
3905 g_free(name);
3906 if (init_str != value)
3907 g_free(init_str);
3908 g_free(value);
3909 }
3910
3911 /*
3912 * The ISINDEX tag is just a deprecated form of <INPUT type=text> with
3913 * implied FORM, afaics.
3914 */
3915 static void Html_tag_open_isindex(DilloHtml *html, char *tag, gint tagsize)
3916 {
3917 DilloHtmlForm *form;
3918 DilloHtmlLB *html_lb;
3919 DilloUrl *action;
3920 GtkWidget *widget;
3921 DwWidget *embed_gtk;
3922 const char *attrbuf;
3923
3924 html_lb = html->linkblock;
3925
3926 if ((attrbuf = Html_get_attr(html, tag, tagsize, "action")))
3927 action = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
3928 else
3929 action = a_Url_dup(html->linkblock->base_url);
3930
3931 Html_form_new(html->linkblock, DILLO_HTML_METHOD_GET, action,
3932 DILLO_HTML_ENC_URLENCODING);
3933
3934 form = &(html_lb->forms[html_lb->num_forms - 1]);
3935
3936 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3937 html->stack[(html)->stack_top].style);
3938
3939 if ((attrbuf = Html_get_attr(html, tag, tagsize, "prompt")))
3940 a_Dw_page_add_text(DW_PAGE (html->dw), g_strdup(attrbuf),
3941 html->stack[html->stack_top].style);
3942
3943 widget = gtk_entry_new();
3944 Html_add_input(form, DILLO_HTML_INPUT_INDEX,
3945 widget, NULL, NULL, NULL, FALSE);
3946 gtk_signal_connect(GTK_OBJECT(widget), "activate",
3947 GTK_SIGNAL_FUNC(Html_enter_submit_form),
3948 html_lb);
3949 gtk_widget_show(widget);
3950 /* compare <input type=text> */
3951 gtk_signal_connect_after(GTK_OBJECT(widget), "button_press_event",
3952 GTK_SIGNAL_FUNC(gtk_true),
3953 NULL);
3954
3955 embed_gtk = a_Dw_embed_gtk_new();
3956 a_Dw_embed_gtk_add_gtk(DW_EMBED_GTK(embed_gtk), widget);
3957 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
3958 html->stack[html->stack_top].style);
3959
3960 a_Url_free(action);
3961 }
3962
3963 /*
3964 * Close textarea
3965 * (TEXTAREA is parsed in VERBATIM mode, and entities are handled here)
3966 */
3967 static void Html_tag_close_textarea(DilloHtml *html, gint TagIdx)
3968 {
3969 DilloHtmlLB *html_lb = html->linkblock;
3970 char *str;
3971 DilloHtmlForm *form;
3972 gint i;
3973
3974 if (html->InFlags & IN_FORM && html->InFlags & IN_TEXTAREA) {
3975 /* Remove the line ending that follows the opening tag */
3976 if (html->Stash->str[0] == '\r')
3977 html->Stash = g_string_erase(html->Stash, 0, 1);
3978 if (html->Stash->str[0] == '\n')
3979 html->Stash = g_string_erase(html->Stash, 0, 1);
3980
3981 /* As the spec recommends to canonicalize line endings, it is safe
3982 * to replace '\r' with '\n'. It will be canonicalized anyway! */
3983 for (i = 0; i < html->Stash->len; ++i) {
3984 if (html->Stash->str[i] == '\r') {
3985 if (html->Stash->str[i + 1] == '\n')
3986 g_string_erase(html->Stash, i, 1);
3987 else
3988 html->Stash->str[i] = '\n';
3989 }
3990 }
3991
3992 /* The HTML3.2 spec says it can have "text and character entities". */
3993 str = Html_parse_entities(html, html->Stash->str, html->Stash->len);
3994
3995 form = &(html_lb->forms[html_lb->num_forms - 1]);
3996 form->inputs[form->num_inputs - 1].init_str = str;
3997 gtk_text_insert(GTK_TEXT(form->inputs[form->num_inputs - 1].widget),
3998 NULL, NULL, NULL, str, -1);
3999
4000 html->InFlags &= ~IN_TEXTAREA;
4001 }
4002 Html_pop_tag(html, TagIdx);
4003 }
4004
4005 /*
4006 * The textarea tag
4007 * (todo: It doesn't support wrapping).
4008 */
4009 static void Html_tag_open_textarea(DilloHtml *html, char *tag, gint tagsize)
4010 {
4011 DilloHtmlLB *html_lb;
4012 DilloHtmlForm *form;
4013 GtkWidget *widget;
4014 GtkWidget *scroll;
4015 DwWidget *embed_gtk;
4016 char *name;
4017 const char *attrbuf;
4018 int cols, rows;
4019
4020 /* We can't push a new <FORM> because the 'action' URL is unknown */
4021 if (!(html->InFlags & IN_FORM)) {
4022 MSG_HTML("<textarea> outside <form>\n");
4023 html->ReqTagClose = TRUE;
4024 return;
4025 }
4026 if (html->InFlags & IN_TEXTAREA) {
4027 MSG_HTML("nested <textarea>\n");
4028 html->ReqTagClose = TRUE;
4029 return;
4030 }
4031
4032 html->InFlags |= IN_TEXTAREA;
4033 html_lb = html->linkblock;
4034 form = &(html_lb->forms[html_lb->num_forms - 1]);
4035 Html_stash_init(html);
4036 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
4037
4038 cols = 20;
4039 if ((attrbuf = Html_get_attr(html, tag, tagsize, "cols")))
4040 cols = strtol(attrbuf, NULL, 10);
4041 rows = 10;
4042 if ((attrbuf = Html_get_attr(html, tag, tagsize, "rows")))
4043 rows = strtol(attrbuf, NULL, 10);
4044 name = NULL;
4045 if ((attrbuf = Html_get_attr(html, tag, tagsize, "name")))
4046 name = g_strdup(attrbuf);
4047
4048 widget = gtk_text_new(NULL, NULL);
4049 /* compare <input type=text> */
4050 gtk_signal_connect_after(GTK_OBJECT(widget), "button_press_event",
4051 GTK_SIGNAL_FUNC(gtk_true),
4052 NULL);
4053
4054 /* Calculate the width and height based on the cols and rows
4055 * todo: Get it right... Get the metrics from the font that will be used.
4056 */
4057 gtk_widget_set_usize(widget, 6 * cols, 16 * rows);
4058
4059 /* If the attribute readonly isn't specified we make the textarea
4060 * editable. If readonly is set we don't have to do anything.
4061 */
4062 if (!Html_get_attr(html, tag, tagsize, "readonly"))
4063 gtk_text_set_editable(GTK_TEXT(widget), TRUE);
4064
4065 scroll = gtk_scrolled_window_new(NULL, NULL);
4066 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
4067 GTK_POLICY_AUTOMATIC,
4068 GTK_POLICY_AUTOMATIC);
4069 gtk_container_add(GTK_CONTAINER(scroll), widget);
4070 gtk_widget_show(widget);
4071 gtk_widget_show(scroll);
4072
4073 Html_add_input(form, DILLO_HTML_INPUT_TEXTAREA,
4074 widget, name, NULL, NULL, FALSE);
4075 g_free(name);
4076
4077 embed_gtk = a_Dw_embed_gtk_new ();
4078 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), scroll);
4079 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
4080 html->stack[html->stack_top].style);
4081 }
4082
4083 /*
4084 * <SELECT>
4085 */
4086 /* The select tag is quite tricky, because of gorpy html syntax. */
4087 static void Html_tag_open_select(DilloHtml *html, char *tag, gint tagsize)
4088 {
4089 DilloHtmlForm *form;
4090 DilloHtmlSelect *Select;
4091 DilloHtmlLB *html_lb;
4092 GtkWidget *widget, *menu;
4093 char *name;
4094 const char *attrbuf;
4095 gint size, type, multi;
4096
4097 if (!(html->InFlags & IN_FORM)) {
4098 MSG_HTML("<select> outside <form>\n");
4099 return;
4100 }
4101 if (html->InFlags & IN_SELECT) {
4102 MSG_HTML("nested <select>\n");
4103 return;
4104 }
4105 html->InFlags |= IN_SELECT;
4106
4107 html_lb = html->linkblock;
4108
4109 form = &(html_lb->forms[html_lb->num_forms - 1]);
4110
4111 name = Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
4112
4113 size = 0;
4114 if ((attrbuf = Html_get_attr(html, tag, tagsize, "size")))
4115 size = strtol(attrbuf, NULL, 10);
4116
4117 multi = (Html_get_attr(html, tag, tagsize, "multiple")) ? 1 : 0;
4118 if (size < 1)
4119 size = multi ? 10 : 1;
4120
4121 if (size == 1) {
4122 menu = gtk_menu_new();
4123 widget = gtk_option_menu_new();
4124 type = DILLO_HTML_INPUT_SELECT;
4125 } else {
4126 menu = gtk_list_new();
4127 widget = menu;
4128 if (multi)
4129 gtk_list_set_selection_mode(GTK_LIST(menu), GTK_SELECTION_MULTIPLE);
4130 type = DILLO_HTML_INPUT_SEL_LIST;
4131 }
4132
4133 Select = g_new(DilloHtmlSelect, 1);
4134 Select->menu = menu;
4135 Select->size = size;
4136 Select->num_options = 0;
4137 Select->num_options_max = 8;
4138 Select->options = g_new(DilloHtmlOption, Select->num_options_max);
4139
4140 Html_add_input(form, type, widget, name, NULL, Select, FALSE);
4141 Html_stash_init(html);
4142 g_free(name);
4143 }
4144
4145 /*
4146 * ?
4147 */
4148 static void Html_option_finish(DilloHtml *html)
4149 {
4150 DilloHtmlForm *form;
4151 DilloHtmlInput *input;
4152 GtkWidget *menuitem;
4153 GSList *group;
4154 DilloHtmlSelect *select;
4155
4156 if (!(html->InFlags & IN_FORM))
4157 return;
4158
4159 form = &(html->linkblock->forms[html->linkblock->num_forms - 1]);
4160 input = &(form->inputs[form->num_inputs - 1]);
4161 if ( input->select->num_options <= 0)
4162 return;
4163
4164 select = input->select;
4165 if (input->type == DILLO_HTML_INPUT_SELECT ) {
4166 if ( select->num_options == 1)
4167 group = NULL;
4168 else
4169 group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM
4170 (select->options[0].menuitem));
4171 menuitem = gtk_radio_menu_item_new_with_label(group, html->Stash->str);
4172 select->options[select->num_options - 1].menuitem = menuitem;
4173 if ( select->options[select->num_options - 1].value == NULL )
4174 select->options[select->num_options - 1].value =
4175 g_strdup(html->Stash->str);
4176 gtk_menu_append(GTK_MENU(select->menu), menuitem);
4177 if ( select->options[select->num_options - 1].init_val )
4178 gtk_menu_item_activate(GTK_MENU_ITEM(menuitem));
4179 gtk_widget_show(menuitem);
4180 gtk_signal_connect (GTK_OBJECT (menuitem), "select",
4181 GTK_SIGNAL_FUNC (a_Interface_scroll_popup),
4182 NULL);
4183 } else if ( input->type == DILLO_HTML_INPUT_SEL_LIST ) {
4184 menuitem = gtk_list_item_new_with_label(html->Stash->str);
4185 select->options[select->num_options - 1].menuitem = menuitem;
4186 if (select->options[select->num_options - 1].value == NULL)
4187 select->options[select->num_options - 1].value =
4188 g_strdup(html->Stash->str);
4189 gtk_container_add(GTK_CONTAINER(select->menu), menuitem);
4190 if ( select->options[select->num_options - 1].init_val )
4191 gtk_list_select_child(GTK_LIST(select->menu), menuitem);
4192 gtk_widget_show(menuitem);
4193 }
4194 }
4195
4196 /*
4197 * <OPTION>
4198 */
4199 static void Html_tag_open_option(DilloHtml *html, char *tag, gint tagsize)
4200 {
4201 DilloHtmlForm *form;
4202 DilloHtmlInput *input;
4203 DilloHtmlLB *html_lb;
4204 gint no;
4205
4206 if (!(html->InFlags & IN_SELECT))
4207 return;
4208
4209 html_lb = html->linkblock;
4210
4211 form = &(html_lb->forms[html_lb->num_forms - 1]);
4212 input = &(form->inputs[form->num_inputs - 1]);
4213 if (input->type == DILLO_HTML_INPUT_SELECT ||
4214 input->type == DILLO_HTML_INPUT_SEL_LIST) {
4215 Html_option_finish(html);
4216 no = input->select->num_options;
4217 a_List_add(input->select->options, no, input->select->num_options_max);
4218 input->select->options[no].menuitem = NULL;
4219 input->select->options[no].value = Html_get_attr_wdef(html, tag, tagsize,
4220 "value", NULL);
4221 input->select->options[no].init_val =
4222 (Html_get_attr(html, tag, tagsize, "selected") != NULL);
4223 input->select->num_options++;
4224 }
4225 Html_stash_init(html);
4226 }
4227
4228 /*
4229 * ?
4230 */
4231 static void Html_tag_close_select(DilloHtml *html, gint TagIdx)
4232 {
4233 DilloHtmlForm *form;
4234 DilloHtmlInput *input;
4235 GtkWidget *scrolledwindow;
4236 DilloHtmlLB *html_lb;
4237 DwWidget *embed_gtk;
4238 GtkRequisition req;
4239 gint height;
4240
4241 if (html->InFlags & IN_SELECT) {
4242 html->InFlags &= ~IN_SELECT;
4243
4244 html_lb = html->linkblock;
4245
4246 form = &(html_lb->forms[html_lb->num_forms - 1]);
4247 input = &(form->inputs[form->num_inputs - 1]);
4248 if (input->type == DILLO_HTML_INPUT_SELECT) {
4249 Html_option_finish(html);
4250
4251 gtk_option_menu_set_menu(GTK_OPTION_MENU(input->widget),
4252 input->select->menu);
4253 Html_select_set_history(input);
4254
4255 /* gtk_option_menu_set_history(GTK_OPTION_MENU(input->widget), 1); */
4256
4257 gtk_widget_show(input->widget);
4258
4259 embed_gtk = a_Dw_embed_gtk_new ();
4260 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), input->widget);
4261 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
4262 html->stack[html->stack_top].style);
4263 } else if (input->type == DILLO_HTML_INPUT_SEL_LIST) {
4264 Html_option_finish(html);
4265
4266 if (input->select->size < input->select->num_options) {
4267 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
4268 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
4269 GTK_POLICY_NEVER,
4270 GTK_POLICY_AUTOMATIC);
4271 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW
4272 (scrolledwindow),
4273 input->widget);
4274
4275 gtk_container_set_focus_vadjustment
4276 (GTK_CONTAINER (input->widget),
4277 gtk_scrolled_window_get_vadjustment
4278 (GTK_SCROLLED_WINDOW(scrolledwindow)));
4279
4280 /* Calculate the height of the scrolled window */
4281 gtk_widget_size_request(input->select->options[0].menuitem, &req);
4282 height = input->select->size * req.height +
4283 2 * scrolledwindow->style->klass->ythickness;
4284 gtk_widget_set_usize(scrolledwindow, -1, height);
4285
4286 gtk_widget_show(input->widget);
4287 input->widget = scrolledwindow;
4288 }
4289 gtk_widget_show(input->widget);
4290
4291 /* note: In this next call, scrolledwindows get a g_warning from
4292 * gdkwindow.c:422. I'm not really going to sweat it now - the
4293 * embedded widget stuff is going to get massively redone anyway. */
4294 embed_gtk = a_Dw_embed_gtk_new ();
4295 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), input->widget);
4296 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
4297 html->stack[html->stack_top].style);
4298 }
4299 }
4300 Html_pop_tag(html, TagIdx);
4301 }
4302
4303 /*
4304 * Set the Document Base URI
4305 */
4306 static void Html_tag_open_base(DilloHtml *html, char *tag, gint tagsize)
4307 {
4308 const char *attrbuf;
4309 DilloUrl *BaseUrl;
4310
4311 if (html->InFlags & IN_HEAD) {
4312 if ((attrbuf = Html_get_attr(html, tag, tagsize, "href"))) {
4313 BaseUrl = Html_url_new(html, attrbuf, "", 0, 0, 0, 1);
4314 if (URL_SCHEME_(BaseUrl)) {
4315 /* Pass the URL_SpamSafe flag to the new base url */
4316 a_Url_set_flags(
4317 BaseUrl, URL_FLAGS(html->linkblock->base_url) & URL_SpamSafe);
4318 a_Url_free(html->linkblock->base_url);
4319 html->linkblock->base_url = BaseUrl;
4320 } else {
4321 MSG_HTML("base URI is relative (it MUST be absolute)\n");
4322 a_Url_free(BaseUrl);
4323 }
4324 }
4325 } else {
4326 MSG_HTML("the BASE element must appear in the HEAD section\n");
4327 }
4328 }
4329
4330 /*
4331 * <CODE>
4332 */
4333 static void Html_tag_open_code(DilloHtml *html, char *tag, gint tagsize)
4334 {
4335 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
4336 }
4337
4338 /*
4339 * <DFN>
4340 */
4341 static void Html_tag_open_dfn(DilloHtml *html, char *tag, gint tagsize)
4342 {
4343 Html_set_top_font(html, NULL, 0, 2, 3);
4344 }
4345
4346 /*
4347 * <KBD>
4348 */
4349 static void Html_tag_open_kbd(DilloHtml *html, char *tag, gint tagsize)
4350 {
4351 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
4352 }
4353
4354 /*
4355 * <SAMP>
4356 */
4357 static void Html_tag_open_samp(DilloHtml *html, char *tag, gint tagsize)
4358 {
4359 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
4360 }
4361
4362 /*
4363 * <VAR>
4364 */
4365 static void Html_tag_open_var(DilloHtml *html, char *tag, gint tagsize)
4366 {
4367 Html_set_top_font(html, NULL, 0, 2, 2);
4368 }
4369
4370 /*
4371 * <SUB>
4372 */
4373 static void Html_tag_open_sub(DilloHtml *html, char *tag, gint tagsize)
4374 {
4375 HTML_SET_TOP_ATTR (html, valign, DW_STYLE_VALIGN_SUB);
4376 }
4377
4378 /*
4379 * <SUP>
4380 */
4381 static void Html_tag_open_sup(DilloHtml *html, char *tag, gint tagsize)
4382 {
4383 HTML_SET_TOP_ATTR (html, valign, DW_STYLE_VALIGN_SUPER);
4384 }
4385
4386 /*
4387 * <DIV> (todo: make a complete implementation)
4388 */
4389 static void Html_tag_open_div(DilloHtml *html, char *tag, gint tagsize)
4390 {
4391 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
4392 html->stack[(html)->stack_top].style);
4393 Html_tag_set_align_attr (html, tag, tagsize);
4394 }
4395
4396 /*
4397 * </DIV>, also used for </TABLE> and </CENTER>
4398 */
4399 static void Html_tag_close_div(DilloHtml *html, gint TagIdx)
4400 {
4401 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
4402 html->stack[(html)->stack_top].style);
4403 Html_pop_tag(html, TagIdx);
4404 }
4405
4406 /*
4407 * Default close for most tags - just pop the stack.
4408 */
4409 static void Html_tag_close_default(DilloHtml *html, gint TagIdx)
4410 {
4411 Html_pop_tag(html, TagIdx);
4412 }
4413
4414 /*
4415 * Default close for paragraph tags - pop the stack and break.
4416 */
4417 static void Html_tag_close_par(DilloHtml *html, gint TagIdx)
4418 {
4419 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
4420 html->stack[(html)->stack_top].style);
4421 Html_pop_tag(html, TagIdx);
4422 }
4423
4424
4425 /*
4426 * Function index for the open and close functions for each tag
4427 * (Alphabetically sorted for a binary search)
4428 *
4429 * Explanation for the 'Flags' camp:
4430 *
4431 * {"address", B8(010110), ...}
4432 * |||||`- inline element
4433 * ||||`-- block element
4434 * |||`--- inline container
4435 * ||`---- block container
4436 * |`----- body element
4437 * `------ head element
4438 *
4439 * Notes:
4440 * - The upper two bits are not used yet.
4441 * - Empty elements have both inline and block container clear.
4442 * (flow have both set)
4443 */
4444 struct _TagInfo{
4445 gchar *name;
4446 unsigned char Flags;
4447 gchar EndTag;
4448 guchar TagLevel;
4449 TagOpenFunct open;
4450 TagCloseFunct close;
4451 };
4452
4453
4454 static const TagInfo Tags[] = {
4455 {"a", B8(010101),'R',2, Html_tag_open_a, Html_tag_close_a},
4456 {"abbr", B8(010101),'R',2, Html_tag_open_abbr, Html_tag_close_default},
4457 /* acronym 010101 */
4458 {"address", B8(010110),'R',2, Html_tag_open_address, Html_tag_close_par},
4459 {"area", B8(010001),'F',0, Html_tag_open_area, Html_tag_close_default},
4460 {"b", B8(010101),'R',2, Html_tag_open_b, Html_tag_close_default},
4461 {"base", B8(100001),'F',0, Html_tag_open_base, Html_tag_close_default},
4462 /* basefont 010001 */
4463 /* bdo 010101 */
4464 {"big", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
4465 {"blockquote", B8(011110),'R',2,Html_tag_open_blockquote,Html_tag_close_par},
4466 {"body", B8(011110),'O',1, Html_tag_open_body, Html_tag_close_body},
4467 {"br", B8(010001),'F',0, Html_tag_open_br, Html_tag_close_default},
4468 {"button", B8(011101),'R',2, Html_tag_open_button, Html_tag_close_default},
4469 /* caption */
4470 {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_div},
4471 {"cite", B8(010101),'R',2, Html_tag_open_cite, Html_tag_close_default},
4472 {"code", B8(010101),'R',2, Html_tag_open_code, Html_tag_close_default},
4473 /* col 010010 'F' */
4474 /* colgroup */
4475 {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_par},
4476 {"del", B8(011101),'R',2, Html_tag_open_strike, Html_tag_close_default},
4477 {"dfn", B8(010101),'R',2, Html_tag_open_dfn, Html_tag_close_default},
4478 /* dir 011010 */
4479 /* todo: complete <div> support! */
4480 {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_div},
4481 {"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par},
4482 {"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par},
4483 {"em", B8(010101),'R',2, Html_tag_open_em, Html_tag_close_default},
4484 /* fieldset */
4485 {"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default},
4486 {"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form},
4487 {"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default},
4488 {"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default},
4489 {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4490 {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4491 {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4492 {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4493 {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4494 {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4495 {"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head},
4496 {"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default},
4497 {"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html},
4498 {"i", B8(010101),'R',2, Html_tag_open_i, Html_tag_close_default},
4499 {"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default},
4500 {"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default},
4501 {"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default},
4502 /* ins */
4503 {"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default},
4504 {"kbd", B8(010101),'R',2, Html_tag_open_kbd, Html_tag_close_default},
4505 /* label 010101 */
4506 /* legend 01?? */
4507 {"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_default},
4508 /* link 100000 'F' */
4509 {"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map},
4510 /* menu 1010 -- todo: not exactly 1010, it can contain LI and inline */
4511 {"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par},
4512 {"meta", B8(100001),'F',0, Html_tag_open_meta, Html_tag_close_default},
4513 /* noframes 1011 */
4514 /* noscript 1011 */
4515 /* object 11xxxx */
4516 {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_par},
4517 /* optgroup */
4518 {"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default},
4519 {"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
4520 /* param 010001 'F' */
4521 {"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
4522 /* q 010101 */
4523 {"s", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
4524 {"samp", B8(010101),'R',2, Html_tag_open_samp, Html_tag_close_default},
4525 {"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
4526 {"select", B8(011001),'R',2, Html_tag_open_select, Html_tag_close_select},
4527 {"small", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
4528 /* span 0101 */
4529 {"strike", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
4530 {"strong", B8(010101),'R',2, Html_tag_open_strong, Html_tag_close_default},
4531 {"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style},
4532 {"sub", B8(010101),'R',2, Html_tag_open_sub, Html_tag_close_default},
4533 {"sup", B8(010101),'R',2, Html_tag_open_sup, Html_tag_close_default},
4534 {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_div},
4535 /* tbody */
4536 {"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default},
4537 {"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea},
4538 /* tfoot */
4539 {"th", B8(011110),'O',1, Html_tag_open_th, Html_tag_close_default},
4540 /* thead */
4541 {"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title},
4542 {"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default},
4543 {"tt", B8(010101),'R',2, Html_tag_open_tt, Html_tag_close_default},
4544 {"u", B8(010101),'R',2, Html_tag_open_u, Html_tag_close_default},
4545 {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_par},
4546 {"var", B8(010101),'R',2, Html_tag_open_var, Html_tag_close_default}
4547
4548 };
4549 #define NTAGS (sizeof(Tags)/sizeof(Tags[0]))
4550
4551
4552 /*
4553 * Compares tag from buffer ('/' or '>' or space-ended string) [p1]
4554 * with tag from taglist (lowercase, zero ended string) [p2]
4555 * Return value: as strcmp()
4556 */
4557 static gint Html_tag_compare(char *p1, char *p2)
4558 {
4559 while ( *p2 ) {
4560 if ( tolower(*p1) != *p2 )
4561 return(tolower(*p1) - *p2);
4562 ++p1;
4563 ++p2;
4564 }
4565 return !strchr(" >/\n\r\t", *p1);
4566 }
4567
4568 /*
4569 * Get 'tag' index
4570 * return -1 if tag is not handled yet
4571 */
4572 static gint Html_tag_index(char *tag)
4573 {
4574 gint low, high, mid, cond;
4575
4576 /* Binary search */
4577 low = 0;
4578 high = NTAGS - 1; /* Last tag index */
4579 while (low <= high) {
4580 mid = (low + high) / 2;
4581 if ((cond = Html_tag_compare(tag, Tags[mid].name)) < 0 )
4582 high = mid - 1;
4583 else if (cond > 0)
4584 low = mid + 1;
4585 else
4586 return mid;
4587 }
4588 return -1;
4589 }
4590
4591 /*
4592 * For elements with optional close, check whether is time to close.
4593 * Return value: (1: Close, 0: Don't close)
4594 * --tuned for speed.
4595 */
4596 static gint Html_needs_optional_close(gint old_idx, gint cur_idx)
4597 {
4598 static gint i_P = -1, i_LI, i_TD, i_TR, i_TH, i_DD, i_DT, i_OPTION;
4599 /* i_THEAD, i_TFOOT, i_COLGROUP; */
4600
4601 if (i_P == -1) {
4602 /* initialize the indexes of elements with optional close */
4603 i_P = Html_tag_index("p"),
4604 i_LI = Html_tag_index("li"),
4605 i_TD = Html_tag_index("td"),
4606 i_TR = Html_tag_index("tr"),
4607 i_TH = Html_tag_index("th"),
4608 i_DD = Html_tag_index("dd"),
4609 i_DT = Html_tag_index("dt"),
4610 i_OPTION = Html_tag_index("option");
4611 /* i_THEAD = Html_tag_index("thead"); */
4612 /* i_TFOOT = Html_tag_index("tfoot"); */
4613 /* i_COLGROUP = Html_tag_index("colgroup"); */
4614 }
4615
4616 if (old_idx == i_P || old_idx == i_DT) {
4617 /* P and DT are closed by block elements */
4618 return (Tags[cur_idx].Flags & 2);
4619 } else if (old_idx == i_LI) {
4620 /* LI closes LI */
4621 return (cur_idx == i_LI);
4622 } else if (old_idx == i_TD || old_idx == i_TH) {
4623 /* TD and TH are closed by TD, TH and TR */
4624 return (cur_idx == i_TD || cur_idx == i_TH || cur_idx == i_TR);
4625 } else if (old_idx == i_TR) {
4626 /* TR closes TR */
4627 return (cur_idx == i_TR);
4628 } else if (old_idx == i_DD) {
4629 /* DD is closed by DD and DT */
4630 return (cur_idx == i_DD || cur_idx == i_DT);
4631 } else if (old_idx == i_OPTION) {
4632 return 1; /* OPTION always needs close */
4633 }
4634
4635 /* HTML, HEAD, BODY are handled by Html_test_section(), not here. */
4636 /* todo: TBODY is pending */
4637 return 0;
4638 }
4639
4640
4641 /*
4642 * Conditional cleanup of the stack (at open time).
4643 * - This helps catching block elements inside inline containers (a BUG).
4644 * - It also closes elements with "optional" close tag.
4645 *
4646 * This function is called when opening a block element or <OPTION>.
4647 *
4648 * It searches the stack closing open inline containers, and closing
4649 * elements with optional close tag when necessary.
4650 *
4651 * Note: OPTION is the only non-block element with an optional close.
4652 */
4653 static void Html_stack_cleanup_at_open(DilloHtml *html, gint new_idx)
4654 {
4655 /* We know that the element we're about to push is a block element.
4656 * (except for OPTION, which is an empty inline, so is closed anyway)
4657 * Notes:
4658 * Its 'tag' is not yet pushed into the stack,
4659 * 'new_idx' is its index inside Tags[].
4660 */
4661
4662 if (!html->TagSoup)
4663 return;
4664
4665 while (html->stack_top) {
4666 gint oldtag_idx = html->stack[html->stack_top].tag_idx;
4667
4668 if (Tags[oldtag_idx].EndTag == 'O') { /* Element with optional close */
4669 if (!Html_needs_optional_close(oldtag_idx, new_idx))
4670 break;
4671 } else if (Tags[oldtag_idx].Flags & 8) { /* Block container */
4672 break;
4673 }
4674
4675 /* we have an inline (or empty) container... */
4676 if (Tags[oldtag_idx].EndTag == 'R') {
4677 MSG_HTML("<%s> is not allowed to contain <%s>. -- closing <%s>\n",
4678 Tags[oldtag_idx].name, Tags[new_idx].name,
4679 Tags[oldtag_idx].name);
4680 }
4681
4682 /* Workaround for Apache and its bad HTML directory listings... */
4683 if ((html->InFlags & IN_PRE) &&
4684 strcmp(Tags[new_idx].name, "hr") == 0)
4685 break;
4686
4687 /* This call closes the top tag only. */
4688 Html_tag_cleanup_at_close(html, oldtag_idx);
4689 }
4690 }
4691
4692 /*
4693 * HTML, HEAD and BODY elements have optional open and close tags.
4694 * Handle this "magic" here.
4695 */
4696 static void Html_test_section(DilloHtml *html, gint new_idx, gint IsCloseTag)
4697 {
4698 gchar *tag;
4699 gint tag_idx;
4700
4701 if (!(html->InFlags & IN_HTML) && html->DocType == DT_NONE)
4702 MSG_HTML("the required DOCTYPE declaration is missing (or invalid)\n");
4703
4704 if (!(html->InFlags & IN_HTML)) {
4705 tag = "<html>";
4706 tag_idx = Html_tag_index(tag + 1);
4707 if (tag_idx != new_idx || IsCloseTag) {
4708 /* implicit open */
4709 Html_force_push_tag(html, tag_idx);
4710 Tags[tag_idx].open (html, tag, strlen(tag));
4711 }
4712 }
4713
4714 if (Tags[new_idx].Flags & 32) {
4715 /* head element */
4716 if (!(html->InFlags & IN_HEAD)) {
4717 tag = "<head>";
4718 tag_idx = Html_tag_index(tag + 1);
4719 if (tag_idx != new_idx || IsCloseTag) {
4720 /* implicit open of the head element */
4721 Html_force_push_tag(html, tag_idx);
4722 Tags[tag_idx].open (html, tag, strlen(tag));
4723 }
4724 }
4725
4726 } else if (Tags[new_idx].Flags & 16) {
4727 /* body element */
4728 if (html->InFlags & IN_HEAD) {
4729 tag = "</head>";
4730 tag_idx = Html_tag_index(tag + 2);
4731 Tags[tag_idx].close (html, tag_idx);
4732 }
4733 tag = "<body>";
4734 tag_idx = Html_tag_index(tag + 1);
4735 if (tag_idx != new_idx || IsCloseTag) {
4736 /* implicit open */
4737 Html_force_push_tag(html, tag_idx);
4738 Tags[tag_idx].open (html, tag, strlen(tag));
4739 }
4740 }
4741 }
4742
4743 /*
4744 * Process a tag, given as 'tag' and 'tagsize'. -- tagsize is [1 based]
4745 * ('tag' must include the enclosing angle brackets)
4746 * This function calls the right open or close function for the tag.
4747 */
4748 static void Html_process_tag(DilloHtml *html, char *tag, gint tagsize)
4749 {
4750 gint ci, ni; /* current and new tag indexes */
4751 const char *attrbuf;
4752 char *start = tag + 1; /* discard the '<' */
4753 gint IsCloseTag = (*start == '/');
4754
4755 ni = Html_tag_index(start + IsCloseTag);
4756
4757 /* todo: doctype parsing is a bit fuzzy, but enough for the time being */
4758 if (ni == -1 && !(html->InFlags & IN_HTML)) {
4759 if (tagsize > 9 && !g_strncasecmp(tag, "<!doctype", 9))
4760 Html_parse_doctype(html, tag, tagsize);
4761 }
4762
4763 if (!(html->InFlags & IN_HTML)) {
4764 _MSG("\nDoctype: %f\n\n", html->DocTypeVersion);
4765 }
4766
4767 /* Handle HTML, HEAD and BODY. Elements with optional open and close */
4768 if (ni != -1 && !(html->InFlags & IN_BODY) /* && parsing HTML */)
4769 Html_test_section(html, ni, IsCloseTag);
4770
4771 /* White space handling */
4772 if (html->SPCPending && (!SGML_SPCDEL || !IsCloseTag))
4773 /* SGML_SPCDEL requires space pending and open tag */
4774 a_Dw_page_add_space(DW_PAGE (html->dw),
4775 html->stack[html->stack_top].style);
4776 html->SPCPending = FALSE;
4777
4778 /* Tag processing */
4779 ci = html->stack[html->stack_top].tag_idx;
4780 if (ni != -1) {
4781
4782 if (!IsCloseTag) {
4783 /* Open function */
4784
4785 /* Cleanup when opening a block element, or
4786 * when openning over an element with optional close */
4787 if (Tags[ni].Flags & 2 || (ci != -1 && Tags[ci].EndTag == 'O'))
4788 Html_stack_cleanup_at_open(html, ni);
4789
4790 /* todo: this is only raising a warning, take some defined action.
4791 * Note: apache uses IMG inside PRE (we could use its "alt"). */
4792 if ((html->InFlags & IN_PRE) && Html_tag_pre_excludes(ni))
4793 MSG_HTML("<pre> is not allowed to contain <%s>\n", Tags[ni].name);
4794
4795 /* Push the tag into the stack */
4796 Html_push_tag(html, ni);
4797
4798 /* Call the open function for this tag */
4799 Tags[ni].open (html, tag, tagsize);
4800
4801 /* Now parse attributes that can appear on any tag */
4802 if (tagsize >= 8 && /* length of "<t id=i>" */
4803 (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
4804 HTML_LeftTrim | HTML_RightTrim))) {
4805 /* According to the SGML declaration of HTML 4, all NAME values
4806 * occuring outside entities must be converted to uppercase
4807 * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
4808 * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
4809 * So we don't do it and hope for better specs in the future ...
4810 */
4811 Html_check_name_val(html, attrbuf, "id");
4812 /* We compare the "id" value with the url-decoded "name" value */
4813 if (!html->NameVal || strcmp(html->NameVal, attrbuf)) {
4814 if (html->NameVal)
4815 MSG_HTML("'id' and 'name' attribute of <a> tag differ\n");
4816 Html_add_anchor(html, attrbuf);
4817 }
4818 }
4819
4820 /* Reset NameVal */
4821 if (html->NameVal) {
4822 g_free(html->NameVal);
4823 html->NameVal = NULL;
4824 }
4825
4826 /* let the parser know this was an open tag */
4827 html->PrevWasOpenTag = TRUE;
4828
4829 /* Request inmediate close for elements with forbidden close tag. */
4830 /* todo: XHTML always requires close tags. A simple implementation
4831 * of the commented clause below will make it work. */
4832 if (/* parsing HTML && */ Tags[ni].EndTag == 'F')
4833 html->ReqTagClose = TRUE;
4834 }
4835
4836 /* Close function: test for </x>, ReqTagClose, <x /> and <x/> */
4837 if (*start == '/' || /* </x> */
4838 html->ReqTagClose || /* request */
4839 (tag[tagsize - 2] == '/' && /* XML: */
4840 (isspace(tag[tagsize - 3]) || /* <x /> */
4841 (size_t)tagsize == strlen(Tags[ni].name) + 3))) { /* <x/> */
4842
4843 Tags[ni].close (html, ni);
4844 /* This was a close tag */
4845 html->PrevWasOpenTag = FALSE;
4846 html->ReqTagClose = FALSE;
4847 }
4848
4849 } else {
4850 /* tag not working - just ignore it */
4851 }
4852 }
4853
4854 /*
4855 * Get attribute value for 'attrname' and return it.
4856 * Tags start with '<' and end with a '>' (Ex: "<P align=center>")
4857 * tagsize = strlen(tag) from '<' to '>', inclusive.
4858 *
4859 * Returns one of the following:
4860 * * The value of the attribute.
4861 * * An empty string if the attribute exists but has no value.
4862 * * NULL if the attribute doesn't exist.
4863 */
4864 static const char *Html_get_attr2(DilloHtml *html,
4865 const char *tag,
4866 gint tagsize,
4867 const char *attrname,
4868 DilloHtmlTagParsingFlags flags)
4869 {
4870 gint i, isocode, entsize, Found = 0, delimiter = 0, attr_pos = 0;
4871 GString *Buf = html->attr_data;
4872 DilloHtmlTagParsingState state = SEEK_ATTR_START;
4873
4874 g_return_val_if_fail(*attrname, NULL);
4875
4876 g_string_truncate(Buf, 0);
4877
4878 for (i = 1; i < tagsize; ++i) {
4879 switch (state) {
4880 case SEEK_ATTR_START:
4881 if (isspace(tag[i]))
4882 state = SEEK_TOKEN_START;
4883 else if (tag[i] == '=')
4884 state = SEEK_VALUE_START;
4885 break;
4886
4887 case MATCH_ATTR_NAME:
4888 if ((Found = (!(attrname[attr_pos]) &&
4889 (tag[i] == '=' || isspace(tag[i]) || tag[i] == '>')))) {
4890 state = SEEK_TOKEN_START;
4891 --i;
4892 } else if (tolower(tag[i]) != tolower(attrname[attr_pos++]))
4893 state = SEEK_ATTR_START;
4894 break;
4895
4896 case SEEK_TOKEN_START:
4897 if (tag[i] == '=') {
4898 state = SEEK_VALUE_START;
4899 } else if (!isspace(tag[i])) {
4900 attr_pos = 0;
4901 state = (Found) ? FINISHED : MATCH_ATTR_NAME;
4902 --i;
4903 }
4904 break;
4905 case SEEK_VALUE_START:
4906 if (!isspace(tag[i])) {
4907 delimiter = (tag[i] == '"' || tag[i] == '\'') ? tag[i] : ' ';
4908 i -= (delimiter == ' ');
4909 state = (Found) ? GET_VALUE : SKIP_VALUE;
4910 }
4911 break;
4912
4913 case SKIP_VALUE:
4914 if ((delimiter == ' ' && isspace(tag[i])) || tag[i] == delimiter)
4915 state = SEEK_TOKEN_START;
4916 break;
4917 case GET_VALUE:
4918 if ((delimiter == ' ' && (isspace(tag[i]) || tag[i] == '>')) ||
4919 tag[i] == delimiter) {
4920 state = FINISHED;
4921 } else if (tag[i] == '&' && (flags & HTML_ParseEntities)) {
4922 if ((isocode = Html_parse_entity(html, tag+i,
4923 tagsize-i, &entsize)) >= 0) {
4924 g_string_append_c(Buf, (gchar) isocode);
4925 i += entsize-1;
4926 } else {
4927 g_string_append_c(Buf, tag[i]);
4928 }
4929 } else if (tag[i] == '\r' || tag[i] == '\t') {
4930 g_string_append_c(Buf, ' ');
4931 } else if (tag[i] == '\n') {
4932 /* ignore */
4933 } else {
4934 g_string_append_c(Buf, tag[i]);
4935 }
4936 break;
4937
4938 case FINISHED:
4939 i = tagsize;
4940 break;
4941 }
4942 }
4943
4944 if (flags & HTML_LeftTrim)
4945 while (isspace(Buf->str[0]))
4946 g_string_erase(Buf, 0, 1);
4947 if (flags & HTML_RightTrim)
4948 while (Buf->len && isspace(Buf->str[Buf->len - 1]))
4949 g_string_truncate(Buf, Buf->len - 1);
4950
4951 return (Found) ? Buf->str : NULL;
4952 }
4953
4954 /*
4955 * Call Html_get_attr2 telling it to parse entities and strip the result
4956 */
4957 static const char *Html_get_attr(DilloHtml *html,
4958 const char *tag,
4959 gint tagsize,
4960 const char *attrname)
4961 {
4962 return Html_get_attr2(html, tag, tagsize, attrname,
4963 HTML_LeftTrim | HTML_RightTrim | HTML_ParseEntities);
4964 }
4965
4966 /*
4967 * "Html_get_attr with default"
4968 * Call Html_get_attr() and strdup() the returned string.
4969 * If the attribute isn't found a copy of 'def' is returned.
4970 */
4971 static char *Html_get_attr_wdef(DilloHtml *html,
4972 const char *tag,
4973 gint tagsize,
4974 const char *attrname,
4975 const char *def)
4976 {
4977 const char *attrbuf = Html_get_attr(html, tag, tagsize, attrname);
4978
4979 return attrbuf ? g_strdup(attrbuf) : g_strdup(def);
4980 }
4981
4982 /*
4983 * Add a widget to the page.
4984 */
4985 static void Html_add_widget(DilloHtml *html,
4986 DwWidget *widget,
4987 char *width_str,
4988 char *height_str,
4989 DwStyle *style_attrs)
4990 {
4991 DwStyle new_style_attrs, *style;
4992
4993 new_style_attrs = *style_attrs;
4994 new_style_attrs.width = width_str ?
4995 Html_parse_length (html, width_str) : DW_STYLE_LENGTH_AUTO;
4996 new_style_attrs.height = height_str ?
4997 Html_parse_length (html, height_str) : DW_STYLE_LENGTH_AUTO;
4998 style = a_Dw_style_new (&new_style_attrs, (html)->bw->main_window->window);
4999 a_Dw_page_add_widget(DW_PAGE (html->dw), widget, style);
5000 a_Dw_style_unref (style);
5001 }
5002
5003
5004 /*
5005 * Dispatch the apropriate function for 'Op'
5006 * This function is a Cache client and gets called whenever new data arrives
5007 * Op : operation to perform.
5008 * CbData : a pointer to a DilloHtml structure
5009 * Buf : a pointer to new data
5010 * BufSize : new data size (in bytes)
5011 */
5012 static void Html_callback(int Op, CacheClient_t *Client)
5013 {
5014 if ( Op ) {
5015 Html_write(Client->CbData, Client->Buf, Client->BufSize, 1);
5016 Html_close(Client->CbData, Client->Key);
5017 } else
5018 Html_write(Client->CbData, Client->Buf, Client->BufSize, 0);
5019 }
5020
5021 /*
5022 * Here's where we parse the html and put it into the page structure.
5023 * Return value: number of bytes parsed
5024 */
5025 static gint Html_write_raw(DilloHtml *html, char *buf, gint bufsize, gint Eof)
5026 {
5027 char ch = 0, *p, *text;
5028 DwPage *page;
5029 gint token_start, buf_index;
5030
5031 g_return_val_if_fail ((page = DW_PAGE (html->dw)) != NULL, 0);
5032
5033 buf = g_strndup(buf, bufsize);
5034
5035 /* Now, 'buf' and 'bufsize' define a buffer aligned to start at a token
5036 * boundary. Iterate through tokens until end of buffer is reached. */
5037 buf_index = 0;
5038 token_start = buf_index;
5039 while (buf_index < bufsize) {
5040 /* invariant: buf_index == bufsize || token_start == buf_index */
5041
5042 if (html->stack[html->stack_top].parse_mode ==
5043 DILLO_HTML_PARSE_MODE_VERBATIM) {
5044 /* Non HTML code here, let's skip until closing tag */
5045 do {
5046 char *tag = html->stack[html->stack_top].tag_name;
5047 buf_index += strcspn(buf + buf_index, "<");
5048 if (buf_index + (gint)strlen(tag) + 3 > bufsize) {
5049 buf_index = bufsize;
5050 } else if (strncmp(buf + buf_index, "</", 2) == 0 &&
5051 Html_match_tag(tag, buf+buf_index+2, strlen(tag)+1)) {
5052 /* copy VERBATIM text into the stash buffer */
5053 text = g_strndup(buf + token_start, buf_index - token_start);
5054 g_string_append(html->Stash, text);
5055 g_free(text);
5056 token_start = buf_index;
5057 break;
5058 } else
5059 ++buf_index;
5060 } while (buf_index < bufsize);
5061 }
5062
5063 if ( isspace(buf[buf_index]) ) {
5064 /* whitespace: group all available whitespace */
5065 while (++buf_index < bufsize && isspace(buf[buf_index]));
5066 Html_process_space(html, buf + token_start, buf_index - token_start);
5067 token_start = buf_index;
5068
5069 } else if (buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
5070 (isalpha(ch) || strchr("/!?", ch)) ) {
5071 /* Tag */
5072 if (buf_index + 3 < bufsize && !strncmp(buf + buf_index, "<!--", 4)) {
5073 /* Comment: search for close of comment, skipping over
5074 * everything except a matching "-->" tag. */
5075 while ( (p = memchr(buf + buf_index, '>', bufsize - buf_index)) ){
5076 buf_index = p - buf + 1;
5077 if ( p[-1] == '-' && p[-2] == '-' ) break;
5078 }
5079 if ( p ) {
5080 /* Got the whole comment. Let's throw it away! :) */
5081 token_start = buf_index;
5082 } else
5083 buf_index = bufsize;
5084 } else {
5085 /* Tag: search end of tag (skipping over quoted strings) */
5086 html->CurrTagOfs = html->Start_Ofs + token_start;
5087
5088 while ( buf_index < bufsize ) {
5089 buf_index++;
5090 buf_index += strcspn(buf + buf_index, ">\"'<");
5091 if ( (ch = buf[buf_index]) == '>' ) {
5092 break;
5093 } else if ( ch == '"' || ch == '\'' ) {
5094 /* Skip over quoted string */
5095 buf_index++;
5096 buf_index += strcspn(buf + buf_index,
5097 (ch == '"') ? "\">" : "'>");
5098 if ( buf[buf_index] == '>' ) {
5099 /* Unterminated string value? Let's look ahead and test:
5100 * (<: unterminated, closing-quote: terminated) */
5101 gint offset = buf_index + 1;
5102 offset += strcspn(buf + offset,
5103 (ch == '"') ? "\"<" : "'<");
5104 if (buf[offset] == ch || !buf[offset]) {
5105 buf_index = offset;
5106 } else {
5107 MSG_HTML("attribute lacks closing quote\n");
5108 break;
5109 }
5110 }
5111 } else if ( ch == '<' ) {
5112 /* unterminated tag detected */
5113 p = g_strndup(buf+token_start+1,
5114 strcspn(buf+token_start+1, " <"));
5115 MSG_HTML("<%s> element lacks its closing '>'\n", p);
5116 g_free(p);
5117 --buf_index;
5118 break;
5119 }
5120 }
5121 if (buf_index < bufsize) {
5122 buf_index++;
5123 Html_process_tag(html, buf + token_start,
5124 buf_index - token_start);
5125 token_start = buf_index;
5126 }
5127 }
5128 } else {
5129 /* A Word: search for whitespace or tag open */
5130 while (++buf_index < bufsize) {
5131 buf_index += strcspn(buf + buf_index, " <\n\r\t\f\v");
5132 if ( buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
5133 !isalpha(ch) && !strchr("/!?", ch))
5134 continue;
5135 break;
5136 }
5137 if (buf_index < bufsize || Eof) {
5138 /* successfully found end of token */
5139 Html_process_word(html, buf + token_start,
5140 buf_index - token_start);
5141 token_start = buf_index;
5142 }
5143 }
5144 }/*while*/
5145
5146 a_Dw_page_flush(page);
5147 g_free(buf);
5148
5149 return token_start;
5150 }
5151
5152 /*
5153 * Process the newly arrived html and put it into the page structure.
5154 * (This function is called by Html_callback whenever there's new data)
5155 */
5156 static void Html_write(DilloHtml *html, char *Buf, gint BufSize, gint Eof)
5157 {
5158 DwPage *page;
5159 char completestr[32];
5160 gint token_start;
5161 char *buf = Buf + html->Start_Ofs;
5162 gint bufsize = BufSize - html->Start_Ofs;
5163
5164 g_return_if_fail ( (page = DW_PAGE (html->dw)) != NULL );
5165
5166 html->Start_Buf = Buf;
5167 token_start = Html_write_raw(html, buf, bufsize, Eof);
5168 html->Start_Ofs += token_start;
5169
5170 if ( html->bw ) {
5171 g_snprintf(
5172 completestr, 32, "%s%.1f Kb",
5173 PBAR_PSTR(prefs.panel_size == 1),
5174 (float)html->Start_Ofs/1024);
5175 a_Progressbar_update(html->bw->progress, completestr, 1);
5176 }
5177 }
5178
5179 /*
5180 * Finish parsing a HTML page
5181 * (Free html struct, close the client and update progressbar).
5182 */
5183 static void Html_close(DilloHtml *html, gint ClientKey)
5184 {
5185 gint si;
5186
5187 /*
5188 #if defined (DEBUG_LEVEL) && DEBUG_LEVEL >= 1
5189 a_Dw_widget_print_tree (GTK_DW_VIEWPORT(html->dw->viewport)->child);
5190 #endif
5191 */
5192
5193 /* force the close of elements left open (todo: not for XHTML) */
5194 while ((si = html->stack_top)) {
5195 if (html->stack[si].tag_idx != -1) {
5196 Html_tag_cleanup_at_close(html, html->stack[si].tag_idx);
5197 }
5198 }
5199 g_free(html->stack[0].tag_name); /* "none" */
5200 a_Dw_style_unref(html->stack[0].style); /* template style */
5201
5202 g_free(html->stack);
5203
5204 g_string_free(html->Stash, TRUE);
5205 g_free(html->SPCBuf);
5206 g_string_free(html->attr_data, TRUE);
5207
5208 /* Remove this client from our active list */
5209 a_Interface_close_client(html->bw, ClientKey);
5210
5211 /* Set progress bar insensitive */
5212 a_Progressbar_update(html->bw->progress, NULL, 0);
5213
5214 g_free(html);
5215 }
5216
5217
0 #ifndef __HTML_H__
1 #define __HTML_H__
2
3 #include <gdk/gdk.h>
4 #include <gtk/gtkcontainer.h>
5
6 #include "browser.h" /* for BrowserWindow */
7 #include "dw_widget.h" /* for DwWidget */
8 #include "dw_image.h" /* for DwImageMapList */
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif /* __cplusplus */
13
14 /* First, the html linkblock. For now, this mostly has forms, although
15 pointers to actual links will go here soon, if for no other reason
16 than to implement history-sensitive link colors. Also, it seems
17 likely that imagemaps will go here. */
18
19 typedef struct _DilloHtmlLB DilloHtmlLB;
20
21 typedef struct _DilloHtml DilloHtml;
22 typedef struct _DilloHtmlClass DilloHtmlClass;
23 typedef struct _DilloHtmlState DilloHtmlState;
24 typedef struct _DilloHtmlForm DilloHtmlForm;
25 typedef struct _DilloHtmlOption DilloHtmlOption;
26 typedef struct _DilloHtmlSelect DilloHtmlSelect;
27 typedef struct _DilloHtmlInput DilloHtmlInput;
28
29
30 struct _DilloHtmlLB {
31 BrowserWindow *bw;
32 DilloUrl *base_url;
33
34 DilloHtmlForm *forms;
35 gint num_forms;
36 gint num_forms_max;
37
38 DilloUrl **links;
39 gint num_links;
40 gint num_links_max;
41
42 DwImageMapList maps;
43
44 gint32 link_color;
45 gint32 visited_color;
46
47 gint num_page_bugs;
48 GString *page_bugs;
49 };
50
51
52 typedef enum {
53 DT_NONE,
54 DT_HTML,
55 DT_XHTML
56 } DilloHtmlDocumentType;
57
58 typedef enum {
59 DILLO_HTML_PARSE_MODE_INIT,
60 DILLO_HTML_PARSE_MODE_STASH,
61 DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
62 DILLO_HTML_PARSE_MODE_VERBATIM,
63 DILLO_HTML_PARSE_MODE_BODY,
64 DILLO_HTML_PARSE_MODE_PRE
65 } DilloHtmlParseMode;
66
67 typedef enum {
68 SEEK_ATTR_START,
69 MATCH_ATTR_NAME,
70 SEEK_TOKEN_START,
71 SEEK_VALUE_START,
72 SKIP_VALUE,
73 GET_VALUE,
74 FINISHED
75 } DilloHtmlTagParsingState;
76
77 typedef enum {
78 HTML_LeftTrim = 1 << 0,
79 HTML_RightTrim = 1 << 1,
80 HTML_ParseEntities = 1 << 2
81 } DilloHtmlTagParsingFlags;
82
83 typedef enum {
84 DILLO_HTML_TABLE_MODE_NONE, /* no table at all */
85 DILLO_HTML_TABLE_MODE_TOP, /* outside of <tr> */
86 DILLO_HTML_TABLE_MODE_TR, /* inside of <tr>, outside of <td> */
87 DILLO_HTML_TABLE_MODE_TD /* inside of <td> */
88 } DilloHtmlTableMode;
89
90
91 typedef enum {
92 IN_HTML = 1 << 0,
93 IN_HEAD = 1 << 1,
94 IN_BODY = 1 << 2,
95 IN_FORM = 1 << 3,
96 IN_SELECT = 1 << 4,
97 IN_TEXTAREA = 1 << 5,
98 IN_MAP = 1 << 6,
99 IN_PRE = 1 << 7,
100 IN_BUTTON = 1 << 8
101 } DilloHtmlProcessingState;
102
103
104 struct _DilloHtmlState {
105 char *tag_name;
106 DwStyle *style, *table_cell_style;
107 DilloHtmlParseMode parse_mode;
108 DilloHtmlTableMode table_mode;
109 gboolean cell_text_align_set;
110 enum { HTML_LIST_NONE, HTML_LIST_UNORDERED, HTML_LIST_ORDERED } list_type;
111 gint list_number;
112
113 /* TagInfo index for the tag that's being processed */
114 gint tag_idx;
115
116 DwWidget *page, *table;
117
118 /* This is used to align list items (especially in enumerated lists) */
119 DwWidget *ref_list_item;
120
121 /* This makes image processing faster than a function
122 a_Dw_widget_get_background_color. */
123 gint32 current_bg_color;
124
125 /* This is used for list items etc; if it is set to TRUE, breaks
126 have to be "handed over" (see Html_add_indented and
127 Html_eventually_pop_dw). */
128 gboolean hand_over_break;
129 };
130
131 typedef enum {
132 DILLO_HTML_METHOD_UNKNOWN,
133 DILLO_HTML_METHOD_GET,
134 DILLO_HTML_METHOD_POST
135 } DilloHtmlMethod;
136
137 typedef enum {
138 DILLO_HTML_ENC_URLENCODING
139 } DilloHtmlEnc;
140
141 struct _DilloHtmlForm {
142 DilloHtmlMethod method;
143 DilloUrl *action;
144 DilloHtmlEnc enc;
145
146 DilloHtmlInput *inputs;
147 gint num_inputs;
148 gint num_inputs_max;
149 gint num_entry_fields;
150 gint num_submit_buttons;
151 };
152
153 struct _DilloHtmlOption {
154 GtkWidget *menuitem;
155 char *value;
156 gboolean init_val;
157 };
158
159 struct _DilloHtmlSelect {
160 GtkWidget *menu;
161 gint size;
162
163 DilloHtmlOption *options;
164 gint num_options;
165 gint num_options_max;
166 };
167
168 typedef enum {
169 DILLO_HTML_INPUT_TEXT,
170 DILLO_HTML_INPUT_PASSWORD,
171 DILLO_HTML_INPUT_CHECKBOX,
172 DILLO_HTML_INPUT_RADIO,
173 DILLO_HTML_INPUT_IMAGE,
174 DILLO_HTML_INPUT_FILE,
175 DILLO_HTML_INPUT_BUTTON,
176 DILLO_HTML_INPUT_HIDDEN,
177 DILLO_HTML_INPUT_SUBMIT,
178 DILLO_HTML_INPUT_RESET,
179 DILLO_HTML_INPUT_BUTTON_SUBMIT,
180 DILLO_HTML_INPUT_BUTTON_RESET,
181 DILLO_HTML_INPUT_SELECT,
182 DILLO_HTML_INPUT_SEL_LIST,
183 DILLO_HTML_INPUT_TEXTAREA,
184 DILLO_HTML_INPUT_INDEX
185 } DilloHtmlInputType;
186
187 struct _DilloHtmlInput {
188 DilloHtmlInputType type;
189 void *widget; /* May be a GtkWidget or a DwWidget. */
190 char *name;
191 char *init_str; /* note: some overloading - for buttons, init_str
192 is simply the value of the button; for text
193 entries, it is the initial value */
194 DilloHtmlSelect *select;
195 gboolean init_val; /* only meaningful for buttons */
196 };
197
198 struct _DilloHtml {
199 DwWidget *dw; /* this is duplicated in the stack (page) */
200
201 DilloHtmlLB *linkblock;
202 gchar *Start_Buf;
203 size_t Start_Ofs;
204 size_t CurrTagOfs;
205 size_t OldTagOfs, OldTagLine;
206
207 DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
208 float DocTypeVersion; /* HTML or XHTML version number */
209
210 DilloHtmlState *stack;
211 gint stack_top; /* Index to the top of the stack [0 based] */
212 gint stack_max;
213
214 DilloHtmlProcessingState InFlags; /* tracks which tags we are in */
215
216 GString *Stash;
217 gboolean StashSpace;
218
219 gchar *SPCBuf; /* Buffer for white space */
220
221 gint pre_column; /* current column, used in PRE tags with tabs */
222 gboolean PreFirstChar; /* used to skip the first CR or CRLF in PRE tags */
223 gboolean PrevWasCR; /* Flag to help parsing of "\r\n" in PRE tags */
224 gboolean PrevWasOpenTag; /* Flag to help deferred parsing of white space */
225 gboolean SPCPending; /* Flag to help deferred parsing of white space */
226 gboolean InVisitedLink; /* used to 'contrast_visited_colors' */
227 gboolean ReqTagClose; /* Flag to help handling bad-formed HTML */
228 gboolean CloseOneTag; /* Flag to help Html_tag_cleanup_at_close() */
229 gboolean TagSoup; /* Flag to enable the parser's cleanup functions */
230 gchar *NameVal; /* used for validation of "NAME" and "ID" in <A> */
231
232 /* element counters: used for validation purposes */
233 guchar Num_HTML, Num_HEAD, Num_BODY, Num_TITLE;
234
235 GString *attr_data;
236
237 BrowserWindow *bw;
238 };
239
240 #ifdef __cplusplus
241 }
242 #endif /* __cplusplus */
243
244
245 #endif /* __HTML_H__ */
0 /*
1 * File: image.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * This file implements image data transfer methods. It handles the transfer
14 * of data from an Image to a DwImage widget.
15 */
16
17 #include <gtk/gtk.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "msg.h"
22 #include "image.h"
23
24 /*
25 * Local data
26 */
27 static size_t linebuf_size = 0;
28 static guchar *linebuf = NULL;
29
30
31 /*
32 * Create and initialize a new image structure.
33 */
34 DilloImage *a_Image_new(gint width,
35 gint height,
36 const char *alt_text,
37 gint32 bg_color)
38 {
39 DilloImage *Image;
40
41 Image = g_new(DilloImage, 1);
42 Image->dw = (DwImage *) a_Dw_image_new(DW_IMAGE_RGB, alt_text);
43 Image->width = 0;
44 Image->height = 0;
45 Image->cmap = NULL;
46 Image->in_type = DILLO_IMG_TYPE_NOTSET;
47 Image->bg_color = bg_color;
48 Image->ProcessedBytes = 0;
49 Image->BitVec = NULL;
50 Image->State = IMG_Empty;
51
52 Image->RefCount = 1;
53
54 return Image;
55 }
56
57 /*
58 * Deallocate an Image structure
59 */
60 static void Image_free(DilloImage *Image)
61 {
62 a_Bitvec_free(Image->BitVec);
63 g_free(Image);
64 }
65
66 /*
67 * Unref and free if necessary
68 */
69 void a_Image_unref(DilloImage *Image)
70 {
71 _MSG(" %d ", Image->RefCount);
72 if (Image && --Image->RefCount == 0)
73 Image_free(Image);
74 }
75
76 /*
77 * Add a reference to an Image struct
78 */
79 void a_Image_ref(DilloImage *Image)
80 {
81 if (Image)
82 ++Image->RefCount;
83 }
84
85 /*
86 * Decode 'buf' (an image line) into RGB format.
87 */
88 static guchar *
89 Image_line(DilloImage *Image, const guchar *buf, const guchar *cmap, gint y)
90 {
91 guint x;
92
93 switch (Image->in_type) {
94 case DILLO_IMG_TYPE_INDEXED:
95 if (cmap) {
96 for (x = 0; x < Image->width; x++)
97 memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
98 } else {
99 MSG("Gif:: WARNING, image lacks a color map\n");
100 }
101 break;
102 case DILLO_IMG_TYPE_GRAY:
103 for (x = 0; x < Image->width; x++)
104 memset(linebuf + x * 3, buf[x], 3);
105 break;
106 case DILLO_IMG_TYPE_RGB:
107 /* avoid a memcpy here! --Jcid */
108 return (guchar *)buf;
109 case DILLO_IMG_TYPE_NOTSET:
110 g_warning("ERROR: Image type not set...\n");
111 break;
112 }
113 return linebuf;
114 }
115
116 /*
117 * Set initial parameters of the image
118 */
119 void a_Image_set_parms(DilloImage *Image, guchar *EntryBuf, DilloUrl *url,
120 gint version, guint width, guint height,
121 DilloImgType type)
122 {
123 if ( !Image->dw->buffer )
124 a_Dw_image_set_buffer(Image->dw, EntryBuf, url, version);
125 if ( !Image->BitVec )
126 Image->BitVec = a_Bitvec_new(height);
127 Image->in_type = type;
128 Image->width = width;
129 Image->height = height;
130 if (3 * width > linebuf_size) {
131 linebuf_size = 3 * width;
132 linebuf = g_realloc(linebuf, linebuf_size);
133 }
134 Image->State = IMG_SetParms;
135
136 a_Dw_image_size(Image->dw, width, height);
137 }
138
139 /*
140 * Reference the dicache entry color map
141 */
142 void a_Image_set_cmap(DilloImage *Image, const guchar *cmap)
143 {
144 Image->cmap = cmap;
145 Image->State = IMG_SetCmap;
146 }
147
148 /*
149 * Implement the write method
150 */
151 void a_Image_write(DilloImage *Image, const guchar *buf, guint y, gint decode)
152 {
153 guchar *newbuf;
154
155 g_return_if_fail ( y < Image->height );
156
157 if (decode) {
158 /* Decode 'buf' and copy it into the DicEntry buffer */
159 newbuf = Image_line(Image, buf, Image->cmap, y);
160 memcpy(Image->dw->buffer + y*Image->width*3, newbuf, Image->width*3);
161 }
162 a_Bitvec_set_bit(Image->BitVec, y);
163 Image->State = IMG_Write;
164
165 /* Update the row in DwImage */
166 a_Dw_image_draw_row(Image->dw, Image->width, Image->height, 0, y);
167 }
168
169 /*
170 * Implement the close method
171 */
172 void a_Image_close(DilloImage *Image)
173 {
174 a_Image_unref(Image);
175 }
0 #ifndef __IMAGE_H__
1 #define __IMAGE_H__
2
3 /* The DilloImage data-structure and methods */
4
5 #include <gdk/gdk.h>
6 #include <gtk/gtk.h>
7 #include "dw_image.h"
8 #include "bitvec.h"
9
10
11 typedef struct _DilloImage DilloImage;
12
13 typedef enum {
14 DILLO_IMG_TYPE_INDEXED,
15 DILLO_IMG_TYPE_RGB,
16 DILLO_IMG_TYPE_GRAY,
17 DILLO_IMG_TYPE_NOTSET /* Initial value */
18 } DilloImgType;
19
20 /* These will reflect the Image's "state" */
21 typedef enum {
22 IMG_Empty, /* Just created the entry */
23 IMG_SetParms, /* Parameters set */
24 IMG_SetCmap, /* Color map set */
25 IMG_Write, /* Feeding the entry */
26 IMG_Close, /* Whole image got! */
27 IMG_Abort /* Image transfer aborted */
28 } ImageState;
29
30 struct _DilloImage {
31 DwImage *dw;
32
33 /* Parameters as told by image data */
34 guint width;
35 guint height;
36
37 const guchar *cmap; /* Color map (only for indexed) */
38 DilloImgType in_type; /* Image Type */
39 gint32 bg_color; /* Background color */
40
41 gint ProcessedBytes; /* Amount of bytes already decoded */
42 bitvec_t *BitVec; /* Bit vector for decoded rows */
43 ImageState State;
44
45 gint RefCount; /* Reference counter */
46 };
47
48
49 /*
50 * Function prototypes
51 */
52 DilloImage *a_Image_new(gint width, gint height,
53 const char *alt_text, gint32 bg_color);
54 void a_Image_ref(DilloImage *Image);
55 void a_Image_unref(DilloImage *Image);
56
57 void a_Image_set_parms(DilloImage *Image, guchar *EntryBuf, DilloUrl *url,
58 gint version, guint width, guint height,
59 DilloImgType type);
60 void a_Image_set_cmap(DilloImage *Image, const guchar *cmap);
61 void a_Image_write(DilloImage *Image, const guchar *buf, guint y, gint decode);
62 void a_Image_close(DilloImage *Image);
63
64 #endif /* __IMAGE_H__ */
65
0 /*
1 * File: interface.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Sammy Mannaert <nstalkie@tvd.be>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <config.h>
13
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <gtk/gtk.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
20 #include <fcntl.h>
21
22 #include "msg.h"
23 #include "list.h"
24 #include "misc.h"
25 #include "dillo.h"
26 #include "history.h"
27 #include "nav.h"
28 #include "IO/Url.h"
29 #include "IO/IO.h"
30 #include "interface.h"
31 #include "commands.h"
32 #include "menu.h"
33 #include "bookmark.h"
34 #include "prefs.h"
35 #include "url.h"
36 #include "capi.h"
37 #include "gtk_ext_button.h"
38
39 #include "dw_widget.h"
40 #include "dw_gtk_scrolled_window.h"
41 #include "dw_gtk_viewport.h"
42 #include "dw_gtk_statuslabel.h"
43 #include "dw_container.h"
44 #include "progressbar.h"
45
46 #include "pixmaps.h"
47 #include <gdk/gdkkeysyms.h>
48 #include "../dpip/dpip.h"
49
50 #define DEBUG_LEVEL 0
51 #include "debug.h"
52
53
54 /*
55 * Local Data
56 */
57 /* BrowserWindow holds all the widgets (and perhaps more)
58 * for each new_browser.*/
59 static BrowserWindow **browser_window;
60 static gint num_bw, num_bw_max;
61
62 /* We need only one of them. */
63 static GtkTooltips *tooltips = NULL;
64
65 /* open dialog last dir */
66 static gchar *open_dialog_last_dirname = NULL;
67
68 /* save dialog last dir */
69 static gchar *save_dialog_last_dirname = NULL;
70
71
72 /*
73 * Initialize global data
74 */
75 void a_Interface_init(void)
76 {
77 num_bw = 0;
78 num_bw_max = 16;
79 browser_window = NULL;
80 tooltips = gtk_tooltips_new ();
81 open_dialog_last_dirname = NULL;
82 save_dialog_last_dirname = NULL;
83 }
84
85 /*
86 * Stop all active connections in the browser window (except downloads)
87 */
88 void a_Interface_stop(BrowserWindow *bw)
89 {
90 DEBUG_MSG(3, "a_Interface_stop: hi!\n");
91
92 /* Remove root clients */
93 while ( bw->NumRootClients ) {
94 a_Cache_stop_client(bw->RootClients[0]);
95 a_List_remove(bw->RootClients, 0, bw->NumRootClients);
96 }
97 /* Remove image clients */
98 while ( bw->NumImageClients ) {
99 a_Cache_stop_client(bw->ImageClients[0]);
100 a_List_remove(bw->ImageClients, 0, bw->NumImageClients);
101 }
102 }
103
104 /*
105 * Empty RootClients, ImageClients and PageUrls lists and
106 * reset progress bar data.
107 */
108 void a_Interface_clean(BrowserWindow *bw)
109 {
110 g_return_if_fail ( bw != NULL );
111
112 while ( bw->NumRootClients )
113 a_List_remove(bw->RootClients, 0, bw->NumRootClients);
114
115 while ( bw->NumImageClients )
116 a_List_remove(bw->ImageClients, 0, bw->NumImageClients);
117
118 while ( bw->NumPageUrls ) {
119 a_Url_free(bw->PageUrls[0].Url);
120 a_List_remove(bw->PageUrls, 0, bw->NumPageUrls);
121 }
122
123 /* Zero image-progressbar data */
124 bw->NumImages = 0;
125 bw->NumImagesGot = 0;
126 }
127
128 /*=== Browser Window Interface Updating =====================================*/
129 /*
130 * Remove the cache-client from the bw list
131 * (client can be a image or a html page)
132 */
133 void a_Interface_remove_client(BrowserWindow *bw, gint ClientKey)
134 {
135 gint i;
136 gboolean Found = FALSE;
137
138 for ( i = 0; !Found && i < bw->NumRootClients; ++i)
139 if ( bw->RootClients[i] == ClientKey ) {
140 a_List_remove(bw->RootClients, i, bw->NumRootClients);
141 Found = TRUE;
142 }
143
144 for ( i = 0; !Found && i < bw->NumImageClients; ++i)
145 if ( bw->ImageClients[i] == ClientKey ) {
146 a_List_remove(bw->ImageClients, i, bw->NumImageClients);
147 bw->NumImagesGot++;
148 Found = TRUE;
149 }
150
151 a_Interface_set_button_sens(bw);
152 }
153
154 /*
155 * Remove the cache-client from the bw list
156 * (client can be a image or a html page)
157 */
158 void a_Interface_close_client(BrowserWindow *bw, gint ClientKey)
159 {
160 gchar numstr[32];
161
162 a_Interface_remove_client(bw, ClientKey);
163
164 /* --Progress bars stuff-- */
165 g_snprintf(numstr, 32, "%s%d of %d", PBAR_ISTR(prefs.panel_size == 1),
166 bw->NumImagesGot, bw->NumImages);
167 a_Progressbar_update(bw->imgprogress, numstr,
168 (bw->NumImagesGot == bw->NumImages) ? 0 : 1 );
169 }
170
171 /*
172 * Set the sensitivity on back/forw buttons and menu entries.
173 */
174 static gint Interface_sens_idle_func(BrowserWindow *bw)
175 {
176 gboolean back_sensitive, forw_sensitive, stop_sensitive;
177
178 /* Stop button */
179 stop_sensitive = (bw->NumRootClients > 0);
180 gtk_widget_set_sensitive(bw->stop_button, stop_sensitive);
181
182 /* Back and Forward buttons */
183 back_sensitive = a_Nav_stack_ptr(bw) > 0;
184 gtk_widget_set_sensitive(bw->back_button, back_sensitive);
185 forw_sensitive = (a_Nav_stack_ptr(bw) < a_Nav_stack_size(bw) - 1 &&
186 !bw->nav_expecting);
187 gtk_widget_set_sensitive(bw->forw_button, forw_sensitive);
188
189 bw->sens_idle_id = 0;
190 return FALSE;
191 }
192
193 /*
194 * Set the sensitivity on back/forw buttons and menu entries.
195 */
196 void a_Interface_set_button_sens(BrowserWindow *bw)
197 {
198 if (bw->sens_idle_id == 0)
199 bw->sens_idle_id = gtk_idle_add(
200 (GtkFunction)Interface_sens_idle_func, bw);
201 }
202
203 /*
204 * Add a reference to the cache-client in the browser window's list.
205 * This helps us keep track of which are active in the window so that it's
206 * possible to abort them.
207 * (Root: Flag, whether a Root URL or not)
208 */
209 void a_Interface_add_client(BrowserWindow *bw, gint Key, gint Root)
210 {
211 gint nc;
212 char numstr[32];
213
214 g_return_if_fail ( bw != NULL );
215
216 if ( Root ) {
217 nc = bw->NumRootClients;
218 a_List_add(bw->RootClients, nc, bw->MaxRootClients);
219 bw->RootClients[nc] = Key;
220 bw->NumRootClients++;
221 a_Interface_set_button_sens(bw);
222 } else {
223 nc = bw->NumImageClients;
224 a_List_add(bw->ImageClients, nc, bw->MaxImageClients);
225 bw->ImageClients[nc] = Key;
226 bw->NumImageClients++;
227 bw->NumImages++;
228 a_Interface_set_button_sens(bw);
229
230 /* --Progress bar stuff-- */
231 g_snprintf(numstr, 32, "%s%d of %d", PBAR_ISTR(prefs.panel_size == 1),
232 bw->NumImagesGot, bw->NumImages);
233 a_Progressbar_update(bw->imgprogress, numstr, 1);
234 }
235 }
236
237 /*
238 * Add an URL to the browser window's list.
239 * This helps us keep track of page requested URLs so that it's
240 * possible to stop, abort and reload them.)
241 * Flags: Chosen from {BW_Root, BW_Image, BW_Download}
242 */
243 void a_Interface_add_url(BrowserWindow *bw, const DilloUrl *Url, gint Flags)
244 {
245 gint nu, i;
246 gboolean found = FALSE;
247
248 g_return_if_fail ( bw != NULL && Url != NULL );
249
250 nu = bw->NumPageUrls;
251 for ( i = 0; i < nu; i++ ) {
252 if ( !a_Url_cmp(Url, bw->PageUrls[i].Url) ) {
253 found = TRUE;
254 break;
255 }
256 }
257 if ( !found ) {
258 a_List_add(bw->PageUrls, nu, bw->MaxPageUrls);
259 bw->PageUrls[nu].Url = a_Url_dup(Url);
260 bw->PageUrls[nu].Flags = Flags;
261 bw->NumPageUrls++;
262 }
263
264 /* test:
265 MSG("Urls:\n");
266 for (i = 0; i < bw->NumPageUrls; i++)
267 MSG("%s\n", bw->PageUrls[i].Url);
268 MSG("---\n");
269 */
270 }
271
272 /*
273 * Remove a single browser window. This includes all its open childs,
274 * freeing all resources associated with them, and exiting gtk
275 * if no browser windows are left.
276 */
277 static gboolean Interface_quit(GtkWidget *widget, BrowserWindow *bw)
278 {
279 gint i;
280
281 /* stop/abort open connections. */
282 a_Interface_stop(bw);
283
284 g_slist_free(bw->PanelHandles);
285
286 if (bw->open_dialog_window != NULL)
287 gtk_widget_destroy(bw->open_dialog_window);
288 if (bw->openfile_dialog_window != NULL)
289 gtk_widget_destroy(bw->openfile_dialog_window);
290 if (bw->quit_dialog_window != NULL)
291 gtk_widget_destroy(bw->quit_dialog_window);
292 if (bw->findtext_dialog_window != NULL)
293 gtk_widget_destroy(bw->findtext_dialog_window);
294 if (bw->search_dialog_window != NULL)
295 gtk_widget_destroy(bw->search_dialog_window);
296 if (bw->proxy_passwd_dialog_window != NULL)
297 gtk_widget_destroy(bw->proxy_passwd_dialog_window);
298 if (bw->question_dialog_window != NULL)
299 gtk_widget_destroy(bw->question_dialog_window);
300
301 if (bw->menu_popup.over_page)
302 gtk_widget_destroy(bw->menu_popup.over_page);
303 if (bw->menu_popup.over_link)
304 /* this also destroys menu_popup.over_image */
305 gtk_widget_destroy(bw->menu_popup.over_link);
306 if (bw->menu_popup.over_back)
307 gtk_widget_destroy(bw->menu_popup.over_back);
308 if (bw->menu_popup.over_forw)
309 gtk_widget_destroy(bw->menu_popup.over_forw);
310 if (bw->menu_popup.over_bug)
311 gtk_widget_destroy(bw->menu_popup.over_bug);
312 if (bw->menu_popup.url)
313 a_Url_free(bw->menu_popup.url);
314 if (bw->menu_popup.url2)
315 a_Url_free(bw->menu_popup.url2);
316
317 if (bw->sens_idle_id)
318 gtk_idle_remove(bw->sens_idle_id);
319
320 for (i = 0; i < num_bw; i++)
321 if (browser_window[i] == bw) {
322 browser_window[i] = browser_window[--num_bw];
323 break;
324 }
325
326 /* free nav_stack and nav_expect stuff */
327 a_Nav_free(bw);
328
329 g_free(bw->RootClients);
330 g_free(bw->ImageClients);
331
332 for (i = 0; i < bw->NumPageUrls; i++)
333 a_Url_free(bw->PageUrls[i].Url);
334 g_free(bw->PageUrls);
335 g_free(bw);
336
337 if (num_bw == 0)
338 gtk_main_quit();
339
340 return FALSE;
341 }
342
343
344 /*=== Browser Window Interface Construction =================================*/
345
346 /*
347 * Clear a text entry
348 */
349 static void Interface_entry_clear(GtkEntry *entry)
350 {
351 gtk_entry_set_text(entry, "");
352 gtk_widget_grab_focus(GTK_WIDGET(entry));
353 }
354
355 /*
356 * Get the selection into the clear url button.
357 * (cub = clear url button)
358 */
359 static void Interface_cub_get_selection(GtkWidget *widget, gpointer data)
360 {
361 /* Request the the primary selection as a string */
362 gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
363 GDK_SELECTION_TYPE_STRING,
364 GDK_CURRENT_TIME);
365 }
366
367 /*
368 * Receive the selection (from a paste event)
369 * (cub = clear url button)
370 */
371 static void Interface_cub_selection_received(GtkWidget *widget,
372 GtkSelectionData *selection_data,
373 guint32 time,
374 gpointer data)
375 {
376 BrowserWindow *bw = data;
377 gchar *damn_string;
378
379 _MSG("Interface_cub_selection_received:\n");
380
381 if (selection_data->length < 0) {
382 DEBUG_MSG (1, "Selection retrieval failed\n");
383 return;
384 }
385
386 damn_string = g_strndup((gchar *)selection_data->data,
387 selection_data->length);
388 gtk_entry_set_text(GTK_ENTRY(bw->location), damn_string);
389 gtk_widget_activate(GTK_WIDGET(bw->location));
390 g_free(damn_string);
391
392 return;
393 }
394
395 /*
396 * Create a pixmap and return it.
397 */
398 static GtkWidget *Interface_pixmap_new(GtkWidget *parent, gchar **data)
399 {
400 GtkWidget *pixmapwid;
401 GdkPixmap *pixmap;
402 GdkBitmap *mask;
403 GtkStyle *style;
404
405 style = gtk_widget_get_style(parent);
406
407 pixmap = gdk_pixmap_create_from_xpm_d(parent->window, &mask,
408 &style->bg[GTK_STATE_NORMAL], data);
409
410 pixmapwid = gtk_pixmap_new(pixmap, mask);
411
412 return (pixmapwid);
413 }
414
415 /*
416 * Create an extended button for the toolbar. "label_text" may be NULL.
417 * "icon_ret" and "label_ret " may be used to return the child widgets, but
418 * may also be 0.
419 */
420 static GtkWidget *Interface_toolbox_ext_button_new(GtkWidget *parent,
421 gchar *label_text,
422 gchar **image_data,
423 GtkWidget **icon_ret,
424 GtkWidget **label_ret)
425 {
426 GtkWidget *button, *pixmap, *vbox, *label;
427
428 button = a_Gtk_ext_button_new ();
429 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
430 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
431 pixmap = Interface_pixmap_new (parent, image_data);
432
433 if (label_text) {
434 vbox = gtk_vbox_new(FALSE, 0);
435 gtk_container_add(GTK_CONTAINER (button), vbox);
436 gtk_box_pack_start(GTK_BOX(vbox), pixmap, FALSE, FALSE, 0);
437 label = gtk_label_new(label_text);
438 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
439 } else {
440 gtk_container_add(GTK_CONTAINER (button), pixmap);
441 label = NULL;
442 }
443
444 gtk_widget_show_all (button);
445
446 if (icon_ret)
447 *icon_ret = pixmap;
448 if (label_ret)
449 *label_ret = label;
450
451 return button;
452 }
453
454 /*
455 * Set the bw's cursor type
456 */
457 void a_Interface_set_cursor(BrowserWindow *bw, GdkCursorType CursorType)
458 {
459 GdkCursor *cursor;
460
461 if ( bw->CursorType != CursorType ) {
462 cursor = gdk_cursor_new(CursorType);
463 gdk_window_set_cursor(bw->docwin->window, cursor);
464 gdk_cursor_destroy(cursor);
465 bw->CursorType = CursorType;
466 }
467 }
468
469 /*
470 * Connect button's "clicked" event with (key, key_mod) pair.
471 */
472 static void Interface_set_button_accel(GtkButton *button,
473 gint key,
474 gint key_mod,
475 GtkAccelGroup *accel_group)
476 {
477 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group,
478 key, key_mod, GTK_ACCEL_LOCKED);
479 }
480
481 /*
482 * Create the "NEW" button, its location-entry and the search button.
483 */
484 static GtkWidget *Interface_locbar_new(BrowserWindow *bw)
485 {
486 GtkWidget *hbox, *toolbar_l, *toolbar_r;
487
488 hbox = gtk_hbox_new(FALSE, 0);
489
490 /* location entry */
491 bw->location = gtk_entry_new();
492 gtk_signal_connect(GTK_OBJECT(bw->location), "activate",
493 (GtkSignalFunc) a_Interface_entry_open_url, bw);
494 gtk_widget_add_accelerator(GTK_WIDGET(bw->location), "grab_focus",
495 bw->accel_group, GDK_u,
496 GDK_CONTROL_MASK, GTK_ACCEL_LOCKED);
497
498 /* left toolbar (Clear url) */
499 toolbar_l = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
500 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar_l), GTK_RELIEF_NONE);
501 GTK_WIDGET_UNSET_FLAGS (toolbar_l, GTK_CAN_FOCUS);
502
503 bw->clear_url_button =
504 Interface_toolbox_ext_button_new(bw->main_window, NULL, s_new_xpm,
505 NULL, NULL);
506 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->clear_url_button), 1);
507 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->clear_url_button), 2);
508
509 gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar_l), bw->clear_url_button,
510 "Clear the URL box (middle-click to paste an "
511 "URL).", "Toolbar/Clear");
512 gtk_signal_connect_object(GTK_OBJECT(bw->clear_url_button), "clicked",
513 GTK_SIGNAL_FUNC (Interface_entry_clear),
514 GTK_OBJECT(bw->location));
515 gtk_signal_connect_object(GTK_OBJECT(bw->clear_url_button), "clicked1",
516 GTK_SIGNAL_FUNC (Interface_entry_clear),
517 GTK_OBJECT(bw->location));
518 gtk_signal_connect(GTK_OBJECT(bw->clear_url_button), "clicked2",
519 GTK_SIGNAL_FUNC (Interface_cub_get_selection),
520 NULL);
521 gtk_signal_connect(GTK_OBJECT(bw->clear_url_button), "selection_received",
522 GTK_SIGNAL_FUNC (Interface_cub_selection_received),
523 bw);
524
525 /* right toolbar (Search) */
526 toolbar_r = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
527 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar_r), GTK_RELIEF_NONE);
528 GTK_WIDGET_UNSET_FLAGS (toolbar_r, GTK_CAN_FOCUS);
529
530 bw->search_button = gtk_toolbar_append_item(
531 GTK_TOOLBAR(toolbar_r),
532 NULL, "Search the Web", "Toolbar/New",
533 Interface_pixmap_new(bw->main_window, search_xpm),
534 GTK_SIGNAL_FUNC(a_Interface_search_dialog),
535 bw);
536 gtk_widget_add_accelerator(GTK_WIDGET(bw->search_button), "clicked",
537 bw->accel_group, GDK_s,
538 GDK_CONTROL_MASK, GTK_ACCEL_LOCKED);
539 gtk_box_pack_start(GTK_BOX(hbox), toolbar_l, FALSE, FALSE, 0);
540 gtk_widget_show(toolbar_l);
541 gtk_box_pack_start(GTK_BOX(hbox), bw->location, TRUE, TRUE, 0);
542 gtk_widget_show(bw->location);
543 gtk_box_pack_start(GTK_BOX(hbox), toolbar_r, FALSE, FALSE, 0);
544 gtk_widget_show(toolbar_r);
545 gtk_widget_show(hbox);
546
547 return (hbox);
548 }
549
550 /*
551 * Create a new toolbar (Back, Forward, Home, Reload, Save and Stop buttons)
552 */
553 static GtkWidget *Interface_toolbar_new(BrowserWindow *bw, gint label)
554 {
555 GtkWidget *toolbar, *label_widget, *icon_widget;
556 GtkToolbarChild *toolbar_child;
557 gboolean s = prefs.small_icons;
558
559 toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
560 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
561
562 /* back button */
563 bw->back_button =
564 Interface_toolbox_ext_button_new(bw->main_window, label ? "Back" : NULL,
565 s ? s_left_xpm : left_xpm,
566 &label_widget, &icon_widget);
567 gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), bw->back_button,
568 "Go to previous page (right-click for menu).",
569 "Toolbar/Back");
570 gtk_widget_set_sensitive(bw->back_button, FALSE);
571 Interface_set_button_accel(GTK_BUTTON(bw->back_button), GDK_comma,
572 0, bw->accel_group);
573 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->back_button), 1);
574 a_Gtk_ext_button_attach_menu_creator(
575 GTK_EXT_BUTTON(bw->back_button), 3,
576 a_Commands_back_button_menu_creator_callback, (gpointer)bw);
577 gtk_signal_connect(GTK_OBJECT(bw->back_button), "clicked",
578 GTK_SIGNAL_FUNC(a_Commands_back_callback), (gpointer)bw);
579 gtk_signal_connect(GTK_OBJECT(bw->back_button), "clicked1",
580 GTK_SIGNAL_FUNC(a_Commands_back_callback), (gpointer)bw);
581
582 /*
583 * HACK: We have added GtkExtButton's as widgets
584 * (gtk_toolbar_append_widget), but they should behave like buttons etc.
585 * (gtk_toolbar_append_item), especially, they should have the same size.
586 * So we change some parts of the internal representation.
587 */
588 toolbar_child = ((GtkToolbarChild*)GTK_TOOLBAR(toolbar)->children->data);
589 toolbar_child->type = GTK_TOOLBAR_CHILD_BUTTON;
590 toolbar_child->widget = bw->back_button;
591 toolbar_child->icon = icon_widget;
592 toolbar_child->label = label_widget;
593
594 /* forward button */
595 bw->forw_button =
596 Interface_toolbox_ext_button_new(bw->main_window,
597 label ? "Forward" : NULL,
598 s ? s_right_xpm : right_xpm,
599 &label_widget, &icon_widget);
600 gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), bw->forw_button,
601 "Go to next page (right-click for menu).",
602 "Toolbar/Forward");
603 gtk_widget_set_sensitive(bw->forw_button, FALSE);
604 Interface_set_button_accel(GTK_BUTTON(bw->forw_button), GDK_period,
605 0, bw->accel_group);
606 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->forw_button), 1);
607 a_Gtk_ext_button_attach_menu_creator(
608 GTK_EXT_BUTTON(bw->forw_button), 3,
609 a_Commands_forw_button_menu_creator_callback, (gpointer)bw);
610 gtk_signal_connect(GTK_OBJECT(bw->forw_button), "clicked",
611 GTK_SIGNAL_FUNC(a_Commands_forw_callback), (gpointer)bw);
612 gtk_signal_connect(GTK_OBJECT(bw->forw_button), "clicked1",
613 GTK_SIGNAL_FUNC(a_Commands_forw_callback), (gpointer)bw);
614
615 /*
616 * HACK: See above.
617 */
618 toolbar_child =
619 ((GtkToolbarChild*)GTK_TOOLBAR(toolbar)->children->next->data);
620 toolbar_child->type = GTK_TOOLBAR_CHILD_BUTTON;
621 toolbar_child->widget = bw->forw_button;
622 toolbar_child->icon = icon_widget;
623 toolbar_child->label = label_widget;
624
625 /* home button */
626 bw->home_button = gtk_toolbar_append_item(
627 GTK_TOOLBAR(toolbar),
628 label ? "Home" : NULL,
629 "Go to the Home page", "Toolbar/Home",
630 Interface_pixmap_new(bw->main_window,
631 s ? s_home_xpm : home_xpm),
632 (GtkSignalFunc) a_Commands_home_callback, bw);
633 /*
634 * SG: This had never any effect, was it there for future extensions?
635 * gtk_signal_connect(GTK_OBJECT(bw->home_button), "button-press-event",
636 * GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
637 */
638
639 /* reload button */
640 bw->reload_button = gtk_toolbar_append_item(
641 GTK_TOOLBAR(toolbar),
642 label ? "Reload" : NULL,
643 "Reload this page", "Toolbar/Reload",
644 Interface_pixmap_new(bw->main_window,
645 s ? s_reload_xpm : reload_xpm),
646 (GtkSignalFunc) a_Commands_reload_callback, bw);
647 Interface_set_button_accel(GTK_BUTTON(bw->reload_button), GDK_r,
648 GDK_CONTROL_MASK, bw->accel_group);
649
650 /* save button */
651 bw->save_button = gtk_toolbar_append_item(
652 GTK_TOOLBAR(toolbar),
653 label ? "Save" : NULL,
654 "Save this page", "Toolbar/Save",
655 Interface_pixmap_new(bw->main_window,
656 s ? s_save_xpm : save_xpm),
657 (GtkSignalFunc) a_Commands_save_callback, bw);
658 /* stop button */
659 bw->stop_button = gtk_toolbar_append_item(
660 GTK_TOOLBAR(toolbar),
661 label ? "Stop" : NULL,
662 "Stop the current transfer", "Toolbar/Stop",
663 Interface_pixmap_new(bw->main_window,
664 s ? s_stop_xpm : stop_xpm),
665 (GtkSignalFunc) a_Commands_stop_callback, bw);
666 gtk_widget_set_sensitive(bw->stop_button, FALSE);
667
668 /* bookmarks button */
669 bw->bookmarks_button = gtk_toolbar_append_item(
670 GTK_TOOLBAR(toolbar),
671 label ? "Book" : NULL,
672 "View bookmarks", "Toolbar/Bookmarks",
673 Interface_pixmap_new(bw->main_window,
674 s ? s_bm_xpm : bm_xpm),
675 (GtkSignalFunc) a_Commands_viewbm_callback, bw);
676 gtk_widget_set_sensitive(bw->bookmarks_button, TRUE);
677 Interface_set_button_accel(GTK_BUTTON(bw->bookmarks_button), GDK_semicolon,
678 0, bw->accel_group);
679 Interface_set_button_accel(GTK_BUTTON(bw->bookmarks_button), GDK_b,
680 GDK_CONTROL_MASK, bw->accel_group);
681 /*
682 * SG: This had never any effect, was it there for future extensions?
683 * gtk_signal_connect(GTK_OBJECT(bw->bookmarks_button),
684 * "button-press-event",
685 * GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
686 */
687
688 gtk_widget_show(toolbar);
689 return toolbar;
690 }
691
692 /*
693 * Create the progressbar's box
694 */
695 static GtkWidget *Interface_progressbox_new(BrowserWindow *bw, gint vertical)
696 {
697 GtkWidget *progbox;
698
699 progbox = vertical ? gtk_vbox_new(FALSE, 0) : gtk_hbox_new(FALSE, 0);
700 bw->progress_box = progbox;
701 bw->imgprogress = a_Progressbar_new();
702 bw->progress = a_Progressbar_new();
703 gtk_box_pack_start(GTK_BOX(progbox), bw->imgprogress, TRUE, TRUE, 0);
704 gtk_widget_show(bw->imgprogress);
705 gtk_box_pack_start(GTK_BOX(progbox), bw->progress, TRUE, TRUE, 0);
706 gtk_widget_show(bw->progress);
707 return (progbox);
708 }
709
710 /*
711 * Hide/Unhide this bw's control panels.
712 * toggle: Flag [toggle or set].
713 */
714 static void Interface_toggle_panel(BrowserWindow *bw, gint toggle)
715 {
716 if (toggle)
717 bw->fullwindow = !bw->fullwindow;
718
719 if (bw->fullwindow) {
720 g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_hide, NULL);
721 gtk_widget_hide(bw->status_box);
722 gtk_widget_show (bw->full_screen_off_button);
723 gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
724 } else {
725 g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_show, NULL);
726 gtk_widget_show(bw->status_box);
727 gtk_widget_hide (bw->full_screen_off_button);
728 }
729 }
730
731 /*
732 * Customize the appearance of the bw.
733 */
734 static void Interface_browser_window_customize(BrowserWindow *bw)
735 {
736 if ( !prefs.show_back )
737 gtk_widget_hide(bw->back_button);
738 if ( !prefs.show_forw )
739 gtk_widget_hide(bw->forw_button);
740 if ( !prefs.show_home )
741 gtk_widget_hide(bw->home_button);
742 if ( !prefs.show_reload )
743 gtk_widget_hide(bw->reload_button);
744 if ( !prefs.show_save )
745 gtk_widget_hide(bw->save_button);
746 if ( !prefs.show_stop )
747 gtk_widget_hide(bw->stop_button);
748 if ( !prefs.show_bookmarks )
749 gtk_widget_hide(bw->bookmarks_button);
750 if ( !prefs.show_menubar )
751 gtk_widget_hide(bw->menubar);
752 if ( !prefs.show_clear_url)
753 gtk_widget_hide(bw->clear_url_button);
754 if ( !prefs.show_url )
755 gtk_widget_hide(bw->location);
756 if ( !prefs.show_search )
757 gtk_widget_hide(bw->search_button);
758 if ( !prefs.show_progress_box )
759 gtk_widget_hide(bw->progress_box);
760
761 bw->fullwindow = prefs.fullwindow_start;
762 Interface_toggle_panel(bw, FALSE);
763 }
764
765 static void Interface_full_screen_callback (BrowserWindow *bw)
766 {
767 Interface_toggle_panel(bw, TRUE);
768 }
769
770 /*
771 * Handler for double-mouse-clicks that don't belong to the viewport.
772 */
773 static gint Interface_click_callback(BrowserWindow *bw, GdkEventButton *event)
774 {
775 if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
776 Interface_toggle_panel(bw, TRUE);
777 return TRUE;
778 }
779
780 /*
781 * Handler for key presses that don't belong to the viewport.
782 * (Used to customize the interface a bit)
783 */
784 static void Interface_key_press_handler(GtkWidget *widget,
785 GdkEventKey *event,
786 gpointer client_data)
787 {
788 BrowserWindow *bw = client_data;
789
790 switch (event->keyval) {
791 case GDK_BackSpace:
792 /* This key is handled here because GTK accel group ignores it */
793 if (event->state & GDK_SHIFT_MASK)
794 a_Commands_forw_callback(NULL, bw);
795 else
796 a_Commands_back_callback(NULL, bw);
797 break;
798 case GDK_slash:
799 /* This key is handled here because GTK accel group ignores it */
800 a_Commands_findtext_callback(NULL, bw);
801 break;
802 default:
803 _MSG(">> Key pressed!\n");
804 break;
805 }
806 }
807
808 /*
809 * Add the button for switching into fullscreen mode
810 */
811 static void Interface_add_full_screen_button (BrowserWindow *bw, GtkBox *box)
812 {
813 /* The button is put into a vbox, so that it is not enlarged vertically. */
814 GtkWidget *vbox, *dummy, *button, *pixmap;
815
816 vbox = gtk_vbox_new (FALSE, 0);
817 gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
818 gtk_widget_show (vbox);
819
820 /* The dummy will make the button align at the bottom.
821 * (Important only when using large text fonts.) */
822 dummy = gtk_vbox_new (FALSE, 0);
823 gtk_box_pack_start (GTK_BOX (vbox), dummy, TRUE, TRUE, 0);
824 gtk_widget_show (dummy);
825
826 button = gtk_button_new ();
827 gtk_tooltips_set_tip (tooltips, button, "Hide Controls", "Show Controls");
828 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
829 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
830 gtk_widget_show (button);
831
832 pixmap = Interface_pixmap_new(bw->main_window, full_screen_on_xpm);
833 gtk_container_add (GTK_CONTAINER (button), pixmap);
834 gtk_widget_show (pixmap);
835
836 gtk_signal_connect_object
837 (GTK_OBJECT (button), "clicked",
838 GTK_SIGNAL_FUNC (Interface_full_screen_callback), (gpointer)bw);
839 }
840
841 /*
842 * Create a new browser window and return it.
843 * (the new window is stored in browser_window[])
844 */
845 BrowserWindow *
846 a_Interface_browser_window_new(gint width, gint height, guint32 xid)
847 {
848 GtkWidget *box1, *hbox, *button, *label,
849 *progbox, *toolbar, *handlebox, *menubar, *locbox, *pixmap;
850 BrowserWindow *bw;
851 char buf[64];
852
853 /* We use g_new0() to zero the memory */
854 bw = g_new0(BrowserWindow, 1);
855 a_List_add(browser_window, num_bw, num_bw_max);
856 browser_window[num_bw++] = bw;
857
858 /* initialize nav_stack struct in browser_window struct */
859 a_Nav_init(bw);
860
861 if (!xid)
862 bw->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
863 else
864 bw->main_window = gtk_plug_new(xid);
865
866 gtk_window_set_policy(GTK_WINDOW(bw->main_window), TRUE, TRUE, FALSE);
867 gtk_signal_connect(GTK_OBJECT(bw->main_window), "delete_event",
868 GTK_SIGNAL_FUNC(gtk_object_destroy), bw);
869 gtk_signal_connect(GTK_OBJECT(bw->main_window), "destroy",
870 GTK_SIGNAL_FUNC(Interface_quit), bw);
871 gtk_container_border_width(GTK_CONTAINER(bw->main_window), 0);
872
873 gtk_window_set_wmclass(GTK_WINDOW(bw->main_window), "dillo", "Dillo");
874
875 /* -RL :: I must realize the window to see it correctly */
876 gtk_widget_realize(bw->main_window);
877
878 /* Create and attach an accel group to the main window */
879 bw->accel_group = gtk_accel_group_new();
880 gtk_window_add_accel_group(GTK_WINDOW(bw->main_window), bw->accel_group);
881
882 /* set window title */
883 g_snprintf(buf, 64, "Version %s", VERSION);
884 a_Interface_set_page_title(bw, buf);
885
886 box1 = gtk_vbox_new(FALSE, 0);
887
888 /* setup the control panel */
889 if (prefs.panel_size == 1) {
890 handlebox = gtk_handle_box_new();
891 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
892 hbox = gtk_hbox_new(FALSE, 0);
893 /* Control Buttons */
894 toolbar = Interface_toolbar_new(bw, 0);
895 /* Menus */
896 menubar = a_Menu_mainbar_new(bw, 1);
897 /* Location entry */
898 locbox = Interface_locbar_new(bw);
899 /* progress bars */
900 progbox = Interface_progressbox_new(bw, 0);
901
902 gtk_box_pack_start(GTK_BOX(hbox), toolbar, FALSE, FALSE, 0);
903 gtk_widget_show(toolbar);
904 gtk_box_pack_start(GTK_BOX(hbox), menubar, FALSE, FALSE, 0);
905 gtk_widget_show(menubar);
906 gtk_box_pack_start(GTK_BOX(hbox), locbox, TRUE, TRUE, 0);
907 gtk_widget_show(locbox);
908 gtk_box_pack_start(GTK_BOX(hbox), progbox, FALSE, FALSE, 0);
909 gtk_widget_show(progbox);
910 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
911 gtk_widget_show(hbox);
912 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
913 gtk_widget_show(handlebox);
914
915 } else if (prefs.panel_size == 2 || prefs.panel_size == 3) {
916 handlebox = gtk_handle_box_new();
917 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
918 hbox = gtk_hbox_new(FALSE, 0);
919 menubar = a_Menu_mainbar_new(bw, 0);
920 locbox = Interface_locbar_new(bw);
921 gtk_box_pack_start(GTK_BOX(hbox), menubar, FALSE, FALSE, 0);
922 gtk_widget_show(menubar);
923 gtk_box_pack_start(GTK_BOX(hbox), locbox, TRUE, TRUE, 0);
924 gtk_widget_show(locbox);
925 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
926 gtk_widget_show(hbox);
927 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
928 gtk_widget_show(handlebox);
929
930 handlebox = gtk_handle_box_new();
931 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
932 gtk_container_border_width(GTK_CONTAINER(handlebox), 4);
933 hbox = gtk_hbox_new(FALSE, 0);
934 toolbar = Interface_toolbar_new(bw, (prefs.panel_size == 3));
935 progbox = Interface_progressbox_new(bw, (prefs.panel_size == 3));
936 gtk_box_pack_start(GTK_BOX(hbox), toolbar, TRUE, TRUE, 0);
937 gtk_widget_show(toolbar);
938 gtk_box_pack_start(GTK_BOX(hbox), progbox, FALSE, FALSE, 0);
939 gtk_widget_show(progbox);
940 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
941 gtk_widget_show(hbox);
942 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
943 gtk_widget_show(handlebox);
944
945 } else {
946 handlebox = gtk_handle_box_new();
947 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
948 menubar = a_Menu_mainbar_new(bw, 0);
949 gtk_container_add(GTK_CONTAINER(handlebox), menubar);
950 gtk_widget_show(menubar);
951 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
952 gtk_widget_show(handlebox);
953
954 handlebox = gtk_handle_box_new();
955 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
956 gtk_container_border_width(GTK_CONTAINER(handlebox), 4);
957 hbox = gtk_hbox_new(FALSE, 0);
958 toolbar = Interface_toolbar_new(bw, 1);
959 progbox = Interface_progressbox_new(bw, 1);
960 gtk_box_pack_start(GTK_BOX(hbox), toolbar, TRUE, TRUE, 0);
961 gtk_widget_show(toolbar);
962 gtk_box_pack_start(GTK_BOX(hbox), progbox, FALSE, FALSE, 0);
963 gtk_widget_show(progbox);
964 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
965 gtk_widget_show(hbox);
966 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
967 gtk_widget_show(handlebox);
968
969 handlebox = gtk_handle_box_new();
970 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
971 locbox = Interface_locbar_new(bw);
972 gtk_container_add(GTK_CONTAINER(handlebox), locbox);
973 gtk_widget_show(locbox);
974 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
975 gtk_widget_show(handlebox);
976 }
977
978 /* Add box1 */
979 gtk_container_add(GTK_CONTAINER(bw->main_window), box1);
980
981 /* Now the main document window */
982 bw->docwin = a_Dw_gtk_scrolled_window_new();
983 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(bw->docwin),
984 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
985 gtk_box_pack_start(GTK_BOX(box1), bw->docwin, TRUE, TRUE, 0);
986 gtk_widget_show(bw->docwin);
987
988 /* todo (GTK2): this call is a bit of a workaround to missing focus
989 * between the location box and the docwin. It's fixed in GTK2:
990 * http://mail.gnome.org/archives/gtk-list/2003-June/msg00307.html */
991 gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
992
993 /* Full screen mode via double click is done in two ways: First,
994 * a feature of the selection is used, since in complex pages,
995 * getting events back to the viewport is quite difficult. Second,
996 * a simple callback, called e.g. when viewing image resources. */
997 a_Selection_set_dclick_callback(
998 GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(bw->docwin)->child)->child)->selection,
999 (void(*)(gpointer))Interface_full_screen_callback, bw);
1000 /* Selection requires an owner widget */
1001 a_Selection_set_owner(
1002 GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(bw->docwin)->child)->child)->selection,
1003 GTK_BIN(bw->docwin)->child);
1004
1005 gtk_signal_connect_object_after(GTK_OBJECT(GTK_BIN(bw->docwin)->child),
1006 "button_press_event",
1007 GTK_SIGNAL_FUNC(Interface_click_callback),
1008 (gpointer)bw);
1009
1010 /* full screen button: actually there're two buttons, one in the scrolled
1011 * window (fs off) and one in the status bar (fs on). They look as one. */
1012 bw->full_screen_off_button = gtk_button_new ();
1013 gtk_tooltips_set_tip (tooltips, bw->full_screen_off_button,
1014 "Show Controls", "Hide Controls");
1015 GTK_WIDGET_UNSET_FLAGS (bw->full_screen_off_button, GTK_CAN_FOCUS);
1016 a_Dw_gtk_scrolled_window_add_gadget (GTK_DW_SCROLLED_WINDOW (bw->docwin),
1017 bw->full_screen_off_button);
1018 pixmap = Interface_pixmap_new(bw->main_window, full_screen_off_xpm);
1019 gtk_container_add (GTK_CONTAINER (bw->full_screen_off_button), pixmap);
1020 gtk_widget_show (pixmap);
1021 gtk_signal_connect_object
1022 (GTK_OBJECT (bw->full_screen_off_button), "clicked",
1023 GTK_SIGNAL_FUNC (Interface_full_screen_callback), (gpointer)bw);
1024 Interface_set_button_accel(GTK_BUTTON(bw->full_screen_off_button), GDK_h,
1025 GDK_CONTROL_MASK, bw->accel_group);
1026
1027 /* Catch key_press event */
1028 gtk_signal_connect(GTK_OBJECT(GTK_BIN(bw->docwin)->child),
1029 "key_press_event",
1030 GTK_SIGNAL_FUNC(Interface_key_press_handler), bw);
1031
1032 gtk_widget_set_usize(bw->main_window, width, height);
1033
1034 /* status widget */
1035 /* create the over-bug-meter menu */
1036 bw->menu_popup.over_bug = a_Menu_popup_ob_new(bw);
1037
1038 bw->status = a_Dw_gtk_statuslabel_new("");
1039 gtk_misc_set_alignment(GTK_MISC(bw->status), 0.0, 0.5);
1040 /* status widget for HTML errors.
1041 * Note: the "clicked" signal is hooked with the linkblock later */
1042 button = bw->status_bug_meter = a_Gtk_ext_button_new();
1043 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
1044 a_Gtk_ext_button_set_command (GTK_EXT_BUTTON (button), 1);
1045 a_Gtk_ext_button_attach_menu (GTK_EXT_BUTTON (button), 3,
1046 GTK_MENU(bw->menu_popup.over_bug));
1047
1048 hbox = gtk_hbox_new(FALSE, 0);
1049 pixmap = Interface_pixmap_new(bw->main_window, mini_bug_xpm);
1050 gtk_object_set_data(GTK_OBJECT (button), "bug", pixmap);
1051 gtk_box_pack_start(GTK_BOX(hbox), pixmap, FALSE, FALSE, 0);
1052 pixmap = Interface_pixmap_new(bw->main_window, mini_ok_xpm);
1053 gtk_object_set_data(GTK_OBJECT (button), "ok", pixmap);
1054 gtk_box_pack_start(GTK_BOX(hbox), pixmap, FALSE, FALSE, 0);
1055 gtk_widget_show (pixmap);
1056 label = gtk_label_new("");
1057 gtk_object_set_data(GTK_OBJECT (button), "label", label);
1058 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1059 gtk_container_add (GTK_CONTAINER (button), hbox);
1060 gtk_widget_show(hbox);
1061 gtk_tooltips_set_tip (tooltips, button,
1062 "Show HTML bugs (right-click for menu).", "");
1063
1064 bw->status_box = gtk_hbox_new(FALSE, 0);
1065 gtk_box_pack_start(GTK_BOX(bw->status_box), bw->status, TRUE, TRUE, 2);
1066 gtk_widget_show(bw->status);
1067 gtk_box_pack_start(GTK_BOX(bw->status_box), button, FALSE, FALSE, 0);
1068 gtk_widget_show(button);
1069
1070 Interface_add_full_screen_button (bw, GTK_BOX (bw->status_box));
1071
1072 gtk_box_pack_start(GTK_BOX(box1), bw->status_box, FALSE, FALSE, 0);
1073 gtk_widget_show(bw->status_box);
1074
1075 gtk_widget_show(bw->main_window);
1076 gtk_widget_show(box1);
1077
1078 /* initialize the rest of the bw's data. */
1079 bw->pagemarks_menuitem = NULL;
1080 bw->pagemarks_menu = NULL;
1081 bw->pagemarks_last = NULL;
1082 bw->viewbugs_menuitem = NULL;
1083
1084 /* the image menu is created first because it is used by the link menu */
1085 bw->menu_popup.over_image = a_Menu_popup_oi_new(bw);
1086 bw->menu_popup.over_link = a_Menu_popup_ol_new(bw);
1087 bw->menu_popup.over_page = a_Menu_popup_op_new(bw);
1088 bw->menu_popup.over_back = NULL;
1089 bw->menu_popup.over_forw = NULL;
1090 bw->menu_popup.url = NULL;
1091 bw->menu_popup.url2 = NULL;
1092
1093 bw->redirect_level = 0;
1094 bw->sens_idle_id = 0;
1095
1096 bw->CursorType = -1;
1097
1098 bw->RootClients = NULL;
1099 bw->NumRootClients = 0;
1100 bw->MaxRootClients = 8;
1101
1102 bw->ImageClients = NULL;
1103 bw->NumImageClients = 0;
1104 bw->MaxImageClients = 8;
1105 bw->NumImages = 0;
1106 bw->NumImagesGot = 0;
1107
1108 bw->PageUrls = NULL;
1109 bw->NumPageUrls = 0;
1110 bw->MaxPageUrls = 8;
1111
1112 bw->open_dialog_window = NULL;
1113 bw->open_dialog_entry = NULL;
1114 bw->openfile_dialog_window = NULL;
1115 bw->quit_dialog_window = NULL;
1116 bw->save_dialog_window = NULL;
1117 bw->save_link_dialog_window = NULL;
1118 bw->findtext_dialog_window = NULL;
1119 bw->findtext_dialog_check = NULL;
1120 bw->findtext_dialog_entry = NULL;
1121 bw->search_dialog_window = NULL;
1122 bw->search_dialog_entry = NULL;
1123 bw->proxy_passwd_dialog_window = NULL;
1124 bw->proxy_passwd_dialog_entry = NULL;
1125 bw->question_dialog_window = NULL;
1126 bw->question_dialog_data = NULL;
1127 bw->viewsource_window = NULL;
1128 bw->pagebugs_window = NULL;
1129
1130 /* now that the bw is made, let's customize it.. */
1131 Interface_browser_window_customize(bw);
1132
1133 return bw;
1134 }
1135
1136 /*
1137 * Set the title of the browser window to start with "Dillo: "
1138 * prepended to it.
1139 */
1140 void a_Interface_set_page_title(BrowserWindow *bw, char *title)
1141 {
1142 GString *buf;
1143
1144 g_return_if_fail (bw != NULL && title != NULL);
1145
1146 buf = g_string_new("");
1147 g_string_sprintfa(buf, "Dillo: %s", title);
1148 gtk_window_set_title(GTK_WINDOW(bw->main_window), buf->str);
1149 g_string_free(buf, TRUE);
1150 }
1151
1152 /*
1153 * Set location entry's text
1154 */
1155 void a_Interface_set_location_text(BrowserWindow *bw, char *text)
1156 {
1157 gtk_entry_set_text(GTK_ENTRY(bw->location), text);
1158 }
1159
1160 /*
1161 * Get location entry's text
1162 */
1163 gchar *a_Interface_get_location_text(BrowserWindow *bw)
1164 {
1165 return gtk_entry_get_text(GTK_ENTRY(bw->location));
1166 }
1167
1168 /*
1169 * Reset images and text progress bars
1170 */
1171 void a_Interface_reset_progress_bars(BrowserWindow *bw)
1172 {
1173 a_Progressbar_update(bw->progress, "", 0);
1174 a_Progressbar_update(bw->imgprogress, "", 0);
1175 }
1176
1177 /*
1178 * Set the status string on the bottom of the dillo window.
1179 */
1180 void a_Interface_msg(BrowserWindow *bw, const char *format, ... )
1181 {
1182 static char msg[1024];
1183 va_list argp;
1184
1185 if ( bw ) {
1186 va_start(argp, format);
1187 g_vsnprintf(msg, 1024, format, argp);
1188 va_end(argp);
1189 gtk_label_set_text(GTK_LABEL(bw->status), msg);
1190 bw->status_is_link = 0;
1191 }
1192 }
1193
1194 /*
1195 * Update the bug-meter button for detected page errors.
1196 */
1197 void a_Interface_bug_meter_update(BrowserWindow *bw, gint num_err)
1198 {
1199 static char msg[64];
1200 gpointer label, bug, ok;
1201
1202 if ( bw ) {
1203 label = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "label");
1204 if (num_err < 2) {
1205 bug = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "bug");
1206 ok = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "ok");
1207 if (num_err == 0) {
1208 gtk_widget_hide(bug);
1209 gtk_widget_hide(label);
1210 gtk_widget_show(ok);
1211 } else {
1212 gtk_widget_show(bug);
1213 gtk_widget_show(label);
1214 gtk_widget_hide(ok);
1215 }
1216 gtk_widget_queue_resize (GTK_WIDGET (bw->status_bug_meter));
1217 }
1218 g_snprintf(msg, 64, " %d", num_err);
1219 gtk_label_set_text(GTK_LABEL(label), msg);
1220 }
1221 }
1222
1223 /*
1224 * Called from `destroy' callback in Interface_make_*_dialog
1225 */
1226 static void Interface_destroy_window(GtkWidget *widget, GtkWidget **window)
1227 {
1228 /* todo: sometimes this function is called twice with dialog windows */
1229 _MSG("Interface_destroy_window %p\n", *window);
1230 if (*window) {
1231 gtk_widget_destroy(*window);
1232 *window = NULL;
1233 }
1234 }
1235
1236
1237 /*
1238 * Close and free every single browser_window (called at exit time)
1239 */
1240 void a_Interface_quit_all(void)
1241 {
1242 BrowserWindow **bws;
1243 gint i, n_bw;
1244
1245 n_bw = num_bw;
1246 bws = g_malloc(sizeof(BrowserWindow *) * n_bw);
1247
1248 /* we copy into a new list because destroying the main window can
1249 * modify the browser_window array. */
1250 for (i = 0; i < n_bw; i++)
1251 bws[i] = browser_window[i];
1252
1253 for (i = 0; i < n_bw; i++)
1254 gtk_widget_destroy(bws[i]->main_window);
1255
1256 g_free(bws);
1257
1258 g_free(open_dialog_last_dirname);
1259 g_free(save_dialog_last_dirname);
1260 }
1261
1262 /*
1263 * Make a dialog for choosing files (by calling
1264 * gtk_file_selection_*() calls)
1265 * This can be used for saving, opening, or whatever,
1266 * just set the correct callbacks
1267 */
1268 static void
1269 Interface_make_choose_file_dialog(GtkWidget **DialogWindow,
1270 char *WmName, char *WmClass, char *WTitle,
1271 GtkSignalFunc B1CallBack, void *B1CbData)
1272 {
1273 GtkAccelGroup *accel_group;
1274
1275 *DialogWindow = gtk_file_selection_new(WTitle);
1276 gtk_window_set_modal(GTK_WINDOW(*DialogWindow), FALSE);
1277 gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
1278
1279 gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(*DialogWindow));
1280 gtk_signal_connect(
1281 GTK_OBJECT(*DialogWindow),
1282 "destroy", (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1283 gtk_signal_connect(
1284 GTK_OBJECT(GTK_FILE_SELECTION(*DialogWindow)->ok_button),
1285 "clicked", (GtkSignalFunc) B1CallBack, B1CbData);
1286 gtk_signal_connect(
1287 GTK_OBJECT(GTK_FILE_SELECTION (*DialogWindow)->cancel_button),
1288 "clicked", (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1289
1290 /* Make GDK_Escape close the dialog */
1291 accel_group = gtk_accel_group_new();
1292 gtk_window_add_accel_group(GTK_WINDOW(*DialogWindow), accel_group);
1293 gtk_widget_add_accelerator(
1294 GTK_FILE_SELECTION(*DialogWindow)->cancel_button, "clicked",
1295 accel_group, GDK_Escape, 0, GTK_ACCEL_LOCKED);
1296 }
1297
1298 /*
1299 * Get the file URL from the widget and push it to the browser window.
1300 */
1301 static void
1302 Interface_openfile_ok_callback(GtkWidget *widget, BrowserWindow *bw)
1303 {
1304 char *fn, *Cfn, *s;
1305 DilloUrl *url;
1306 GString *UrlStr = g_string_sized_new(1024);
1307
1308 fn = gtk_file_selection_get_filename(
1309 GTK_FILE_SELECTION(bw->openfile_dialog_window));
1310
1311 Cfn = (s = a_Misc_escape_chars(fn, "% ")) ? s : fn;
1312 g_string_sprintf(UrlStr, "file:%s", Cfn);
1313 url = a_Url_new(UrlStr->str, NULL, 0, 0, 0);
1314 a_Nav_push(bw, url);
1315 a_Url_free(url);
1316 g_string_free(UrlStr, TRUE);
1317 g_free(s);
1318
1319 g_free(open_dialog_last_dirname);
1320 open_dialog_last_dirname = g_strdup(fn);
1321
1322 gtk_widget_destroy(bw->openfile_dialog_window);
1323 }
1324
1325 /*
1326 * Returns a newly allocated string holding a search url generated from
1327 * a string of keywords (separarated by blanks) and prefs.search_url.
1328 * The search string is urlencoded.
1329 */
1330 static gchar *Interface_make_search_url(const gchar *str)
1331 {
1332 gchar *keys = a_Url_encode_hex_str(str), *c = prefs.search_url;
1333 GString *newstr = g_string_sized_new(strlen(c)+strlen(keys));
1334 gchar *search_url;
1335
1336 for (; *c; c++)
1337 if (*c == '%')
1338 switch(*++c) {
1339 case 's':
1340 g_string_append(newstr, keys); break;;
1341 case '%':
1342 g_string_append_c(newstr, '%'); break;;
1343 case 0:
1344 MSG("Warning: search_url ends with '%%'\n"); c--; break;;
1345 default:
1346 MSG("Warning: illegal specifier '%%%c' in search_url\n", *c);
1347 }
1348 else
1349 g_string_append_c(newstr, *c);
1350
1351 g_free(keys);
1352
1353 search_url = newstr->str;
1354 g_string_free(newstr, FALSE);
1355 return search_url;
1356 }
1357
1358 /*
1359 * Open an url string.
1360 * The URL is not sent "as is", illegal chars are ripped out,
1361 * then it's fully parsed by a_Url_new().
1362 */
1363 static void Interface_open_url_string(gchar *text, BrowserWindow *bw)
1364 {
1365 gchar *new_text;
1366 DilloUrl *url;
1367
1368 if (text && *text) {
1369 /* Filter URL string */
1370 new_text = a_Url_string_strip_delimiters(text);
1371
1372 url = a_Url_new(new_text, NULL, 0, 0, 0);
1373 if (url) {
1374 a_Nav_push(bw, url);
1375 a_Url_free(url);
1376 }
1377 g_free(new_text);
1378 }
1379
1380 /* let the rendered area have focus */
1381 gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
1382 }
1383
1384 /*
1385 * Open an URL specified in the location entry, or in the open URL dialog.
1386 */
1387 void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw)
1388 {
1389 gchar *text;
1390 GtkEntry *entry;
1391
1392 /* entry = { bw->location | bw->open_dialog_entry } */
1393 entry = GTK_ENTRY(widget == bw->location ? widget : bw->open_dialog_entry);
1394 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
1395 DEBUG_MSG(1, "entry_open_url %s\n", text);
1396 Interface_open_url_string(text, bw);
1397 g_free(text);
1398
1399 if (bw->open_dialog_window != NULL)
1400 gtk_widget_hide(bw->open_dialog_window);
1401 }
1402
1403 /*
1404 * Create and show the "Open File" dialog
1405 */
1406 void a_Interface_openfile_dialog(BrowserWindow *bw)
1407 {
1408 if (!bw->openfile_dialog_window) {
1409 Interface_make_choose_file_dialog(
1410 &(bw->openfile_dialog_window),
1411 "openfile_dialog", "Dillo", "Dillo: Open File",
1412 (GtkSignalFunc) Interface_openfile_ok_callback, (void *)bw);
1413 }
1414
1415 if (open_dialog_last_dirname)
1416 gtk_file_selection_set_filename(
1417 GTK_FILE_SELECTION(bw->openfile_dialog_window),
1418 open_dialog_last_dirname);
1419
1420 if (!GTK_WIDGET_VISIBLE(bw->openfile_dialog_window))
1421 gtk_widget_show(bw->openfile_dialog_window);
1422 else
1423 gdk_window_raise(bw->openfile_dialog_window->window);
1424 }
1425
1426 /*
1427 * Make a dialog interface with three buttons, a text entry, and an optional
1428 * check button.
1429 */
1430 static void
1431 Interface_make_dialog(GtkWidget **DialogWindow, char *WmName, char *WmClass,
1432 char *WTitle, GtkWidget **DialogEntry, char *EntryStr, gint VisibleEntry,
1433 GtkWidget **CheckButton, char *CheckButtonText,
1434 char *B1Label, GtkSignalFunc B1CallBack, void *B1CbData)
1435 {
1436 GtkWidget *button, *box1, *box2, *entry;
1437 GtkAccelGroup *accel_group;
1438
1439 *DialogWindow = gtk_window_new(GTK_WINDOW_DIALOG);
1440 gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
1441 gtk_window_set_position(GTK_WINDOW(*DialogWindow), GTK_WIN_POS_CENTER);
1442 gtk_window_set_title(GTK_WINDOW(*DialogWindow), WTitle);
1443 gtk_signal_connect(GTK_OBJECT(*DialogWindow), "destroy",
1444 (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1445
1446 /* Create and attach an accel group to the dialog window */
1447 accel_group = gtk_accel_group_new();
1448 gtk_window_add_accel_group(GTK_WINDOW(*DialogWindow), accel_group);
1449
1450 gtk_container_border_width(GTK_CONTAINER(*DialogWindow), 5);
1451
1452 box1 = gtk_vbox_new(FALSE, 5);
1453 gtk_container_add(GTK_CONTAINER(*DialogWindow), box1);
1454 gtk_widget_show(box1);
1455
1456 entry = gtk_entry_new();
1457 GTK_WIDGET_SET_FLAGS(entry, GTK_HAS_FOCUS);
1458 gtk_widget_set_usize(entry, 250, 0);
1459 gtk_entry_set_text(GTK_ENTRY(entry), EntryStr);
1460 gtk_box_pack_start(GTK_BOX(box1), entry, FALSE, FALSE, 0);
1461 *DialogEntry = GTK_WIDGET(entry);
1462 gtk_entry_set_visibility(GTK_ENTRY(entry), VisibleEntry ? TRUE : FALSE);
1463 gtk_widget_show(entry);
1464
1465 if (CheckButton && CheckButtonText) {
1466 *CheckButton = gtk_check_button_new_with_label(CheckButtonText);
1467 gtk_box_pack_start(GTK_BOX(box1), *CheckButton, FALSE, FALSE, 0);
1468 gtk_widget_show(*CheckButton);
1469 }
1470
1471 gtk_signal_connect(GTK_OBJECT(entry), "activate", B1CallBack, B1CbData);
1472
1473 box2 = gtk_hbox_new(TRUE, 5);
1474 gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, FALSE, 0);
1475 gtk_widget_show(box2);
1476
1477 button = gtk_button_new_with_label(B1Label);
1478 gtk_signal_connect(GTK_OBJECT(button), "clicked", B1CallBack, B1CbData);
1479 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1480 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1481 gtk_widget_grab_default(button);
1482 gtk_widget_show(button);
1483 gtk_signal_connect_object(GTK_OBJECT(entry), "focus_in_event",
1484 (GtkSignalFunc) gtk_widget_grab_default,
1485 GTK_OBJECT(button));
1486
1487 button = gtk_button_new_with_label("Clear");
1488 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1489 (GtkSignalFunc) Interface_entry_clear,
1490 GTK_OBJECT(entry));
1491 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1492 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1493 gtk_widget_show(button);
1494
1495 button = gtk_button_new_with_label("Cancel");
1496 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1497 (GtkSignalFunc) gtk_widget_destroy,
1498 GTK_OBJECT(*DialogWindow));
1499 gtk_widget_add_accelerator(button, "clicked", accel_group,
1500 GDK_Escape, 0, GTK_ACCEL_LOCKED);
1501 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1502 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1503 gtk_widget_show(button);
1504
1505 gtk_widget_grab_focus(entry);
1506 }
1507
1508 /*
1509 * Set bw->question_dialog_answer
1510 */
1511 static void Interface_question_dialog_set_answer_cb(DialogAnswer *answer)
1512 {
1513 _MSG("Interface_question_dialog_set_answer_cb\n");
1514 answer->bw->question_dialog_answer = answer;
1515 }
1516
1517 /*
1518 * Make a question-dialog with a question and some alternatives.
1519 * The selected choices are left in bw->question_dialog_answer in a structure.
1520 * A generic callback function can decide afterwards based on that information.
1521 * (0 means the window was cancelled, and 1 to 5 the respective alternatives)
1522 */
1523 static void Interface_make_question_dialog(
1524 BrowserWindow *bw,
1525 GtkWidget **DialogWindow, char *WmName, char *WmClass,
1526 char *WTitle, char *Question, gint modal_flag,
1527 char *alt1, char *alt2, char *alt3, char *alt4, char *alt5,
1528 GtkSignalFunc AnswerCallback)
1529 {
1530 GtkWidget *frame, *label, *button = NULL, *box1, *box2;
1531 DialogAnswer *answer;
1532 int i;
1533
1534 *DialogWindow = gtk_window_new(GTK_WINDOW_DIALOG);
1535 gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
1536 gtk_window_set_title(GTK_WINDOW(*DialogWindow), WTitle);
1537 gtk_window_set_position(GTK_WINDOW(*DialogWindow), GTK_WIN_POS_CENTER);
1538 gtk_container_border_width(GTK_CONTAINER(*DialogWindow), 10);
1539 gtk_window_set_modal(GTK_WINDOW(*DialogWindow), modal_flag);
1540 /* attach AnswerCallback to "destroy" so appropriate actions can be taken */
1541 gtk_signal_connect_object(GTK_OBJECT(*DialogWindow), "destroy",
1542 (GtkSignalFunc) AnswerCallback, (gpointer)bw);
1543 gtk_signal_connect(GTK_OBJECT(*DialogWindow), "destroy",
1544 (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1545
1546 box1 = gtk_vbox_new(FALSE, 5);
1547 frame = gtk_frame_new(NULL);
1548 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
1549 label = gtk_label_new(Question);
1550 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_FILL);
1551 gtk_misc_set_padding(GTK_MISC(label), 20, 20);
1552 gtk_container_add(GTK_CONTAINER(frame), label);
1553 gtk_widget_show(label);
1554 gtk_widget_show(frame);
1555 gtk_box_pack_start(GTK_BOX(box1), frame, TRUE, TRUE, 0);
1556
1557 /* This struct will save us some code and lots of callback functions.
1558 * (it looks clumsy, but provides a handy interface for the caller) */
1559 answer = g_new(DialogAnswer, 6);
1560 for (i = 0; i < 6; ++i) {
1561 answer[i].bw = bw;
1562 answer[i].alt_num = i;
1563 answer[i].this = answer;
1564 }
1565 answer[0].alt_str = NULL; answer[1].alt_str = alt1;
1566 answer[2].alt_str = alt2; answer[3].alt_str = alt3;
1567 answer[4].alt_str = alt4; answer[5].alt_str = alt5;
1568 /* Set the default answer */
1569 bw->question_dialog_answer = &answer[0];
1570
1571 /* pack the alternatives */
1572 box2 = gtk_hbox_new(TRUE, 5);
1573 for (i = 1; i < 6 && answer[i].alt_str; ++i) {
1574 button = gtk_button_new_with_label(answer[i].alt_str);
1575 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1576 (GtkSignalFunc) Interface_question_dialog_set_answer_cb,
1577 (gpointer)&answer[i]);
1578 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1579 (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1580 gtk_widget_show(button);
1581 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1582 }
1583
1584 gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, FALSE, 0);
1585 gtk_container_add(GTK_CONTAINER(*DialogWindow), box1);
1586
1587 gtk_widget_show(box2);
1588 gtk_widget_show(box1);
1589 gtk_widget_grab_focus(button);
1590 gtk_widget_show(*DialogWindow);
1591 }
1592
1593 /*
1594 * Create and show an [OK|Cancel] question dialog
1595 */
1596 void a_Interface_question_dialog(
1597 BrowserWindow *bw, gchar *QuestionTxt, gint modal_flag,
1598 char *alt1, char *alt2, char *alt3, char *alt4, char *alt5,
1599 GtkSignalFunc AnswerCallback)
1600 {
1601 if (!bw->question_dialog_window) {
1602 Interface_make_question_dialog(
1603 bw,
1604 &(bw->question_dialog_window), "question_dialog", "Dillo",
1605 "Dillo: Question", QuestionTxt, modal_flag,
1606 alt1, alt2, alt3, alt4, alt5,
1607 AnswerCallback);
1608 } else {
1609 /* should not reach here */
1610 gtk_widget_destroy(bw->question_dialog_window);
1611 }
1612 }
1613
1614 /*
1615 * Create and show the open URL dialog
1616 */
1617 void a_Interface_open_dialog(GtkWidget *widget, BrowserWindow *bw)
1618 {
1619 if (!bw->open_dialog_window) {
1620 Interface_make_dialog(&(bw->open_dialog_window),
1621 "open_dialog", "Dillo", "Dillo: Open URL",
1622 &(bw->open_dialog_entry), "", 1, NULL, NULL,
1623 "OK", (GtkSignalFunc) a_Interface_entry_open_url, (void *)bw);
1624 if (prefs.transient_dialogs)
1625 gtk_window_set_transient_for(GTK_WINDOW(bw->open_dialog_window),
1626 GTK_WINDOW(bw->main_window));
1627 }
1628
1629 if (!GTK_WIDGET_VISIBLE(bw->open_dialog_window))
1630 gtk_widget_show(bw->open_dialog_window);
1631 else
1632 gdk_window_raise(bw->open_dialog_window->window);
1633 }
1634
1635 /*
1636 * Receive data from the cache and save it to a local file
1637 */
1638 static void Interface_save_callback(int Op, CacheClient_t *Client)
1639 {
1640 DilloWeb *Web = Client->Web;
1641 gint Bytes;
1642
1643 if ( Op ){
1644 struct stat st;
1645
1646 fflush(Web->stream);
1647 fstat(fileno(Web->stream), &st);
1648 fclose(Web->stream);
1649 a_Interface_msg(Web->bw, "File saved (%d Bytes)", st.st_size);
1650 } else {
1651 if ( (Bytes = Client->BufSize - Web->SavedBytes) > 0 ) {
1652 Bytes = fwrite(Client->Buf + Web->SavedBytes, 1, Bytes, Web->stream);
1653 Web->SavedBytes += Bytes;
1654 }
1655 }
1656 }
1657
1658 /*
1659 * Save current page to a local file
1660 */
1661 static void Interface_file_save_url(GtkWidget *widget, BrowserWindow *bw)
1662 {
1663 const char *name;
1664 GtkFileSelection *choosefile;
1665 GtkEntry *entry_url;
1666 DilloUrl *url;
1667 FILE *out;
1668
1669 choosefile = GTK_FILE_SELECTION(bw->save_dialog_window);
1670 entry_url = GTK_ENTRY(bw->location);
1671 name = gtk_file_selection_get_filename(choosefile);
1672 url = a_Url_dup(a_History_get_url(NAV_TOP(bw)));
1673
1674 if ( strlen(name) && (out = fopen(name, "w")) != NULL ) {
1675 DilloWeb *Web = a_Web_new(url);
1676 Web->bw = bw;
1677 Web->stream = out;
1678 Web->flags |= WEB_Download;
1679 /* todo: keep track of this client */
1680 a_Capi_open_url(Web, Interface_save_callback, Web);
1681
1682 g_free(save_dialog_last_dirname);
1683 save_dialog_last_dirname = g_strdup(name);
1684 }
1685 a_Url_free(url);
1686
1687 gtk_widget_destroy(bw->save_dialog_window);
1688 }
1689
1690 /*
1691 * Save the link-URL to a local file
1692 */
1693 static void Interface_file_save_link(GtkWidget *widget, BrowserWindow *bw)
1694 {
1695 const gchar *name;
1696 const DilloUrl *url;
1697 gchar *cmd, *buf;
1698 FILE *out;
1699 gint buf_size;
1700
1701 name = gtk_file_selection_get_filename(
1702 GTK_FILE_SELECTION(bw->save_link_dialog_window));
1703 url = a_Menu_popup_get_url(bw);
1704
1705 g_free(save_dialog_last_dirname);
1706 save_dialog_last_dirname = g_strdup(name);
1707
1708 if (!a_Capi_get_buf(url, &buf, &buf_size)) {
1709 /* Not cached, ask the downloads server to get it */
1710 cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s",
1711 "download", URL_STR(url), name);
1712 a_Capi_dpi_send_cmd(NULL, bw, cmd, "downloads", 1);
1713 g_free(cmd);
1714
1715 } else {
1716 /* Cached! Save from the cache */
1717 if ( strlen(name) && (out = fopen(name, "w")) != NULL ) {
1718 DilloWeb *Web = a_Web_new(url);
1719 Web->bw = bw;
1720 Web->stream = out;
1721 Web->flags |= WEB_Download;
1722 /* todo: keep track of this client */
1723 a_Capi_open_url(Web, Interface_save_callback, Web);
1724 } else
1725 g_printerr("Error trying to save: %s\n", name);
1726 }
1727
1728 gtk_widget_destroy(bw->save_link_dialog_window);
1729 }
1730
1731 /*
1732 * Scan Url and return a local-filename suggestion for saving
1733 */
1734 static char *Interface_make_save_name(const DilloUrl *url)
1735 {
1736 int i;
1737 gchar *FileName, *FilenameWithDir, *o, *n;
1738
1739 if ((FileName = strrchr(URL_PATH(url), '/')))
1740 FileName = g_strndup(FileName + 1, MIN(strlen(FileName + 1), 80));
1741 else
1742 FileName = g_strdup("");
1743
1744 /* Replace %20 and ' ' with '_' in Filename */
1745 o = n = FileName;
1746 for (i = 0; o[i]; i++) {
1747 *n++ = (o[i] == ' ' || (o[i] == '%' && o[i+1] == '2' && o[i+2] == '0')) ?
1748 i+=2, '_' : o[i];
1749 }
1750 *n = 0;
1751
1752 if (save_dialog_last_dirname) {
1753 if (*FileName) {
1754 gchar *dirpart = g_dirname(save_dialog_last_dirname);
1755 FilenameWithDir =
1756 g_strconcat(dirpart, G_DIR_SEPARATOR_S, FileName, NULL);
1757 g_free(dirpart);
1758 } else {
1759 FilenameWithDir = g_strdup(save_dialog_last_dirname);
1760 }
1761 g_free(FileName);
1762 return FilenameWithDir;
1763 } else
1764 return FileName;
1765 }
1766
1767 /*
1768 * Show the dialog interface for saving an URL
1769 */
1770 void a_Interface_save_dialog(GtkWidget *widget, BrowserWindow *bw)
1771 {
1772 gchar *SuggestedName; /* Suggested save name */
1773 DilloUrl* url;
1774
1775 if (!bw->save_dialog_window) {
1776 Interface_make_choose_file_dialog(
1777 &bw->save_dialog_window,
1778 "save_dialog", "Dillo", "Dillo: Save URL as File...",
1779 (GtkSignalFunc) Interface_file_save_url, (void *)bw );
1780 }
1781 url = a_Url_new(a_Interface_get_location_text(bw), NULL, 0, 0, 0);
1782 SuggestedName = Interface_make_save_name(url);
1783 gtk_file_selection_set_filename(
1784 GTK_FILE_SELECTION(bw->save_dialog_window), SuggestedName);
1785 g_free(SuggestedName);
1786 a_Url_free(url);
1787
1788 if (!GTK_WIDGET_VISIBLE(bw->save_dialog_window))
1789 gtk_widget_show(bw->save_dialog_window);
1790 else
1791 gdk_window_raise(bw->save_dialog_window->window);
1792 }
1793
1794 /*
1795 * Show the dialog interface for saving a link
1796 */
1797 void a_Interface_save_link_dialog(GtkWidget *widget, BrowserWindow *bw)
1798 {
1799 char *SuggestedName; /* Suggested save name */
1800
1801 if (!bw->save_link_dialog_window) {
1802 Interface_make_choose_file_dialog(
1803 &bw->save_link_dialog_window,
1804 "save_link_dialog", "Dillo",
1805 "Dillo: Save link as File...",
1806 (GtkSignalFunc) Interface_file_save_link,
1807 (void *)bw);
1808 }
1809 SuggestedName = Interface_make_save_name(a_Menu_popup_get_url(bw));
1810 gtk_file_selection_set_filename(
1811 GTK_FILE_SELECTION(bw->save_link_dialog_window), SuggestedName);
1812 g_free(SuggestedName);
1813
1814 if (!GTK_WIDGET_VISIBLE(bw->save_link_dialog_window))
1815 gtk_widget_show(bw->save_link_dialog_window);
1816 else
1817 gdk_window_raise(bw->save_link_dialog_window->window);
1818 }
1819
1820 /*
1821 * Offer the "Save Link As..." dialog for an unhandled MIME type URL.
1822 */
1823 void a_Interface_offer_link_download(BrowserWindow *bw, const DilloUrl *url)
1824 {
1825 a_Menu_popup_set_url(bw, url);
1826 a_Interface_save_link_dialog(NULL, bw);
1827 }
1828
1829 /*
1830 * Scroll to an occurence of a string in the open page
1831 */
1832 static void Interface_entry_search(GtkWidget *widget, BrowserWindow* bw)
1833 {
1834 char *string;
1835 gboolean case_sens;
1836
1837 string = gtk_editable_get_chars(GTK_EDITABLE(bw->findtext_dialog_entry),
1838 0, -1);
1839 case_sens = gtk_toggle_button_get_active
1840 (GTK_TOGGLE_BUTTON(bw->findtext_dialog_check));
1841 switch(a_Dw_gtk_scrolled_window_search(GTK_DW_SCROLLED_WINDOW(bw->docwin),
1842 string, case_sens)) {
1843 case FINDTEXT_RESTART:
1844 a_Interface_message_window("Dillo: Find text",
1845 "No further occurence of \"%s\". "
1846 "Restarting from the beginning.", string);
1847 break;
1848 case FINDTEXT_NOT_FOUND:
1849 a_Interface_message_window("Dillo: Find text",
1850 "Cannot find \"%s\".", string);
1851 break;
1852 }
1853 g_free(string);
1854 }
1855
1856 /*
1857 * Show the dialog interface for finding text in a page
1858 */
1859 void a_Interface_findtext_dialog(BrowserWindow *bw)
1860 {
1861 if (!bw->findtext_dialog_window) {
1862 Interface_make_dialog(&(bw->findtext_dialog_window),
1863 "findtext_dialog", "Dillo", "Dillo: Find text in page",
1864 &(bw->findtext_dialog_entry), "", 1,
1865 &(bw->findtext_dialog_check), "Case sensitive",
1866 "Find", (GtkSignalFunc) Interface_entry_search, (void *)bw);
1867 if (prefs.transient_dialogs)
1868 gtk_window_set_transient_for(GTK_WINDOW(bw->findtext_dialog_window),
1869 GTK_WINDOW(bw->main_window));
1870 gtk_signal_connect_object
1871 (GTK_OBJECT(bw->findtext_dialog_window), "destroy",
1872 (GtkSignalFunc) a_Dw_gtk_scrolled_window_reset_search,
1873 (void*)bw->docwin);
1874 gtk_window_set_position(GTK_WINDOW(bw->findtext_dialog_window),
1875 GTK_WIN_POS_NONE);
1876 }
1877
1878 a_Interface_set_nice_window_pos(bw->findtext_dialog_window,
1879 bw->main_window);
1880
1881 if (!GTK_WIDGET_VISIBLE(bw->findtext_dialog_window))
1882 gtk_widget_show(bw->findtext_dialog_window);
1883 else
1884 gdk_window_raise(bw->findtext_dialog_window->window);
1885 }
1886
1887 /*
1888 * Use the search dialog's entry to feed a web search engine.
1889 */
1890 static void Interface_search_callback(GtkWidget *widget, BrowserWindow *bw)
1891 {
1892 gchar *keyw, *url_str;
1893
1894 keyw = gtk_editable_get_chars(GTK_EDITABLE(bw->search_dialog_entry), 0, -1);
1895 if (keyw) {
1896 url_str = Interface_make_search_url(keyw);
1897 Interface_open_url_string(url_str, bw);
1898 g_free(url_str);
1899 g_free(keyw);
1900 }
1901 if (bw->search_dialog_window != NULL)
1902 gtk_widget_hide(bw->search_dialog_window);
1903 }
1904
1905 /*
1906 * Show the dialog interface for web search engine.
1907 */
1908 void a_Interface_search_dialog(GtkWidget *widget, BrowserWindow *bw)
1909 {
1910 if (!bw->search_dialog_window) {
1911 Interface_make_dialog(&(bw->search_dialog_window),
1912 "search_dialog", "Dillo", "Dillo: Search the Web",
1913 &(bw->search_dialog_entry), "", 1, NULL, NULL,
1914 "Search", (GtkSignalFunc)Interface_search_callback, (void *)bw);
1915 if (prefs.transient_dialogs)
1916 gtk_window_set_transient_for(GTK_WINDOW(bw->search_dialog_window),
1917 GTK_WINDOW(bw->main_window));
1918 }
1919
1920 if (!GTK_WIDGET_VISIBLE(bw->search_dialog_window))
1921 gtk_widget_show(bw->search_dialog_window);
1922 else
1923 gdk_window_raise(bw->search_dialog_window->window);
1924 }
1925
1926 /*
1927 * Get and activate a proxy password.
1928 */
1929 static void Interface_entry_proxy_passwd(GtkWidget *widget, BrowserWindow *bw)
1930 {
1931 gchar *text;
1932
1933 text = gtk_editable_get_chars(GTK_EDITABLE(bw->proxy_passwd_dialog_entry),
1934 0, -1);
1935 a_Http_set_proxy_passwd(text);
1936 g_free(text);
1937
1938 if (bw->proxy_passwd_dialog_window != NULL)
1939 gtk_widget_destroy(bw->proxy_passwd_dialog_window);
1940 }
1941
1942 /*
1943 * Show the dialog interface for asking proxy password.
1944 */
1945 void a_Interface_proxy_passwd_dialog(BrowserWindow *bw)
1946 {
1947 GString *buf = g_string_new("");
1948
1949 g_string_sprintf(buf, "Dillo: Enter proxy password for '%s'",
1950 prefs.http_proxyuser);
1951
1952 if (!bw->proxy_passwd_dialog_window) {
1953 Interface_make_dialog(&(bw->proxy_passwd_dialog_window),
1954 "proxy_passwd_dialog", "Dillo", buf->str,
1955 &(bw->proxy_passwd_dialog_entry), "", 0, NULL, NULL,
1956 "OK", (GtkSignalFunc)Interface_entry_proxy_passwd, (void *)bw);
1957 if (prefs.transient_dialogs)
1958 gtk_window_set_transient_for(
1959 GTK_WINDOW(bw->proxy_passwd_dialog_window),
1960 GTK_WINDOW(bw->main_window));
1961 gtk_window_set_modal(GTK_WINDOW(bw->proxy_passwd_dialog_window), TRUE);
1962 }
1963
1964 if (!GTK_WIDGET_VISIBLE(bw->proxy_passwd_dialog_window))
1965 gtk_widget_show(bw->proxy_passwd_dialog_window);
1966
1967 g_string_free(buf, TRUE);
1968 }
1969
1970 /*
1971 * This signal callback adjusts the position of a menu.
1972 * It's useful for very long menus.
1973 */
1974 void a_Interface_scroll_popup(GtkWidget *widget)
1975 {
1976 /*
1977 * todo:
1978 * 1) Scrolling menues should rather be the task of Gtk+. This is
1979 * a hack, and I don't know if it does not break anything.
1980 * 2) It could be improved, e.g. a timeout could be added for
1981 * better mouse navigation.
1982 */
1983 int y, h, mx, my, sh;
1984
1985 y = widget->allocation.y;
1986 h = widget->allocation.height;
1987 gdk_window_get_geometry (widget->parent->parent->window,
1988 &mx, &my, NULL, NULL, NULL);
1989 sh = gdk_screen_height ();
1990
1991 if (y + my < 0)
1992 gdk_window_move (widget->parent->parent->window, mx, - y + 1);
1993 else if (y + my > sh - h)
1994 gdk_window_move (widget->parent->parent->window, mx, sh - h - y - 1);
1995 }
1996
1997 /*
1998 * A general purpose message window.
1999 */
2000 void a_Interface_message_window(const char *title, const char *format, ... )
2001 {
2002 GtkAccelGroup *accel_group;
2003 GtkWidget *window, *frame, *label, *button, *box;
2004 static char msg[1024];
2005 va_list argp;
2006
2007 va_start(argp, format);
2008 g_vsnprintf(msg, 1024, format, argp);
2009 va_end(argp);
2010
2011 window = gtk_window_new(GTK_WINDOW_DIALOG);
2012 gtk_window_set_wmclass(GTK_WINDOW(window), "question_dialog", "dillo");
2013 gtk_window_set_title(GTK_WINDOW(window), title);
2014 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
2015 gtk_container_border_width(GTK_CONTAINER(window), 10);
2016 gtk_signal_connect_object(GTK_OBJECT(window), "delete_event",
2017 (GtkSignalFunc)gtk_widget_destroy, (void*)window);
2018
2019 /* accel_group for the message window */
2020 accel_group = gtk_accel_group_new();
2021 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
2022
2023 box = gtk_vbox_new(FALSE, 5);
2024 frame = gtk_frame_new(NULL);
2025 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
2026 label = gtk_label_new(msg);
2027 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_FILL);
2028 gtk_misc_set_padding(GTK_MISC(label), 20, 20);
2029 gtk_container_add(GTK_CONTAINER(frame), label);
2030 gtk_widget_show(label);
2031 gtk_widget_show(frame);
2032 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2033
2034 button = gtk_button_new_with_label("Close");
2035 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
2036 (GtkSignalFunc)gtk_widget_destroy, (void*)window);
2037 gtk_widget_add_accelerator(button, "clicked",
2038 accel_group, GDK_Escape, 0, GTK_ACCEL_LOCKED);
2039 gtk_widget_show(button);
2040 gtk_box_pack_start(GTK_BOX(box), button, FALSE, TRUE, 0);
2041 gtk_widget_show(button);
2042 gtk_container_add(GTK_CONTAINER(window), box);
2043
2044 gtk_widget_show(box);
2045 gtk_widget_grab_focus(button);
2046 gtk_widget_show(window);
2047 }
2048
2049 /*
2050 * A general purpose window for long text display.
2051 */
2052 void a_Interface_text_window (GtkWidget **text_widget,
2053 gchar *title, gchar *wm_class,
2054 gchar *buf, gint buf_size,
2055 gint xsize_max, gint ysize_max)
2056 {
2057 gint xsize, ysize;
2058 GtkWidget *window, *box1, *button, *scrolled_window, *text;
2059
2060 if (*text_widget)
2061 gtk_widget_destroy (*text_widget);
2062
2063 /* -RL :: This code is adapted from testgtk. */
2064 if ( !*text_widget ) {
2065 window = *text_widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2066 gtk_window_set_wmclass(GTK_WINDOW(window), wm_class, "Dillo");
2067 gtk_widget_set_name (window, "text window");
2068 xsize = (prefs.width < xsize_max) ? prefs.width : xsize_max;
2069 ysize = (prefs.height < ysize_max) ? prefs.height : ysize_max;
2070 gtk_widget_set_usize (window, xsize, ysize);
2071 gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE);
2072
2073 gtk_signal_connect (GTK_OBJECT (window), "destroy",
2074 GTK_SIGNAL_FUNC(gtk_widget_destroyed),
2075 text_widget);
2076
2077 gtk_window_set_title (GTK_WINDOW (window), title);
2078 gtk_container_border_width (GTK_CONTAINER (window), 0);
2079
2080 box1 = gtk_vbox_new (FALSE, 0);
2081 gtk_container_add (GTK_CONTAINER (window), box1);
2082 gtk_widget_show (box1);
2083
2084 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2085 gtk_box_pack_start (GTK_BOX (box1), scrolled_window, TRUE, TRUE, 0);
2086 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
2087 GTK_POLICY_NEVER,
2088 GTK_POLICY_ALWAYS);
2089 gtk_widget_show (scrolled_window);
2090
2091 text = gtk_text_new (NULL, NULL);
2092 gtk_text_set_editable (GTK_TEXT (text), FALSE);
2093 gtk_container_add (GTK_CONTAINER (scrolled_window), text);
2094 gtk_widget_show (text);
2095
2096 gtk_text_freeze (GTK_TEXT (text));
2097 gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, buf, buf_size);
2098 gtk_text_thaw (GTK_TEXT (text));
2099
2100 button = gtk_button_new_with_label ("close");
2101 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
2102 GTK_SIGNAL_FUNC(gtk_widget_destroy),
2103 GTK_OBJECT (window));
2104 gtk_box_pack_start (GTK_BOX (box1), button, FALSE, FALSE, 0);
2105 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
2106 gtk_widget_grab_default (button);
2107 gtk_widget_show (button);
2108 }
2109
2110 if (!GTK_WIDGET_VISIBLE (*text_widget))
2111 gtk_widget_show (*text_widget);
2112 }
2113
2114 /*
2115 * Places win1 in a way that it does not, or as less as possible, cover win2.
2116 */
2117 void a_Interface_set_nice_window_pos(GtkWidget *win1, GtkWidget *win2)
2118 {
2119 gint w1, h1, x2, y2, w2, h2, sw, sh, sr, sl, sb, st, max;
2120
2121 gtk_widget_realize(win1);
2122 gdk_window_get_geometry(win1->window, NULL, NULL, &w1, &h1, NULL);
2123 gdk_window_get_origin(win2->window, &x2, &y2);
2124 gdk_window_get_geometry(win2->window, NULL, NULL, &w2, &h2, NULL);
2125 sw = gdk_screen_width();
2126 sh = gdk_screen_height();
2127
2128 /* space (excluding win1 space) at right, left, bottom and top */
2129 sr = sw - (x2 + w2 + w1);
2130 sl = x2 - w1;
2131 sb = sh - (y2 + h2 + h1);
2132 st = y2 - h1;
2133
2134 /* First, we test, whether win1 can be placed so that it does not
2135 * covor win2. */
2136 if (sr >= 0)
2137 gtk_widget_set_uposition(win1, x2 + w2, (sh - h1) / 2);
2138 else if (sl >= 0)
2139 gtk_widget_set_uposition(win1, x2 - w1, (sh - h1) / 2);
2140 else if (sb >= 0)
2141 gtk_widget_set_uposition(win1, (sh - h1) / 2, y2 + h2);
2142 else if (st >= 0)
2143 gtk_widget_set_uposition(win1, (sh - h1) / 2, y2 - h1);
2144 else {
2145 /* Second, we search for the position where the covered space
2146 * is (more or less) minimized. */
2147 max = MAX(MAX(sr, sl), MAX(sb, st));
2148 if (sr == max)
2149 gtk_widget_set_uposition(win1, sw - w1, (sh - h1) / 2);
2150 else if (sl == max)
2151 gtk_widget_set_uposition(win1, 0, (sh - h1) / 2);
2152 else if (sb == max)
2153 gtk_widget_set_uposition(win1, (sh - h1) / 2, sh - h1);
2154 else
2155 gtk_widget_set_uposition(win1, (sh - h1) / 2, 0);
2156 }
2157 }
0 #ifndef __INTERFACE_H__
1 #define __INTERFACE_H__
2
3 #include "browser.h"
4
5 /*
6 * This struct is used with custom dialogs.
7 * It can be extended to provide extra widgets (e.g. check buttons).
8 *
9 */
10 typedef struct _DialogAnswer DialogAnswer;
11 struct _DialogAnswer {
12 BrowserWindow *bw;
13 gint alt_num;
14 char *alt_str;
15 DialogAnswer *this;
16 };
17
18
19 void a_Interface_init(void);
20 void a_Interface_stop(BrowserWindow *bw);
21 void a_Interface_clean(BrowserWindow *bw);
22 void a_Interface_quit_all(void);
23
24 void a_Interface_add_client(BrowserWindow *bw, gint Key, gint Root);
25 void a_Interface_remove_client(BrowserWindow *bw, gint ClientKey);
26 void a_Interface_add_url(BrowserWindow *bw, const DilloUrl *Url, gint Flags);
27 void a_Interface_close_client(BrowserWindow *bw, gint ClientKey);
28
29 void a_Interface_msg(BrowserWindow *bw, const char *format, ... );
30 void a_Interface_bug_meter_update(BrowserWindow *bw, gint num_err);
31
32 void a_Interface_openfile_dialog(BrowserWindow *bw);
33 void a_Interface_open_dialog(GtkWidget *widget, BrowserWindow *bw);
34 void a_Interface_save_dialog(GtkWidget *widget, BrowserWindow *bw);
35 void a_Interface_save_link_dialog(GtkWidget *widget, BrowserWindow *bw);
36 void a_Interface_offer_link_download(BrowserWindow *bw, const DilloUrl *url);
37 void a_Interface_search_dialog(GtkWidget *widget, BrowserWindow *bw);
38 void a_Interface_findtext_dialog(BrowserWindow *bw);
39 void a_Interface_proxy_passwd_dialog(BrowserWindow *bw);
40 void a_Interface_quit_dialog(BrowserWindow *bw);
41
42 void a_Interface_set_page_title(BrowserWindow *bw, char *title);
43 void a_Interface_set_location_text(BrowserWindow *bw, char *text);
44 gchar *a_Interface_get_location_text(BrowserWindow *bw);
45 void a_Interface_reset_progress_bars(BrowserWindow *bw);
46 void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw);
47 void a_Interface_set_cursor(BrowserWindow *bw, GdkCursorType CursorType);
48 BrowserWindow *
49 a_Interface_browser_window_new(gint width, gint height, guint32 xid);
50
51 void a_Interface_set_button_sens(BrowserWindow *bw);
52
53 void a_Interface_scroll_popup(GtkWidget *widget);
54
55 void a_Interface_question_dialog(
56 BrowserWindow *bw, gchar *QuestionTxt, gint modal_flag,
57 char *alt1, char *alt2, char *alt3, char *alt4, char *alt5,
58 GtkSignalFunc AnswerCallback);
59 void a_Interface_message_window(const char *title, const char *format, ... );
60 void a_Interface_text_window (GtkWidget **text_widget,
61 gchar *title, gchar *wm_class,
62 gchar *buf, gint buf_size,
63 gint xsize_max, gint ysize_max);
64
65 void a_Interface_set_nice_window_pos(GtkWidget *win1, GtkWidget *win2);
66
67
68 #endif /* __INTERFACE_H__ */
0 /*
1 * File: jpeg.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 * 2000 Jorge Arellano Cid <jcid@users.sourceforge.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * The jpeg decoder for dillo. It is responsible for decoding JPEG data
15 * and transferring it to the dicache. It uses libjpeg to do the actual
16 * decoding.
17 */
18
19 #include <config.h>
20 #ifdef ENABLE_JPEG
21
22 #include <stdio.h>
23 #include <gtk/gtk.h>
24 #include <setjmp.h>
25
26 /* avoid a redefinition of HAVE_STDLIB_H with old jpeglib.h */
27 #ifdef HAVE_STDLIB_H
28 # undef HAVE_STDLIB_H
29 #endif
30 #include <jpeglib.h>
31
32 #include "image.h"
33 #include "web.h"
34 #include "cache.h"
35 #include "dicache.h"
36
37 #define DEBUG_LEVEL 6
38 #include "debug.h"
39
40 typedef enum {
41 DILLO_JPEG_INIT,
42 DILLO_JPEG_STARTING,
43 DILLO_JPEG_READING,
44 DILLO_JPEG_DONE,
45 DILLO_JPEG_ERROR
46 } DilloJpegState;
47
48 /* An implementation of a suspending source manager */
49
50 typedef struct {
51 struct jpeg_source_mgr pub; /* public fields */
52 struct DilloJpeg *jpeg; /* a pointer back to the jpeg object */
53 } my_source_mgr;
54
55 struct my_error_mgr {
56 struct jpeg_error_mgr pub; /* "public" fields */
57 jmp_buf setjmp_buffer; /* for return to caller */
58 };
59 typedef struct my_error_mgr * my_error_ptr;
60
61 typedef struct DilloJpeg {
62 DilloImage *Image;
63 DilloUrl *url;
64 gint version;
65
66 my_source_mgr Src;
67
68 DilloJpegState state;
69 size_t Start_Ofs, Skip, NewStart;
70 char *Data;
71
72 guint y;
73
74 struct jpeg_decompress_struct cinfo;
75 struct my_error_mgr jerr;
76 } DilloJpeg;
77
78 /*
79 * Forward declarations
80 */
81 static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, gint version);
82 static void Jpeg_callback(int Op, CacheClient_t *Client);
83 static void Jpeg_write(DilloJpeg *jpeg, void *Buf, guint BufSize);
84 static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client);
85 METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo);
86
87 /* exported function */
88 DwWidget *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
89 void **Data);
90
91
92 /* this is the routine called by libjpeg when it detects an error. */
93 METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo)
94 {
95 /* display message and return to setjmp buffer */
96 my_error_ptr myerr = (my_error_ptr) cinfo->err;
97 (*cinfo->err->output_message) (cinfo);
98 longjmp(myerr->setjmp_buffer, 1);
99 }
100
101 /*
102 * MIME handler for "image/jpeg" type
103 * (Sets Jpeg_callback or a_Dicache_callback as the cache-client)
104 */
105 DwWidget *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
106 void **Data)
107 {
108 DilloWeb *web = P;
109 DICacheEntry *DicEntry;
110
111 if ( !web->Image )
112 web->Image = a_Image_new(0, 0, NULL, 0);
113
114 /* Add an extra reference to the Image (for dicache usage) */
115 a_Image_ref(web->Image);
116
117 DicEntry = a_Dicache_get_entry(web->url);
118 if ( !DicEntry ) {
119 /* Let's create an entry for this image... */
120 DicEntry = a_Dicache_add_entry(web->url);
121
122 /* ... and let the decoder feed it! */
123 *Data = Jpeg_new(web->Image, DicEntry->url, DicEntry->version);
124 *Call = (CA_Callback_t) Jpeg_callback;
125 } else {
126 /* Let's feed our client from the dicache */
127 a_Dicache_ref(DicEntry->url, DicEntry->version);
128 *Data = web->Image;
129 *Call = (CA_Callback_t) a_Dicache_callback;
130 }
131 return DW_WIDGET (web->Image->dw);
132 }
133
134 /*
135 * Finish the decoding process
136 */
137 static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
138 {
139 a_Dicache_close(jpeg->url, jpeg->version, Client);
140
141 if (jpeg->state != DILLO_JPEG_DONE) {
142 jpeg_destroy_decompress(&(jpeg->cinfo));
143 }
144 g_free(jpeg);
145 }
146
147 static void init_source(j_decompress_ptr cinfo)
148 {
149 }
150
151 static boolean fill_input_buffer(j_decompress_ptr cinfo)
152 {
153 DilloJpeg *jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
154
155 DEBUG_MSG(5, "fill_input_buffer\n");
156 #if 0
157 if (!cinfo->src->bytes_in_buffer) {
158 DEBUG_MSG(5, "fill_input_buffer: %ld bytes in buffer\n",
159 (glong)cinfo->src->bytes_in_buffer);
160
161 jpeg->Start_Ofs = (gulong) jpeg->cinfo.src->next_input_byte -
162 (gulong) jpeg->Data;
163 #endif
164 if (jpeg->Skip) {
165 jpeg->Start_Ofs = jpeg->NewStart + jpeg->Skip - 1;
166 jpeg->Skip = 0;
167 } else {
168 jpeg->Start_Ofs = (gulong) jpeg->cinfo.src->next_input_byte -
169 (gulong) jpeg->Data;
170 }
171 return FALSE;
172 #if 0
173 }
174 return TRUE;
175 #endif
176 }
177
178 static void skip_input_data(j_decompress_ptr cinfo, glong num_bytes)
179 {
180 DilloJpeg *jpeg;
181
182 if (num_bytes < 1)
183 return;
184 jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
185
186 DEBUG_MSG(5, "skip_input_data: Start_Ofs = %lu, num_bytes = %ld,"
187 " %ld bytes in buffer\n",
188 (gulong)jpeg->Start_Ofs, num_bytes,
189 (glong)cinfo->src->bytes_in_buffer);
190
191 cinfo->src->next_input_byte += num_bytes;
192 if (num_bytes < (glong)cinfo->src->bytes_in_buffer) {
193 cinfo->src->bytes_in_buffer -= num_bytes;
194 } else {
195 jpeg->Skip += num_bytes - cinfo->src->bytes_in_buffer + 1;
196 cinfo->src->bytes_in_buffer = 0;
197 }
198 }
199
200 static void term_source(j_decompress_ptr cinfo)
201 {
202 }
203
204 static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, gint version)
205 {
206 my_source_mgr *src;
207 DilloJpeg *jpeg = g_malloc(sizeof(*jpeg));
208
209 jpeg->Image = Image;
210 jpeg->url = url;
211 jpeg->version = version;
212
213 jpeg->state = DILLO_JPEG_INIT;
214 jpeg->Start_Ofs = 0;
215 jpeg->Skip = 0;
216
217 /* decompression step 1 (see libjpeg.doc) */
218 jpeg->cinfo.err = jpeg_std_error(&(jpeg->jerr.pub));
219 jpeg->jerr.pub.error_exit = Jpeg_errorexit;
220
221 jpeg_create_decompress(&(jpeg->cinfo));
222
223 /* decompression step 2 (see libjpeg.doc) */
224 jpeg->cinfo.src = &jpeg->Src.pub;
225 src = &jpeg->Src;
226 src->pub.init_source = init_source;
227 src->pub.fill_input_buffer = fill_input_buffer;
228 src->pub.skip_input_data = skip_input_data;
229 src->pub.resync_to_restart = jpeg_resync_to_restart;/* use default method */
230 src->pub.term_source = term_source;
231 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
232 src->pub.next_input_byte = NULL;/* until buffer loaded */
233
234 src->jpeg = jpeg;
235
236 /* decompression steps continue in write method */
237 return jpeg;
238 }
239
240 static void Jpeg_callback(int Op, CacheClient_t *Client)
241 {
242 if (Op)
243 Jpeg_close(Client->CbData, Client);
244 else
245 Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
246 }
247
248 /*
249 * Receive and process new chunks of JPEG image data
250 */
251 static void Jpeg_write(DilloJpeg *jpeg, void *Buf, guint BufSize)
252 {
253 DilloImgType type;
254 guchar *linebuf;
255 JSAMPLE *array[1];
256 gint num_read;
257
258 DEBUG_MSG(5, "Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg,
259 (glong) BufSize, (gulong)jpeg->Start_Ofs);
260
261 /* See if we are supposed to skip ahead. */
262 if (BufSize <= jpeg->Start_Ofs)
263 return;
264
265 /* Concatenate with the partial input, if any. */
266 jpeg->cinfo.src->next_input_byte = (guchar *)Buf + jpeg->Start_Ofs;
267 jpeg->cinfo.src->bytes_in_buffer = BufSize - jpeg->Start_Ofs;
268 jpeg->NewStart = BufSize;
269 jpeg->Data = Buf;
270
271 if (setjmp(jpeg->jerr.setjmp_buffer)) {
272 /* If we get here, the JPEG code has signaled an error. */
273 jpeg->state = DILLO_JPEG_ERROR;
274 }
275
276 /* Process the bytes in the input buffer. */
277 if (jpeg->state == DILLO_JPEG_INIT) {
278
279 /* decompression step 3 (see libjpeg.doc) */
280 if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) {
281 type = DILLO_IMG_TYPE_GRAY;
282 if (jpeg->cinfo.num_components == 1)
283 type = DILLO_IMG_TYPE_GRAY;
284 else if (jpeg->cinfo.num_components == 3)
285 type = DILLO_IMG_TYPE_RGB;
286 else
287 DEBUG_MSG(5, "jpeg: can't handle %d component images\n",
288 jpeg->cinfo.num_components);
289 a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
290 (guint)jpeg->cinfo.image_width,
291 (guint)jpeg->cinfo.image_height,
292 type);
293
294 /* decompression step 4 (see libjpeg.doc) */
295 jpeg->state = DILLO_JPEG_STARTING;
296 }
297 }
298 if (jpeg->state == DILLO_JPEG_STARTING) {
299 /* decompression step 5 (see libjpeg.doc) */
300 if (jpeg_start_decompress(&(jpeg->cinfo))) {
301 jpeg->y = 0;
302 jpeg->state = DILLO_JPEG_READING;
303 }
304 }
305 if (jpeg->state == DILLO_JPEG_READING) {
306 linebuf = g_malloc(jpeg->cinfo.image_width *
307 jpeg->cinfo.num_components);
308 array[0] = linebuf;
309 while (jpeg->y < jpeg->cinfo.image_height) {
310 num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1);
311 if (num_read == 0)
312 break;
313 a_Dicache_write(jpeg->Image, jpeg->url, jpeg->version,
314 linebuf, 0, jpeg->y);
315
316 jpeg->y++;
317 }
318 if (jpeg->y == jpeg->cinfo.image_height) {
319 DEBUG_MSG(5, "height achieved\n");
320
321 jpeg_destroy_decompress(&(jpeg->cinfo));
322 jpeg->state = DILLO_JPEG_DONE;
323 }
324 g_free(linebuf);
325 }
326 if (jpeg->state == DILLO_JPEG_ERROR) {
327 jpeg_destroy_decompress(&(jpeg->cinfo));
328 jpeg->state = DILLO_JPEG_DONE;
329 }
330 }
331
332 #endif /* ENABLE_JPEG */
0 /*
1 * File: klist.c
2 *
3 * Copyright 2001 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * A simple ADT for Key-Data pairs
13 *
14 * NOTE: this ADT is not perfect. The possibility of holding a Key, after
15 * its data has been removed, long enough for the key-counter to reset and
16 * reuse the same key is very low, but EXISTS. So, the responsibility
17 * remains with the caller.
18 */
19
20 #include "klist.h"
21
22
23 /*
24 * Compare function for searching data by its key
25 */
26 static gint Klist_key_cmp(gconstpointer Node, gconstpointer key)
27 {
28 return ( GPOINTER_TO_INT(key) != ((KlistNode_t *)Node)->Key );
29 }
30
31 /*
32 * Return the data pointer for a given Key (or NULL if not found)
33 */
34 gpointer a_Klist_get_data(Klist_t *Klist, gint Key)
35 {
36 GSList *list;
37
38 if (!Klist)
39 return NULL;
40 list= g_slist_find_custom(Klist->List, GINT_TO_POINTER(Key), Klist_key_cmp);
41 return (list) ? ((KlistNode_t *)list->data)->Data : NULL;
42 }
43
44 /*
45 * Insert a data pointer and return a key for it.
46 */
47 gint a_Klist_insert(Klist_t **Klist, gpointer Data)
48 {
49 KlistNode_t *Node;
50
51 if (!*Klist) {
52 (*Klist) = g_new(Klist_t, 1);
53 (*Klist)->List = NULL;
54 (*Klist)->Clean = 1;
55 (*Klist)->Counter = 0;
56 }
57
58 /* This avoids repeated keys at the same time */
59 do {
60 if ( ++((*Klist)->Counter) == 0 ) {
61 (*Klist)->Counter = 1;
62 (*Klist)->Clean = 0;
63 }
64 } while (!((*Klist)->Clean) &&
65 a_Klist_get_data((*Klist), (*Klist)->Counter));
66
67 Node = g_new(KlistNode_t, 1);
68 Node->Key = (*Klist)->Counter;
69 Node->Data = Data;
70 (*Klist)->List = g_slist_prepend((*Klist)->List, Node);
71
72 return (*Klist)->Counter;
73 }
74
75 /*
76 * Remove data by Key
77 */
78 void a_Klist_remove(Klist_t *Klist, gint Key)
79 {
80 GSList *list;
81
82 list= g_slist_find_custom(Klist->List, GINT_TO_POINTER(Key), Klist_key_cmp);
83 if (list) {
84 g_free(list->data);
85 Klist->List = g_slist_remove(Klist->List, list->data);
86 }
87 if (Klist->List == NULL)
88 Klist->Clean = 1;
89 }
90
91 /*
92 * Free a Klist
93 */
94 void a_Klist_free(Klist_t **KlistPtr)
95 {
96 gpointer node;
97 Klist_t *Klist = *KlistPtr;
98
99 if (!Klist)
100 return;
101
102 while (Klist && Klist->List) {
103 node = Klist->List->data;
104 Klist->List = g_slist_remove(Klist->List, node);
105 g_free(node);
106 }
107 g_free(Klist);
108 *KlistPtr = NULL;
109 }
110
0 #ifndef __KLIST_H__
1 #define __KLIST_H__
2
3 #include <glib.h>
4
5
6 typedef struct _KlistNode KlistNode_t;
7 typedef struct _Klist Klist_t;
8
9 struct _KlistNode {
10 gint Key; /* primary key */
11 gpointer *Data; /* data reference */
12 };
13
14 struct _Klist {
15 GSList *List;
16 gint Clean; /* check flag */
17 gint Counter; /* counter (for making keys) */
18 };
19
20
21 /*
22 * Function prototypes
23 */
24 gpointer a_Klist_get_data(Klist_t *Klist, gint Key);
25 gint a_Klist_insert(Klist_t **Klist, gpointer Data);
26 void a_Klist_remove(Klist_t *Klist, gint Key);
27 void a_Klist_free(Klist_t **Klist);
28
29
30 #endif /* __KLIST_H__ */
0 /*
1 * Fast list methods
2 * Feb 2000 --Jcid
3 *
4 */
5
6 #ifndef __LIST_H__
7 #define __LIST_H__
8
9 /*
10 * a_List_resize()
11 *
12 * Make sure there's space for 'num_items' items within the list
13 * (First, allocate an 'alloc_step' sized chunk, after that, double the
14 * list size --to make it faster)
15 */
16 #define a_List_resize(list,num_items,alloc_step) \
17 if ( !list ) { \
18 list = g_malloc(alloc_step * sizeof((*list))); \
19 } \
20 if ( num_items >= alloc_step ){ \
21 while ( num_items >= alloc_step ) \
22 alloc_step <<= 1; \
23 list = g_realloc(list, alloc_step * sizeof((*list))); \
24 }
25
26
27 /*
28 * a_List_add()
29 *
30 * Make sure there's space for one more item within the list.
31 */
32 #define a_List_add(list,num_items,alloc_step) \
33 a_List_resize(list,num_items,alloc_step)
34
35
36 /*
37 * a_List_remove()
38 *
39 * Quickly remove an item from the list
40 * ==> We preserve relative position, but not the element index <==
41 */
42 #define a_List_remove(list, item, num_items) \
43 if ( list && item < num_items ) { \
44 list[item] = list[--num_items]; \
45 }
46
47
48 #endif /* __LIST_H__ */
0 /*
1 * Dillo
2 *
3 * Some code copied from:
4 * The GIMP -- an image manipulation program
5 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdkkeysyms.h>
27 #include "history.h"
28 #include "nav.h"
29 #include "commands.h"
30 #include "dw_gtk_scrolled_window.h"
31 #include "dw_page.h" /* for a_Dw_page_add_anchor */
32 #include "bookmark.h"
33 #include "interface.h"
34 #include "menu.h"
35 #include "selection.h"
36 #include "gtk_ext_menu.h"
37 #include "gtk_ext_menu_item.h"
38 #include "gtk_menu_title.h"
39
40 /*
41 * Forward declarations
42 */
43 static void Menu_pagemarks_goto_pagemark(GtkWidget *widget,
44 gpointer client_data);
45
46 /*
47 * Make a new menu, insert it into the menu bar, and return it.
48 */
49 static GtkWidget* Menu_new(GtkWidget *menubar, const char *name,
50 gboolean right_justify, BrowserWindow *bw)
51 {
52 GtkWidget *menu;
53 GtkWidget *menuitem;
54 guint tmp_key;
55
56 menu = gtk_menu_new();
57 menuitem = gtk_menu_item_new_with_label((char *) name);
58 tmp_key = gtk_label_parse_uline(GTK_LABEL(GTK_BIN(menuitem)->child), name);
59 gtk_widget_add_accelerator(menuitem, "activate_item", bw->accel_group,
60 tmp_key, GDK_MOD1_MASK, 0);
61
62 if ( right_justify )
63 gtk_menu_item_right_justify(GTK_MENU_ITEM(menuitem));
64 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
65 gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
66 gtk_widget_show(menuitem);
67 gtk_menu_set_accel_group(GTK_MENU(menu), bw->accel_group);
68 return menu;
69 }
70
71 /*
72 * Pick the right URL for this popup.
73 */
74 static void Menu_pick_url (GtkWidget *widget, gpointer client_data)
75 {
76 BrowserWindow *bw = (BrowserWindow *)client_data;
77
78 if (gtk_object_get_data(GTK_OBJECT (GTK_MENU (widget->parent)), "url2")) {
79 a_Menu_popup_set_url(bw, bw->menu_popup.url2);
80 }
81 /* clean the url2 tag (currently "over_image" is the only one using it) */
82 gtk_object_remove_data(GTK_OBJECT (bw->menu_popup.over_image), "url2");
83 }
84
85 /*
86 * Add an item to a menu, including the name, an accelerator (not yet
87 * implemented), and a callback function for activation.
88 */
89 static GtkWidget *
90 Menu_add (GtkWidget *menu, const char *name, const char *accel,
91 BrowserWindow *bw,
92 void (*callback) (GtkWidget *widget, void *data), void *data)
93 {
94 GtkWidget *menuitem;
95 GtkAccelGroup *menu_accels;
96 GdkModifierType accel_mods;
97 guint accel_key;
98 guint tmp_key;
99
100 menuitem = gtk_menu_item_new_with_label((char *) name);
101 gtk_menu_append(GTK_MENU(menu), menuitem);
102 menu_accels = gtk_menu_ensure_uline_accel_group(GTK_MENU(menu));
103 tmp_key = gtk_label_parse_uline(GTK_LABEL(GTK_BIN(menuitem)->child), name);
104 gtk_widget_add_accelerator(menuitem, "activate_item",
105 menu_accels, tmp_key, 0, 0);
106 gtk_widget_show(menuitem);
107
108 if (accel != NULL) {
109 gtk_accelerator_parse(accel, &accel_key, &accel_mods);
110 gtk_widget_add_accelerator(menuitem, "activate", bw->accel_group,
111 accel_key, (guint)accel_mods, GTK_ACCEL_VISIBLE);
112 }
113 if (callback != NULL) {
114 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
115 (GtkSignalFunc) Menu_pick_url, bw);
116 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
117 (GtkSignalFunc) callback, data);
118 }
119 return menuitem;
120 }
121
122 /*
123 * Add a menu title.
124 */
125 static GtkWidget *Menu_add_title (GtkWidget *menu, const char *name)
126 {
127 GtkWidget *menuitem;
128
129 menuitem = a_Gtk_menu_title_new(name);
130 gtk_menu_append(GTK_MENU(menu), menuitem);
131 gtk_widget_show(menuitem);
132 return menuitem;
133 }
134
135 /*
136 * Add a separator into the menu.
137 */
138 static void Menu_sep(GtkWidget *menu)
139 {
140 GtkWidget *widget;
141
142 widget = gtk_menu_item_new();
143 gtk_menu_append(GTK_MENU(menu), widget);
144 gtk_widget_set_sensitive(widget, FALSE);
145 gtk_widget_show(widget);
146 }
147
148 /*
149 * Make up a new menubar for a main browser window. The accelerator table
150 * is stored in bw->accel_group.
151 * Currently does not deal with dynamic menu items (bookmarks and history).
152 * CP: It *seems* to handle dynamic menu items...
153 */
154 GtkWidget *a_Menu_mainbar_new(BrowserWindow *bw, gint tiny)
155 {
156 GtkWidget *menubar;
157 GtkWidget *file_menu;
158 /* GtkWidget *help_menu; */
159
160 bw->menubar = menubar = gtk_menu_bar_new();
161
162 /* FILE MENU */
163 file_menu = Menu_new(menubar, tiny ? "_F" : "_File", FALSE, bw);
164 Menu_add(file_menu, "_New Browser", "<ctrl>N", bw,
165 a_Commands_new_callback, bw);
166 Menu_add(file_menu, "_Open File...", "<ctrl>O", bw,
167 a_Commands_openfile_callback, bw);
168 Menu_add(file_menu, "Open _URL...", "<ctrl>L", bw,
169 a_Commands_openurl_callback, bw);
170 /*
171 Menu_add(file_menu, "_Preferences", "<ctrl>E", bw,
172 a_Commands_prefs_callback, bw);
173 */
174 Menu_add(file_menu, "Close Window", "<ctrl>Q", bw,
175 a_Commands_close_callback, bw);
176 Menu_sep(file_menu);
177 Menu_add(file_menu, "Exit Dillo", "<alt>Q", bw,
178 a_Commands_exit_callback, bw);
179
180 /* HELP MENU
181 help_menu = Menu_new(menubar, "_Help", TRUE, bw);
182 Menu_add(help_menu, "Dillo _Home", NULL, bw,
183 a_Commands_helphome_callback, bw);
184 */
185 return menubar;
186 }
187
188 /*
189 * Make a new popup menu and return it.
190 */
191 GtkWidget *a_Menu_popup_op_new(BrowserWindow *bw)
192 {
193 GtkWidget *menu;
194
195 menu = gtk_menu_new();
196 Menu_sep(menu);
197 Menu_add_title(menu, "PAGE OPTIONS");
198 Menu_sep(menu);
199 Menu_add(menu, "View page Source", NULL, bw,
200 a_Commands_viewsource_callback, bw);
201 bw->viewbugs_menuitem = Menu_add(menu, "View page Bugs", NULL,bw, NULL,bw);
202
203 Menu_add(menu, "Bookmark this page", NULL, bw,
204 a_Commands_addbm_callback, bw);
205 Menu_sep(menu);
206 Menu_add(menu, "_Find Text", "<ctrl>F", bw,
207 a_Commands_findtext_callback, bw);
208
209 bw->pagemarks_menuitem = Menu_add(menu, "Jump to...", NULL, bw, NULL, bw);
210
211 Menu_sep(menu);
212 Menu_add(menu, "Save page As...", NULL, bw,
213 a_Commands_save_callback, bw);
214
215 return menu;
216 }
217
218 /*
219 * Set the popup's primary DilloUrl
220 */
221 void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url)
222 {
223 if (bw->menu_popup.url)
224 a_Url_free(bw->menu_popup.url);
225 bw->menu_popup.url = a_Url_dup(url);
226 }
227
228 /*
229 * Set the popup's secondary DilloUrl
230 * Note: this URL is used by the image submenu of the link popup.
231 * (If the need arises, this can be extended using an URL array in the popup
232 * structure, and by setting the to-be-picked-URL-index in the popup)
233 */
234 void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url)
235 {
236 if (bw->menu_popup.url2)
237 a_Url_free(bw->menu_popup.url2);
238 bw->menu_popup.url2 = a_Url_dup(url);
239 }
240
241 /*
242 * Remove the popup's secondary DilloUrl.
243 * (This is done at popup time, so and old "url2" can't linger)
244 */
245 void a_Menu_popup_clear_url2(GtkWidget *menu_popup)
246 {
247 /* clear the url2 tag (currently "over_image" is the only one using it) */
248 gtk_object_remove_data(GTK_OBJECT (menu_popup), "url2");
249 }
250
251 /*
252 * Get the popup's DilloUrl
253 */
254 DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw)
255 {
256 return bw->menu_popup.url;
257 }
258
259 /*
260 * Put the url in the status bar when moving cursor over menuitem.
261 */
262 static void
263 Menu_popup_history_select_callback(GtkWidget *widget, gpointer data)
264 {
265 gint idx;
266 BrowserWindow *bw = data;
267
268 idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
269 if (idx >= 0 && idx < a_Nav_stack_size(bw)) {
270 a_Interface_msg(bw, "%s", URL_STR_(a_History_get_url(NAV_IDX(bw,idx))));
271 bw->status_is_link = 1;
272 }
273 }
274
275 /*
276 * Remove the url in the status bar when removing cursor from menuitem.
277 */
278 static void
279 Menu_popup_history_deselect_callback(GtkWidget *widget, gpointer data) {
280 BrowserWindow *bw = data;
281
282 a_Interface_msg(bw, "");
283 }
284
285 /*
286 * Make a new popup menu for history browsing
287 * direction: {backward = -1, forward = 1}
288 */
289 GtkWidget *a_Menu_popup_history_new(BrowserWindow *bw, gint direction)
290 {
291 int i;
292 GtkWidget *menu, *menu_item;
293 GString *text = g_string_sized_new(128);
294
295 menu = a_Gtk_ext_menu_new();
296 Menu_sep(menu);
297 if (direction>0) {
298 Menu_add_title(menu, "FOLLOWING PAGES");
299 i = a_Nav_stack_ptr(bw) + 1;
300 } else {
301 Menu_add_title(menu, "PREVIOUS PAGES");
302 i = a_Nav_stack_ptr(bw) - 1;
303 }
304 Menu_sep(menu);
305
306 for ( ; i >= 0 && i < a_Nav_stack_size(bw); i+=direction ) {
307 if (a_History_get_title(NAV_IDX(bw,i))) {
308 g_string_assign(text, a_History_get_title(NAV_IDX(bw,i)));
309 } else {
310 g_string_assign(text, URL_STR(a_History_get_url(NAV_IDX(bw,i))));
311 }
312 if ( text->len > 64 ) {
313 g_string_truncate(text, 64);
314 g_string_append(text, "...");
315 }
316 menu_item = a_Gtk_ext_menu_item_new_with_label(text->str);
317 gtk_menu_append(GTK_MENU(menu), menu_item);
318 gtk_widget_show(menu_item);
319 /* attach the nav_stack index to the menu item */
320 gtk_object_set_data(GTK_OBJECT (menu_item), "nav_idx",
321 GINT_TO_POINTER(i));
322
323 gtk_signal_connect (
324 GTK_OBJECT (menu_item), "select",
325 GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
326 gtk_signal_connect (
327 GTK_OBJECT (menu_item), "select",
328 GTK_SIGNAL_FUNC (Menu_popup_history_select_callback), bw);
329 gtk_signal_connect (
330 GTK_OBJECT (menu_item), "deselect",
331 GTK_SIGNAL_FUNC (Menu_popup_history_deselect_callback), bw);
332
333 gtk_signal_connect (
334 GTK_OBJECT (menu_item), "activate",
335 GTK_SIGNAL_FUNC (a_Commands_history_callback_same_bw), bw);
336 gtk_signal_connect (
337 GTK_OBJECT (menu_item), "activate1",
338 GTK_SIGNAL_FUNC (a_Commands_history_callback_same_bw), bw);
339 gtk_signal_connect (
340 GTK_OBJECT (menu_item), "activate2",
341 GTK_SIGNAL_FUNC (a_Commands_history_callback_new_bw), bw);
342 gtk_signal_connect (
343 GTK_OBJECT (menu_item), "activate3",
344 GTK_SIGNAL_FUNC (a_Commands_history_callback_same_bw), bw);
345 }
346
347 g_string_free(text, TRUE);
348 return menu;
349 }
350
351 /*
352 * Make a new popup menu for when the mouse is over a link.
353 */
354 GtkWidget *a_Menu_popup_ol_new(BrowserWindow *bw)
355 {
356 GtkWidget *menu;
357 GtkWidget *copy;
358 GtkWidget *menuitem;
359
360 menu = gtk_menu_new();
361 Menu_sep(menu);
362 Menu_add_title(menu, "LINK OPTIONS");
363 Menu_sep(menu);
364 Menu_add(menu, "Open Link in New Window", NULL, bw,
365 a_Commands_open_link_nw_callback, bw);
366 Menu_add(menu, "Bookmark this Link", NULL, bw,
367 a_Commands_addbm_callback, bw);
368
369 copy = Menu_add(menu, "Copy Link location", NULL, bw,
370 a_Commands_select_popup_url_callback, bw);
371 a_Selection_init_selection(copy);
372
373 Menu_sep(menu);
374 Menu_add(menu, "Save Link As...", NULL, bw,
375 a_Commands_save_link_callback, bw);
376
377 Menu_sep(menu);
378 menuitem = Menu_add(menu, "Image Menu...", NULL, bw, NULL, bw);
379 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),
380 bw->menu_popup.over_image);
381 bw->menu_popup.ol_oi_submenu = menuitem;
382
383 return menu;
384 }
385
386 /*
387 * Make a new popup menu for when the mouse is over an image
388 */
389 GtkWidget *a_Menu_popup_oi_new (BrowserWindow *bw)
390 {
391 GtkWidget *menu;
392 GtkWidget *copy;
393
394 menu = gtk_menu_new();
395 Menu_sep(menu);
396 Menu_add_title(menu, "IMAGE OPTIONS");
397 Menu_sep(menu);
398 Menu_add(menu, "Isolate Image", NULL, bw,
399 a_Commands_open_link_callback, bw);
400 Menu_add(menu, "Open Image in New Window", NULL, bw,
401 a_Commands_open_link_nw_callback, bw);
402 Menu_add(menu, "Bookmark this Image", NULL, bw,
403 a_Commands_addbm_callback, bw);
404 copy = Menu_add(menu, "Copy Image location", NULL, bw,
405 a_Commands_select_popup_url_callback, bw);
406 a_Selection_init_selection(copy);
407
408 Menu_sep(menu);
409 Menu_add(menu, "Save Image As...", NULL, bw,
410 a_Commands_save_link_callback, bw);
411 return menu;
412 }
413
414 /*
415 * Make a new popup menu for right click over the bug meter.
416 */
417 GtkWidget *a_Menu_popup_ob_new (BrowserWindow *bw)
418 {
419 GtkWidget *menu;
420
421 menu = gtk_menu_new();
422 Menu_sep(menu);
423 Menu_add_title(menu, "BUG METER OPTIONS");
424 Menu_sep(menu);
425 Menu_add(menu, "Validate URL with W3C", NULL, bw,
426 a_Commands_ob_w3c_callback, bw);
427 Menu_add(menu, "Validate URL with WDG", NULL, bw,
428 a_Commands_ob_wdg_callback, bw);
429 Menu_sep(menu);
430 Menu_add(menu, "About Bug Meter...", NULL, bw,
431 a_Commands_ob_info_callback, bw);
432 return menu;
433 }
434
435 /*
436 * Show or Hide the image submenu.
437 */
438 void a_Menu_popup_ol_show_oi(BrowserWindow *bw, gboolean show)
439 {
440 if (show)
441 gtk_widget_show(bw->menu_popup.ol_oi_submenu);
442 else
443 gtk_widget_hide(bw->menu_popup.ol_oi_submenu);
444 }
445
446 /*
447 * Functions to manipulate Pagemarks menu.
448 * Should this be in a separate "pagemark.c" ?
449 */
450
451 /*
452 * make the page scroll to the pagemark
453 */
454 static void Menu_pagemarks_goto_pagemark(GtkWidget *widget,
455 gpointer client_data)
456 {
457 BrowserWindow *bw = (BrowserWindow *) client_data;
458 char anchor[32];
459
460 g_snprintf (anchor, 32, "#%ld", (long int)widget);
461 a_Dw_gtk_scrolled_window_set_anchor(GTK_DW_SCROLLED_WINDOW(bw->docwin),
462 anchor);
463 }
464
465 /*
466 * Deallocate the memory used by a pagemarks menu and create a new one
467 */
468 void a_Menu_pagemarks_new(BrowserWindow *bw)
469 {
470 gtk_widget_set_sensitive(bw->pagemarks_menuitem, FALSE);
471
472 if (bw->pagemarks_menu)
473 gtk_widget_destroy(bw->pagemarks_menu);
474 bw->pagemarks_menu = gtk_menu_new();
475 bw->pagemarks_last = NULL;
476
477 gtk_menu_item_set_submenu(GTK_MENU_ITEM(bw->pagemarks_menuitem),
478 bw->pagemarks_menu);
479 }
480
481 /*
482 * Add a new pagemark (opening H tag).
483 * The text can be set only at the closing H tag.
484 * level is the level of the heading (1-6).
485 */
486 void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
487 gint level)
488 {
489 char anchor[32], spaces[32], name[32];
490
491 gtk_widget_set_sensitive(bw->pagemarks_menuitem, TRUE);
492 g_snprintf(spaces, 32, "%*s", 3 * (level - 1), "");
493 bw->pagemarks_last = Menu_add(bw->pagemarks_menu, spaces, NULL, bw,
494 Menu_pagemarks_goto_pagemark, bw);
495 g_snprintf(anchor, 32, "#%ld", (glong)(bw->pagemarks_last));
496 a_Dw_page_add_anchor(page, anchor, style);
497 gtk_signal_connect (GTK_OBJECT (bw->pagemarks_last), "select",
498 GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
499 g_snprintf(name, 32, "dilloHeading%d", level);
500 gtk_widget_set_name(bw->pagemarks_last, name);
501 }
502
503 /*
504 * Set the text for the last created pagemark.
505 */
506 void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str)
507 {
508 GString *text;
509 GtkWidget *child;
510
511 /* Avoid problems with lonely closing tags and nested headings */
512 if ( bw->pagemarks_last &&
513 (child = GTK_BIN (bw->pagemarks_last)->child) &&
514 GTK_IS_LABEL (child) ) {
515 text = g_string_new(GTK_LABEL(child)->label);
516 g_string_append(text, str);
517 if ( text->len > 64 ) {
518 g_string_truncate(text, 64);
519 g_string_append(text, "...");
520 }
521 gtk_label_set_text(GTK_LABEL (child), text->str);
522 g_string_free(text, 1);
523 bw->pagemarks_last = NULL;
524 }
525 }
0 #ifndef __MENU_H__
1 #define __MENU_H__
2
3 #include <gtk/gtk.h>
4
5 GtkWidget *a_Menu_mainbar_new (BrowserWindow *bw, gint tiny);
6 GtkWidget *a_Menu_popup_op_new (BrowserWindow *bw);
7 GtkWidget *a_Menu_popup_ol_new (BrowserWindow *bw);
8 GtkWidget *a_Menu_popup_oi_new (BrowserWindow *bw);
9 GtkWidget *a_Menu_popup_ob_new (BrowserWindow *bw);
10 GtkWidget *a_Menu_popup_history_new(BrowserWindow *bw, gint direction);
11 void a_Menu_popup_ol_show_oi(BrowserWindow *bw, gboolean show);
12 void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url);
13 void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url);
14 void a_Menu_popup_clear_url2(GtkWidget *menu_popup);
15
16 DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw);
17
18 void a_Menu_pagemarks_new (BrowserWindow *bw);
19 void a_Menu_pagemarks_destroy (BrowserWindow *bw);
20 void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
21 gint level);
22 void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str);
23
24 #endif /* MENU_H */
0 /*
1 * File: misc.c
2 *
3 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>,
4 * Jörgen Viksell <vsksga@hotmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <glib.h>
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <ctype.h>
19
20 #include "msg.h"
21 #include "misc.h"
22
23 /*
24 * Prepend the users home-dir to 'file' string i.e,
25 * pass in .dillo/bookmarks.html and it will return
26 * /home/imain/.dillo/bookmarks.html
27 *
28 * Remember to g_free() returned value!
29 */
30 gchar *a_Misc_prepend_user_home(const char *file)
31 {
32 return ( g_strconcat(g_get_home_dir(), "/", file, NULL) );
33 }
34
35 /*
36 * Escape characters as %XX sequences.
37 * Return value: New string, or NULL if there's no need to escape.
38 */
39 gchar *a_Misc_escape_chars(const gchar *str, gchar *esc_set)
40 {
41 static const char *hex = "0123456789ABCDEF";
42 gchar *p = NULL;
43 GString *gstr;
44 gint i;
45
46 for (i = 0; str[i]; ++i)
47 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i]))
48 break;
49
50 if (str[i]) {
51 /* needs escaping */
52 gstr = g_string_sized_new(64);
53 for (i = 0; str[i]; ++i) {
54 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
55 g_string_append_c(gstr, '%');
56 g_string_append_c(gstr, hex[(str[i] >> 4) & 15]);
57 g_string_append_c(gstr, hex[str[i] & 15]);
58 } else {
59 g_string_append_c(gstr, str[i]);
60 }
61 }
62 p = gstr->str;
63 g_string_free(gstr, FALSE);
64 }
65 return p;
66 }
67
68 /*
69 * Case insensitive strstr
70 */
71 gchar *a_Misc_stristr(char *src, char *str)
72 {
73 int i, j;
74
75 for (i = 0, j = 0; src[i] && str[j]; ++i)
76 if (tolower(src[i]) == tolower(str[j])) {
77 ++j;
78 } else if (j) {
79 i -= j;
80 j = 0;
81 }
82
83 if (!str[j]) /* Got all */
84 return (src + i - j);
85 return NULL;
86 }
87
88 /*
89 * strsep implementation
90 */
91 gchar *a_Misc_strsep(char **orig, const char *delim)
92 {
93 gchar *str, *p;
94
95 if (!(str = *orig))
96 return NULL;
97
98 p = strpbrk(str, delim);
99 if (p) {
100 *p++ = 0;
101 *orig = p;
102 } else {
103 *orig = NULL;
104 }
105 return str;
106 }
107
108 #define TAB_SIZE 8
109 /*
110 * Takes a string and converts any tabs to spaces.
111 */
112 gchar *a_Misc_expand_tabs(const char *str)
113 {
114 GString *New = g_string_new("");
115 int len, i, j, pos, old_pos;
116 char *val;
117
118 if ( (len = strlen(str)) ) {
119 for (pos = 0, i = 0; i < len; i++) {
120 if (str[i] == '\t') {
121 /* Fill with whitespaces until the next tab. */
122 old_pos = pos;
123 pos += TAB_SIZE - (pos % TAB_SIZE);
124 for (j = old_pos; j < pos; j++)
125 g_string_append_c(New, ' ');
126 } else {
127 g_string_append_c(New, str[i]);
128 pos++;
129 }
130 }
131 }
132 val = New->str;
133 g_string_free(New, FALSE);
134 return val;
135 }
136
137 /*
138 * Split a string into tokens, at any character contained by delim,
139 * and return the starting and ending positions within the string. For
140 * n tokens, the returned array has at least 2 * n + 1 elements, and
141 * contains the start of token i at 2 * i, the end at 2 * i + 1. The
142 * array is terminated by -1.
143 */
144 gint *a_Misc_strsplitpos(const gchar *str, const gchar *delim)
145 {
146 gint array_max = 4;
147 gint *array = g_new(gint, array_max);
148 gint n = 0;
149 gint p1 = 0, p2;
150
151 while (TRUE) {
152 while (str[p1] != 0 && strchr(delim, str[p1]) != NULL)
153 p1++;
154 if (str[p1] == 0)
155 break;
156
157 p2 = p1;
158 while (str[p2] != 0 && strchr(delim, str[p2]) == NULL)
159 p2++;
160
161 if (array_max < 2 * n + 3) {
162 array_max <<= 2;
163 array = g_realloc(array, array_max * sizeof(gint));
164 }
165
166 array[2 * n] = p1;
167 array[2 * n + 1] = p2;
168 n++;
169
170 if (str[p2] == 0)
171 break;
172 else {
173 p1 = p2;
174 }
175 }
176
177 array[2 * n] = -1;
178 return array;
179 }
180
181 /*
182 * Return a copy of an array which was created by a_Misc_strsplitpos.
183 */
184 gint *a_Misc_strsplitposdup(gint *pos)
185 {
186 gint n = 0;
187 gint *pos2;
188 while (pos[2 * n] != -1)
189 n++;
190 pos2 = g_new(gint, 2 * n + 1);
191 memcpy(pos2, pos, (2 * n + 1) * sizeof(gint));
192 return pos2;
193 }
194
195 /* TODO: could use dStr ADT! */
196 typedef struct ContentType_ {
197 const char *str;
198 int len;
199 } ContentType_t;
200
201 static const ContentType_t MimeTypes[] = {
202 { "application/octet-stream", 24 },
203 { "text/html", 9 },
204 { "text/plain", 10 },
205 { "image/gif", 9 },
206 { "image/png", 9 },
207 { "image/jpeg", 10 },
208 { NULL, 0 }
209 };
210
211 /*
212 * Detects 'Content-Type' from a data stream sample.
213 *
214 * It uses the magic(5) logic from file(1). Currently, it
215 * only checks the few mime types that Dillo supports.
216 *
217 * 'Data' is a pointer to the first bytes of the raw data.
218 *
219 * Return value: (0 on success, 1 on doubt, 2 on lack of data).
220 */
221 int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
222 {
223 int st = 1; /* default to "doubt' */
224 int Type = 0; /* default to "application/octet-stream" */
225 char *p = Data;
226 size_t i, non_ascci;
227
228 /* HTML try */
229 for (i = 0; i < Size && isspace(p[i]); ++i);
230 if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
231 (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
232 (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
233 (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) ||
234 /* this line is workaround for FTP through the Squid proxy */
235 (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) {
236
237 Type = 1;
238 st = 0;
239 /* Images */
240 } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
241 Type = 3;
242 st = 0;
243 } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
244 Type = 4;
245 st = 0;
246 } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
247 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
248 * at the character representation should be machine independent. */
249 Type = 5;
250 st = 0;
251
252 /* Text */
253 } else {
254 /* We'll assume "text/plain" if the set of chars above 127 is <= 10
255 * in a 256-bytes sample. Better heuristics are welcomed! :-) */
256 non_ascci = 0;
257 Size = MIN (Size, 256);
258 for (i = 0; i < Size; i++)
259 if ((unsigned char) p[i] > 127)
260 ++non_ascci;
261 if (Size == 256) {
262 Type = (non_ascci > 10) ? 0 : 2;
263 st = 0;
264 } else {
265 Type = (non_ascci > 0) ? 0 : 2;
266 }
267 }
268
269 *PT = MimeTypes[Type].str;
270 return st;
271 }
272
273 /*
274 * Check the server-supplied 'Content-Type' against our detected type.
275 * (some servers seem to default to "text/plain").
276 *
277 * Return value:
278 * 0, if they match
279 * -1, if a mismatch is detected
280 *
281 * There're many MIME types Dillo doesn't know, they're handled
282 * as "application/octet-stream" (as the SPEC says).
283 *
284 * A mismatch happens when receiving a binary stream as
285 * "text/plain" or "text/html", or an image that's not an image of its kind.
286 *
287 * Note: this is a basic security procedure.
288 *
289 */
290 int a_Misc_content_type_check(const char *EntryType, const char *DetectedType)
291 {
292 int i;
293 int st = -1;
294
295 MSG("Type check: [Srv: %s Det: %s]\n", EntryType, DetectedType);
296
297 if (!EntryType)
298 return 0; /* there's no mismatch without server type */
299
300 for (i = 1; MimeTypes[i].str; ++i)
301 if (g_strncasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) == 0)
302 break;
303
304 if (!MimeTypes[i].str) {
305 /* type not found, no mismatch */
306 st = 0;
307 } else if (g_strncasecmp(EntryType, "image/", 6) == 0 &&
308 !g_strncasecmp(DetectedType,MimeTypes[i].str,MimeTypes[i].len)){
309 /* An image, and there's an exact match */
310 st = 0;
311 } else if (g_strncasecmp(EntryType, "text/", 5) ||
312 g_strncasecmp(DetectedType, "application/", 12)) {
313 /* Not an application sent as text */
314 st = 0;
315 }
316
317 return st;
318 }
319
320 /*
321 * Parse a geometry string.
322 */
323 gint a_Misc_parse_geometry(gchar *str, gint *x, gint *y, gint *w, gint *h)
324 {
325 gchar *p, *t1, *t2;
326 gint n1, n2;
327 gint ret = 0;
328
329 if ((p = strchr(str, 'x')) || (p = strchr(str, 'X'))) {
330 n1 = strtol(str, &t1, 10);
331 n2 = strtol(++p, &t2, 10);
332 if (t1 != str && t2 != p) {
333 *w = n1;
334 *h = n2;
335 ret = 1;
336 /* parse x,y now */
337 p = t2;
338 n1 = strtol(p, &t1, 10);
339 n2 = strtol(t1, &t2, 10);
340 if (t1 != p && t2 != t1) {
341 *x = n1;
342 *y = n2;
343 }
344 }
345 }
346 _MSG("geom: w,h,x,y = (%d,%d,%d,%d)\n", *w, *h, *x, *y);
347 return ret;
348 }
349
350 /*
351 * Encodes string using base64 encoding.
352 * Return value: new string or NULL if input string is empty.
353 */
354 gchar *a_Misc_encode_base64(const gchar *in)
355 {
356 static const char *base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
357 "abcdefghijklmnopqrstuvwxyz"
358 "0123456789+/";
359 gchar *out = NULL;
360 int len, i = 0;
361
362 if (in == NULL) return NULL;
363 len = strlen(in);
364
365 out = (gchar *)g_malloc((len + 2) / 3 * 4 + 1);
366
367 for (; len >= 3; len -= 3) {
368 out[i++] = base64_hex[in[0] >> 2];
369 out[i++] = base64_hex[((in[0]<<4) & 0x30) | (in[1]>>4)];
370 out[i++] = base64_hex[((in[1]<<2) & 0x3c) | (in[2]>>6)];
371 out[i++] = base64_hex[in[2] & 0x3f];
372 in += 3;
373 }
374
375 if (len > 0) {
376 unsigned char fragment;
377 out[i++] = base64_hex[in[0] >> 2];
378 fragment = (in[0] << 4) & 0x30;
379 if (len > 1) fragment |= in[1] >> 4;
380 out[i++] = base64_hex[fragment];
381 out[i++] = (len < 2) ? '=' : base64_hex[(in[1] << 2) & 0x3c];
382 out[i++] = '=';
383 }
384 out[i] = '\0';
385 return out;
386 }
0 #ifndef __MISC_H__
1 #define __MISC_H__
2 #include <stdlib.h>
3
4 gchar *a_Misc_prepend_user_home(const char *file);
5 gchar *a_Misc_escape_chars(const gchar *str, gchar *esc_set);
6 gchar *a_Misc_stristr(char *src, char *str);
7 gchar *a_Misc_expand_tabs(const char *str);
8 gchar *a_Misc_strsep(char **orig, const char *delim);
9 #define d_strsep a_Misc_strsep
10 gint *a_Misc_strsplitpos(const gchar *str, const gchar *delim);
11 gint *a_Misc_strsplitposdup(gint *pos);
12 gint a_Misc_get_content_type_from_data(void *Data, size_t Size,
13 const char **PT);
14 int a_Misc_content_type_check(const char *EntryType, const char *DetectedType);
15 gint a_Misc_parse_geometry(gchar *geom, gint *x, gint *y, gint *w, gint *h);
16 gchar *a_Misc_encode_base64(const gchar *in);
17
18 /* Return a NULL-terminated string containing the characters from p1 to p2. */
19 #define a_Misc_strpdup(s, p1, p2) g_strndup((s) + (p1), (p2) - (p1))
20
21 #endif /* __MISC_H__ */
22
0 #ifndef __MSG_H__
1 #define __MSG_H__
2
3 #include "prefs.h"
4
5 /*
6 * You can disable any MSG* macro by adding the '_' prefix.
7 */
8 #define _MSG(fmt...)
9 #define _MSG_HTML(fmt...)
10 #define _MSG_HTTP(fmt...)
11
12
13 #define MSG(fmt...) \
14 G_STMT_START { \
15 if (prefs.show_msg) \
16 g_print(fmt); \
17 } G_STMT_END
18
19 #define MSG_HTML(fmt...) \
20 G_STMT_START { \
21 Html_msg(html, fmt); \
22 } G_STMT_END
23
24 #define MSG_HTTP(fmt...) g_print("HTTP warning: " fmt)
25
26 #endif /* __MSG_H__ */
0 /*
1 * File: nav.c
2 *
3 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
4 * Copyright (C) 2000, 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /* Support for a navigation stack */
13
14 #include <stdio.h>
15 #include <gtk/gtk.h>
16 #include "msg.h"
17 #include "list.h"
18 #include "nav.h"
19 #include "history.h"
20 #include "web.h"
21 #include "menu.h"
22 #include "interface.h"
23 #include "dw_gtk_scrolled_window.h"
24 #include "prefs.h"
25 #include "capi.h"
26
27 /* #define DEBUG_LEVEL 3 */
28 #include "debug.h"
29
30 /*
31 * Forward declarations
32 */
33 static void Nav_reload(BrowserWindow *bw);
34
35
36 /*
37 * Initialize the navigation structure with safe values
38 */
39 void a_Nav_init(BrowserWindow *bw)
40 {
41 bw->nav_stack_size = 0;
42 bw->nav_stack_size_max = 16;
43 bw->nav_stack = NULL;
44 bw->nav_stack_ptr = -1;
45 bw->nav_expecting = FALSE;
46 bw->nav_expect_url = NULL;
47 }
48
49 /*
50 * Free memory used by this module
51 */
52 void a_Nav_free(BrowserWindow *bw)
53 {
54 a_Nav_cancel_expect(bw);
55 g_free(bw->nav_stack);
56 }
57
58
59 /* Navigation stack methods ------------------------------------------------ */
60
61 /*
62 * Return current nav_stack pointer [0 based; -1 = empty]
63 */
64 gint a_Nav_stack_ptr(BrowserWindow *bw)
65 {
66 return bw->nav_stack_ptr;
67 }
68
69 /*
70 * Move the nav_stack pointer
71 */
72 static void Nav_stack_move_ptr(BrowserWindow *bw, gint offset)
73 {
74 gint nptr;
75
76 g_return_if_fail (bw != NULL);
77 if (offset != 0) {
78 nptr = bw->nav_stack_ptr + offset;
79 g_return_if_fail (nptr >= 0 && nptr < bw->nav_stack_size);
80 bw->nav_stack_ptr = nptr;
81 }
82 }
83
84 /*
85 * Return size of nav_stack [1 based]
86 */
87 gint a_Nav_stack_size(BrowserWindow *bw)
88 {
89 return bw->nav_stack_size;
90 }
91
92 /*
93 * Add an URL-index in the navigation stack.
94 */
95 static void Nav_stack_add(BrowserWindow *bw, gint idx)
96 {
97 g_return_if_fail (bw != NULL);
98
99 ++bw->nav_stack_ptr;
100 if ( bw->nav_stack_ptr == bw->nav_stack_size) {
101 a_List_add(bw->nav_stack, bw->nav_stack_size, bw->nav_stack_size_max);
102 ++bw->nav_stack_size;
103 } else {
104 bw->nav_stack_size = bw->nav_stack_ptr + 1;
105 }
106 bw->nav_stack[bw->nav_stack_ptr] = idx;
107 }
108
109 /*
110 * Remove an URL-index from the navigation stack.
111 */
112 static void Nav_stack_remove(BrowserWindow *bw, gint idx)
113 {
114 gint sz = a_Nav_stack_size(bw);
115
116 g_return_if_fail (bw != NULL && idx >=0 && idx < sz);
117
118 for ( ; idx < sz - 1; ++idx)
119 bw->nav_stack[idx] = bw->nav_stack[idx + 1];
120 if ( bw->nav_stack_ptr == --bw->nav_stack_size )
121 --bw->nav_stack_ptr;
122 }
123
124 /*
125 * Remove equal adyacent URLs at the top of the stack.
126 * (It may happen with redirections)
127 */
128 static void Nav_stack_clean(BrowserWindow *bw)
129 {
130 gint i;
131
132 g_return_if_fail (bw != NULL);
133
134 if ((i = a_Nav_stack_size(bw)) >= 2 &&
135 bw->nav_stack[i-2] == bw->nav_stack[i-1])
136 Nav_stack_remove(bw, i - 1);
137 }
138
139
140 /* General methods --------------------------------------------------------- */
141
142 /*
143 * Create a DilloWeb structure for 'url' and ask the cache to send it back.
144 * - Also set a few things related to the browser window.
145 * This function requests the page's root-URL; images and related stuff
146 * are fetched directly by the HTML module.
147 */
148 static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, gint offset)
149 {
150 DilloUrl *old_url = NULL;
151 gboolean MustLoad;
152 gint ClientKey;
153 DilloWeb *Web;
154 gboolean ForceReload = (URL_FLAGS(url) & URL_E2EReload);
155
156 MSG("Nav_open_url: Url=>%s<\n", URL_STR_(url));
157
158 /* Get the url of the current page */
159 if ( a_Nav_stack_ptr(bw) != -1 )
160 old_url = a_History_get_url(NAV_TOP(bw));
161
162 /* Record current scrolling position
163 * (the strcmp check is necessary because of redirections) */
164 if (old_url &&
165 !strcmp(URL_STR(old_url), a_Interface_get_location_text(bw))) {
166 old_url->scrolling_position_x =
167 a_Dw_gtk_scrolled_window_get_scrolling_position_x(
168 GTK_DW_SCROLLED_WINDOW(bw->docwin));
169 old_url->scrolling_position_y =
170 a_Dw_gtk_scrolled_window_get_scrolling_position_y(
171 GTK_DW_SCROLLED_WINDOW(bw->docwin));
172 }
173
174 /* Update navigation-stack-pointer (offset may be zero) */
175 Nav_stack_move_ptr(bw, offset);
176
177 /* Page must be reloaded, if old and new url (without anchor) differ */
178 MustLoad = ForceReload || !old_url;
179 if (old_url){
180 MustLoad |= a_Url_cmp(old_url, url);
181 MustLoad |= strcmp(URL_STR(old_url), a_Interface_get_location_text(bw));
182 }
183
184 if ( MustLoad ) {
185 a_Interface_stop(bw);
186 a_Interface_clean(bw);
187
188 a_Menu_pagemarks_new(bw);
189
190 Web = a_Web_new(url);
191 Web->bw = bw;
192 Web->flags |= WEB_RootUrl;
193 if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
194 a_Interface_add_client(bw, ClientKey, 1);
195 a_Interface_add_url(bw, url, WEB_RootUrl);
196 }
197 a_Interface_set_cursor(bw, GDK_LEFT_PTR);
198 }
199
200 /* Jump to #anchor position */
201 if (URL_FRAGMENT_(url)) {
202 /* todo: push on stack */
203 gchar *pf = a_Url_decode_hex_str(URL_FRAGMENT_(url));
204 a_Dw_gtk_scrolled_window_set_anchor(
205 GTK_DW_SCROLLED_WINDOW(bw->docwin), pf);
206 g_free(pf);
207 }
208 }
209
210 /*
211 * Cancel the last expected url if present. The responsibility
212 * for actually aborting the data stream remains with the caller.
213 */
214 void a_Nav_cancel_expect(BrowserWindow *bw)
215 {
216 if (bw->nav_expecting) {
217 if (bw->nav_expect_url) {
218 a_Url_free(bw->nav_expect_url);
219 bw->nav_expect_url = NULL;
220 }
221 bw->nav_expecting = FALSE;
222 }
223 }
224
225 /*
226 * We have an answer! Set things accordingly.
227 */
228 void a_Nav_expect_done(BrowserWindow *bw)
229 {
230 gint idx;
231 DilloUrl *url;
232
233 g_return_if_fail(bw != NULL);
234
235 if (bw->nav_expecting) {
236 url = bw->nav_expect_url;
237 /* unset E2EReload before adding this url to history */
238 a_Url_set_flags(url, URL_FLAGS(url) & ~URL_E2EReload);
239 idx = a_History_add_url(url);
240 Nav_stack_add(bw, idx);
241
242 a_Url_free(url);
243 bw->nav_expect_url = NULL;
244 bw->nav_expecting = FALSE;
245 }
246 Nav_stack_clean(bw);
247 a_Interface_set_button_sens(bw);
248 }
249
250 /*
251 * Make 'url' the current browsed page (upon data arrival)
252 * - Set bw to expect the URL data
253 * - Ask the cache to feed back the requested URL (via Nav_open_url)
254 */
255 void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
256 {
257 g_return_if_fail (bw != NULL);
258
259 if (bw->nav_expecting && a_Url_cmp(bw->nav_expect_url, url) == 0 &&
260 URL_STRCAMP_EQ(URL_FRAGMENT_(bw->nav_expect_url), URL_FRAGMENT_(url))) {
261 /* we're already expecting that url (most probably a double-click) */
262 return;
263 }
264 a_Nav_cancel_expect(bw);
265 bw->nav_expect_url = a_Url_dup(url);
266 bw->nav_expecting = TRUE;
267 Nav_open_url(bw, url, 0);
268 }
269
270 /*
271 * Same as a_Nav_push() but in a new window.
272 */
273 void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url)
274 {
275 gint width, height;
276 BrowserWindow *newbw;
277
278 gdk_window_get_size (bw->main_window->window, &width, &height);
279 newbw = a_Interface_browser_window_new(width, height, 0);
280 a_Nav_push(newbw, url);
281 }
282
283 /*
284 * Wraps a_Nav_push to match 'DwPage->link' function type
285 */
286 void a_Nav_vpush(void *vbw, const DilloUrl *url)
287 {
288 a_Nav_push(vbw, url);
289 }
290
291 /*
292 * Send the browser back to previous page
293 */
294 void a_Nav_back(BrowserWindow *bw)
295 {
296 gint idx = a_Nav_stack_ptr(bw);
297
298 a_Nav_cancel_expect(bw);
299 if ( --idx >= 0 ){
300 a_Interface_msg(bw, "");
301 Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)), -1);
302 }
303 }
304
305 /*
306 * Send the browser to next page in the history list
307 */
308 void a_Nav_forw(BrowserWindow *bw)
309 {
310 gint idx = a_Nav_stack_ptr(bw);
311
312 a_Nav_cancel_expect(bw);
313 if (++idx < a_Nav_stack_size(bw)) {
314 a_Interface_msg(bw, "");
315 Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)), +1);
316 }
317 }
318
319 /*
320 * Redirect the browser to the HOME page!
321 */
322 void a_Nav_home(BrowserWindow *bw)
323 {
324 a_Nav_push(bw, prefs.home);
325 }
326
327 /*
328 * Jump to an URL within the stack history
329 * NewBw: {0 = same window, 1 = new window}
330 */
331 void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewBw)
332 {
333 int idx;
334 BrowserWindow *bw = client_data;
335
336 idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
337 if (idx >= 0 && idx < a_Nav_stack_size(bw)) {
338 if (NewBw == 1) {
339 a_Nav_push_nw(bw, a_History_get_url(NAV_IDX(bw,idx)));
340 } else {
341 Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)),
342 idx - a_Nav_stack_ptr(bw));
343 }
344 }
345 }
346
347 /*
348 * Callback for reload confirmation
349 */
350 static void Nav_reload_confirmation_cb(BrowserWindow *bw)
351 {
352 DialogAnswer *answer = bw->question_dialog_answer;
353
354 _MSG("Nav_reload_confirmation_cb %p\n", bw->question_dialog_window);
355
356 if (answer->alt_num == 1) { /* "OK" */
357 DEBUG_MSG(3, "Nav_reload_confirmed\n");
358 if ( a_Nav_stack_size(bw) &&
359 bw->question_dialog_data == a_History_get_url(NAV_TOP(bw)) ) {
360 /* a genuine confirmation! */
361 DEBUG_MSG(3, "Nav_reload_confirmed test: OK\n");
362 Nav_reload(bw);
363 }
364
365 } else { /* window closed or cancel button */
366 DEBUG_MSG(3, "Nav_reload_refused\n");
367 }
368
369 /* cleanup */
370 bw->question_dialog_data = NULL;
371 g_free(answer->this);
372 bw->question_dialog_answer = NULL;
373 }
374
375 /*
376 * This one does a_Nav_reload's job!
377 */
378 static void Nav_reload(BrowserWindow *bw)
379 {
380 DilloUrl *url, *ReqURL;
381
382 a_Nav_cancel_expect(bw);
383 if ( a_Nav_stack_size(bw) ) {
384 url = a_History_get_url(NAV_TOP(bw));
385 ReqURL = a_Url_dup(a_History_get_url(NAV_TOP(bw)));
386 /* Let's make reload be end-to-end */
387 a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_E2EReload);
388 /* This is an explicit reload, so clear the SpamSafe flag */
389 a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) & ~URL_SpamSafe);
390 Nav_open_url(bw, ReqURL, 0);
391 a_Url_free(ReqURL);
392 }
393 }
394
395 /*
396 * Implement the RELOAD button functionality.
397 * (We haven't defined it yet ;)
398 */
399 void a_Nav_reload(BrowserWindow *bw)
400 {
401 DilloUrl *url;
402
403 a_Nav_cancel_expect(bw);
404 if ( a_Nav_stack_size(bw) ) {
405 url = a_History_get_url(NAV_TOP(bw));
406 if (URL_FLAGS(url) & URL_Post) {
407 /* Attempt to repost data, let's confirm... */
408 bw->question_dialog_data = (gpointer)url;
409 a_Interface_question_dialog(
410 bw, "Repost form data?", TRUE,
411 "OK", "Cancel", NULL, NULL, NULL,
412 (GtkSignalFunc) Nav_reload_confirmation_cb);
413
414 } else {
415 Nav_reload(bw);
416 }
417 }
418 }
419
0 #ifndef __NAV_H__
1 #define __NAV_H__
2
3 #include "browser.h"
4 #include "dw_widget.h" /* for DwWidget */
5
6
7 /* useful macros for the navigation stack */
8 #define NAV_IDX(bw, i) (bw)->nav_stack[i]
9 #define NAV_TOP(bw) (bw)->nav_stack[(bw)->nav_stack_ptr]
10
11
12 #ifdef __cplusplus
13 extern "C" {
14 #endif /* __cplusplus */
15
16 void a_Nav_push(BrowserWindow *bw, const DilloUrl *url);
17 void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url);
18 void a_Nav_vpush(void *vbw, const DilloUrl *url);
19 void a_Nav_back(BrowserWindow *bw);
20 void a_Nav_forw(BrowserWindow *bw);
21 void a_Nav_home(BrowserWindow *bw);
22 void a_Nav_reload(BrowserWindow *bw);
23 void a_Nav_init(BrowserWindow *bw);
24 void a_Nav_free(BrowserWindow *bw);
25 void a_Nav_cancel_expect (BrowserWindow *bw);
26 void a_Nav_expect_done(BrowserWindow *bw);
27 void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewBw);
28 gint a_Nav_stack_ptr(BrowserWindow *bw);
29 gint a_Nav_stack_size(BrowserWindow *bw);
30
31
32 #ifdef __cplusplus
33 }
34 #endif /* __cplusplus */
35
36 #endif /* __NAV_H__ */
37
38
0 /*
1 * File: pixmaps.h
2 *
3 * Copyright (C) 2000, 2001 Jorge Arellano Cid <jcid@dillo.org>
4 * Design by John Grantham, Dipl.-Designer; http://www.grantham.de/
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #ifndef __PIXMAPS_H__
13 #define __PIXMAPS_H__
14
15 /* XPM
16 static char * history_xpm[] = {
17 "11 20 6 1",
18 " c None",
19 ". c #000000000000",
20 "X c #9658A289BEFB",
21 "O c #FFFF9A690000",
22 "+ c #FFFFFFFF0000",
23 "@ c #FFFFFFFFFFFF",
24 " ",
25 " . ",
26 " .X. ",
27 " .XXX. ",
28 " .XXXXX. ",
29 " .XXXXXXX. ",
30 " ...XXX... ",
31 " .XXX. ",
32 " .XXX. ",
33 " .XXX. ",
34 " OOOOO ",
35 " .XXX. ",
36 " .XXX. ",
37 " ...XXX... ",
38 " .XXXXXXX. ",
39 " .XXXXX. ",
40 " .XXX. ",
41 " .X. ",
42 " . ",
43 " "};
44 */
45 /* XPM */
46 static char * left_xpm[] = {
47 "22 22 46 1",
48 " c None",
49 ". c #000000",
50 "+ c #FF7856",
51 "@ c #FF8361",
52 "# c #FF5325",
53 "$ c #FF8C68",
54 "% c #FF6636",
55 "& c #FF6333",
56 "* c #FF936B",
57 "= c #FF6E39",
58 "- c #FF5111",
59 "; c #FF8C61",
60 "> c #FFA685",
61 ", c #FF8659",
62 "' c #FF996E",
63 ") c #FF773D",
64 "! c #FF5C16",
65 "~ c #FF570F",
66 "{ c #EC510E",
67 "] c #FF894C",
68 "^ c #FF7E3B",
69 "/ c #FF681A",
70 "( c #FF6414",
71 "_ c #EC5D13",
72 ": c #C56932",
73 "< c #CB5A15",
74 "[ c #F86E19",
75 "} c #FF711A",
76 "| c #EC6918",
77 "1 c #9F5F31",
78 "2 c #C56118",
79 "3 c #F87A1E",
80 "4 c #FF7D1F",
81 "5 c #EC741D",
82 "6 c #9F6532",
83 "7 c #C56A1C",
84 "8 c #F88523",
85 "9 c #8C4B14",
86 "0 c #582F0C",
87 "a c #894A13",
88 "b c #9F6B35",
89 "c c #C57320",
90 "d c #B96C1E",
91 "e c #A47339",
92 "f c #C77C23",
93 "g c #93601C",
94 " ",
95 " ",
96 " ",
97 " ",
98 " .. ",
99 " .+. ",
100 " .@#. ",
101 " .$%&...... .... ",
102 " .*=-=;>>>,. .,,. ",
103 " .')!~~~~~~{. .{{. ",
104 " .]^/(((((((_. .__. ",
105 " .:<[}}}}}}}|. .||. ",
106 " .1234444445. .55. ",
107 " .67879000a. .aa. ",
108 " .bcd...... .... ",
109 " .ef. ",
110 " .g. ",
111 " .. ",
112 " .....",
113 " .>. ",
114 " . ",
115 " "};
116 /* XPM */
117 static char * right_xpm[] = {
118 "22 22 46 1",
119 " c None",
120 ". c #000000",
121 "+ c #569A59",
122 "@ c #228126",
123 "# c #60A463",
124 "$ c #2E9132",
125 "% c #319335",
126 "& c #65AE68",
127 "* c #52AC55",
128 "= c #80C282",
129 "- c #5BB15E",
130 "; c #319C35",
131 "> c #07880C",
132 ", c #65B568",
133 "' c #008905",
134 ") c #009405",
135 "! c #07970C",
136 "~ c #31A935",
137 "{ c #65BF68",
138 "] c #009705",
139 "^ c #00A305",
140 "/ c #07A60C",
141 "( c #2AB22E",
142 "_ c #3CB83F",
143 ": c #00A605",
144 "< c #00B305",
145 "[ c #00AE05",
146 "} c #008E04",
147 "| c #219424",
148 "1 c #00B405",
149 "2 c #00C205",
150 "3 c #00BD05",
151 "4 c #009604",
152 "5 c #218124",
153 "6 c #007003",
154 "7 c #004802",
155 "8 c #007303",
156 "9 c #00A104",
157 "0 c #00CB05",
158 "a c #218924",
159 "b c #00A304",
160 "c c #00AD04",
161 "d c #219024",
162 "e c #00B804",
163 "f c #219B24",
164 "g c #008E03",
165 " ",
166 " ",
167 " ",
168 " ",
169 " .. ",
170 " .+. ",
171 " .@#. ",
172 " .... ......$%&. ",
173 " .**. .*===-;>;,. ",
174 " .''. .'))))))!~{. ",
175 " .]]. .]^^^^^^^/(_. ",
176 " .::. .:<<<<<<<[}|. ",
177 " .11. .1222222345. ",
178 " .66. .67778909a. ",
179 " .... ......bcd. ",
180 " .ef. ",
181 " .g. ",
182 " .. ",
183 " .....",
184 " .=. ",
185 " . ",
186 " "};
187 /* XPM */
188 static char * reload_xpm[] = {
189 "22 22 147 2",
190 " c None",
191 ". c #000000",
192 "+ c #BABABA",
193 "@ c #CCCCCC",
194 "# c #CBCBCB",
195 "$ c #5D5D5D",
196 "% c #909090",
197 "& c #9C9C9C",
198 "* c #A0A0A0",
199 "= c #EBEBEB",
200 "- c #4C4C4C",
201 "; c #949494",
202 "> c #8D8D8D",
203 ", c #FFFFFF",
204 "' c #E4E4E4",
205 ") c #424242",
206 "! c #989898",
207 "~ c #A4A4A4",
208 "{ c #878787",
209 "] c #FBFBFB",
210 "^ c #E1E1E1",
211 "/ c #393939",
212 "( c #9B9B9B",
213 "_ c #A8A8A8",
214 ": c #898989",
215 "< c #ECECEC",
216 "[ c #DEDEDE",
217 "} c #313131",
218 "| c #0B0B0B",
219 "1 c #0C0C0C",
220 "2 c #2B2B2B",
221 "3 c #575757",
222 "4 c #8B8B8B",
223 "5 c #AEAEAE",
224 "6 c #8E8E8E",
225 "7 c #DDDDDD",
226 "8 c #2C2C2C",
227 "9 c #282828",
228 "0 c #666666",
229 "a c #A3A3A3",
230 "b c #C0C0C0",
231 "c c #7D7D7D",
232 "d c #3F3F3F",
233 "e c #1D1D1D",
234 "f c #6B6B6B",
235 "g c #929292",
236 "h c #585858",
237 "i c #BEBEBE",
238 "j c #4F4F4F",
239 "k c #A5A5A5",
240 "l c #BDBDBD",
241 "m c #B1B1B1",
242 "n c #A9A9A9",
243 "o c #AAAAAA",
244 "p c #696969",
245 "q c #0A0A0A",
246 "r c #5F5F5F",
247 "s c #515151",
248 "t c #545454",
249 "u c #686868",
250 "v c #868686",
251 "w c #B5B5B5",
252 "x c #B4B4B4",
253 "y c #7F907F",
254 "z c #436B43",
255 "A c #144F14",
256 "B c #004800",
257 "C c #1E1E1E",
258 "D c #838383",
259 "E c #9F9F9F",
260 "F c #262626",
261 "G c #9D9D9D",
262 "H c #B8B8B8",
263 "I c #839583",
264 "J c #577A57",
265 "K c #7E917E",
266 "L c #A7AAA7",
267 "M c #426D42",
268 "N c #004600",
269 "O c #A1A1A1",
270 "P c #404040",
271 "Q c #636363",
272 "R c #C6C6C6",
273 "S c #B7B7B7",
274 "T c #728C72",
275 "U c #B0B2B0",
276 "V c #777777",
277 "W c #323232",
278 "X c #C2C2C2",
279 "Y c #BCBCBC",
280 "Z c #8EA18E",
281 "` c #004B00",
282 " . c #979797",
283 ".. c #0F0F0F",
284 "+. c #D2D2D2",
285 "@. c #AFAFAF",
286 "#. c #C4C4C4",
287 "$. c #B0B0B0",
288 "%. c #D9D9D9",
289 "&. c #C9C9C9",
290 "*. c #7A5200",
291 "=. c #B8AE9A",
292 "-. c #101010",
293 ";. c #DFDFDF",
294 ">. c #CECECE",
295 ",. c #BFBFBF",
296 "'. c #D4D4D4",
297 "). c #7F5500",
298 "!. c #D2D1CE",
299 "~. c #B4A585",
300 "{. c #E5E5E5",
301 "]. c #845800",
302 "^. c #A58A53",
303 "/. c #C3B79F",
304 "(. c #D9D7D3",
305 "_. c #B09A6E",
306 ":. c #C6BBA5",
307 "<. c #747474",
308 "[. c #3A3A3A",
309 "}. c #EAEAEA",
310 "|. c #222222",
311 "1. c #865A00",
312 "2. c #CCC1AA",
313 "3. c #AC915B",
314 "4. c #936C1C",
315 "5. c #E0E0E0",
316 "6. c #414141",
317 "7. c #787878",
318 "8. c #F0F0F0",
319 "9. c #8C8C8C",
320 "0. c #C3C3C3",
321 "a. c #E2E2E2",
322 "b. c #F4F4F4",
323 "c. c #646464",
324 "d. c #9E9E9E",
325 "e. c #DCDCDC",
326 "f. c #6A6A6A",
327 "g. c #555555",
328 "h. c #0E0E0E",
329 "i. c #F8F8F8",
330 "j. c #E6E6E6",
331 "k. c #191919",
332 "l. c #2F2F2F",
333 "m. c #464646",
334 "n. c #444444",
335 "o. c #242424",
336 "p. c #343434",
337 " . . . . . . . . . . . ",
338 " . + @ @ @ @ @ @ @ # $ . ",
339 " . % & & & & & & & * = - . ",
340 " . ; * * * * * * * > , ' ) . ",
341 " . ! ~ ~ ~ ~ ~ ~ ~ { ] , ^ / . ",
342 " . ( _ _ _ _ _ _ _ : < , , [ } . ",
343 " . | 1 1 1 2 3 4 5 6 < , , , 7 8 . ",
344 " . 9 0 a b b a c d e f g : h h f * i . ",
345 " . j k l m n n m l o p q r f s s t u v . ",
346 " . - w x y z A A z y B w u C D & & & E * . ",
347 " F G H I J K L K M N N H O P Q R R R R S . ",
348 ". $ i S T U w w w B B B S i V W @ @ @ @ l . ",
349 ". 6 X Y Z Y Y Y ` ` ` ` Y X ...+.+.+.+.X . ",
350 ". @.R #.#.#.#.#.#.#.#.#.#.R $...%.%.%.%.&.. ",
351 ". n #.@ *.*.*.*.@ @ @ =.@ #.o -.;.;.;.;.>.. ",
352 ". v ,.'.).).).'.'.'.!.~.'.,.% -.{.{.{.{.'.. ",
353 ". 3 w '.].].^./.(./._.:.'.w <.[.}.}.}.}.%.. ",
354 " |.{ X 1.2.3.4.4.3.2.5.X > 6.7.8.8.8.8.[ . ",
355 " . [.9.0.a.}.}.}.}.a.0.> $ 9 X b.b.b.b.a.. ",
356 " . W c.d.R e.e.R d.f.g.h.; i.i.i.i.i.j.. ",
357 " . k.l.m.j j n.[.o.h.p.3 3 3 3 3 3 { . ",
358 " . . . . . . . . . . . . . . . . . "};
359 /* XPM */
360 static char * home_xpm[] = {
361 "22 22 106 2",
362 " c None",
363 ". c #190E0B",
364 "+ c #180D09",
365 "@ c #D8947D",
366 "# c #000000",
367 "$ c #190D09",
368 "% c #CC6746",
369 "& c #A7401F",
370 "* c #C65834",
371 "= c #1A0D09",
372 "- c #D16746",
373 "; c #A63F1F",
374 "> c #160D09",
375 ", c #C13206",
376 "' c #D86746",
377 ") c #AD3F1F",
378 "! c #1C0F0A",
379 "~ c #9B9376",
380 "{ c #D2502A",
381 "] c #1B0D09",
382 "^ c #DF6746",
383 "/ c #B43F1F",
384 "( c #999174",
385 "_ c #C1B588",
386 ": c #D34721",
387 "< c #D8461E",
388 "[ c #1C0D09",
389 "} c #E76746",
390 "| c #BB3F1F",
391 "1 c #1D0F0A",
392 "2 c #C5B98B",
393 "3 c #C4B889",
394 "4 c #93371F",
395 "5 c #E14821",
396 "6 c #1B0803",
397 "7 c #1D0D09",
398 "8 c #EE6746",
399 "9 c #C33F1F",
400 "0 c #1E0F0A",
401 "a c #9E9678",
402 "b c #CABD8F",
403 "c c #CCBF8E",
404 "d c #CFC290",
405 "e c #1E0D09",
406 "f c #F56746",
407 "g c #CA3F1F",
408 "h c #21110A",
409 "i c #A09879",
410 "j c #CDC091",
411 "k c #D2C593",
412 "l c #D8CA97",
413 "m c #DCCE9A",
414 "n c #1D0500",
415 "o c #C63F21",
416 "p c #9E361F",
417 "q c #130300",
418 "r c #988E6A",
419 "s c #D1C492",
420 "t c #D5C895",
421 "u c #DDCF9A",
422 "v c #E4D69F",
423 "w c #E9DAA3",
424 "x c #C9BC8C",
425 "y c #DACC98",
426 "z c #E0D29C",
427 "A c #E8D9A2",
428 "B c #EFE0A7",
429 "C c #F2E3A9",
430 "D c #5E5842",
431 "E c #676047",
432 "F c #F9E9AE",
433 "G c #655F46",
434 "H c #DBCD99",
435 "I c #C16C4E",
436 "J c #D3927C",
437 "K c #C86F51",
438 "L c #FCECB0",
439 "M c #D6D6D6",
440 "N c #D2D2D2",
441 "O c #E3D49F",
442 "P c #A82900",
443 "Q c #B82C00",
444 "R c #AB2A00",
445 "S c #FEEEB1",
446 "T c #6F6D6D",
447 "U c #6D6B6B",
448 "V c #E7D9A2",
449 "W c #B52A00",
450 "X c #C42D00",
451 "Y c #B62A00",
452 "Z c #FFEFB2",
453 "` c #696249",
454 " . c #676048",
455 ".. c #EADBA4",
456 "+. c #C12A00",
457 "@. c #D22D00",
458 "#. c #C22A00",
459 "$. c #ECDDA5",
460 "%. c #CE2A00",
461 "&. c #DF2D00",
462 "*. c #DA2A00",
463 "=. c #EC2D00",
464 "-. c #898060",
465 ";. c #851800",
466 ">. c #551000",
467 ",. c #58523D",
468 " ",
469 " . . ",
470 " + @ @ + # # # ",
471 " $ % & & % $ # * # ",
472 " = - ; > > ; - = # , # ",
473 " = ' ) ! ~ ~ ! ) ' = { # ",
474 " ] ^ / ! ( _ _ ( ! / : < # ",
475 " [ } | 1 ~ 2 3 3 2 ~ 1 4 5 6 ",
476 " 7 8 9 0 a b c d d c b a 0 9 8 7 ",
477 " e f g h i j k l m m l k j i h g f e ",
478 " n o p q r s t u v w w v u t s r q p o n ",
479 " # # # # x y z A B C C B A z y x # # # # ",
480 " # s D # # # E F G # # D s # ",
481 " # H # I J K # L # M N # H # ",
482 " # O # P Q R # S # T U # O # ",
483 " # V # W X Y # Z ` # # .V # ",
484 " # ..# +.@.#.# Z Z Z S S ..# ",
485 " # $.# %.&.%.# Z Z Z Z Z $.# ",
486 " # $.# *.=.*.# Z Z Z Z Z $.# ",
487 " # -.# ;.>.;.# ,.,.,.,.,.-.# ",
488 " # # # # # # # # # # # # # # ",
489 " "};
490 /* XPM */
491 static char * save_xpm[] = {
492 "22 22 59 1",
493 " c None",
494 ". c #000000",
495 "+ c #638163",
496 "@ c #4D5A4D",
497 "# c #A4BDA4",
498 "$ c #184318",
499 "% c #0E280E",
500 "& c #4B7E4B",
501 "* c #194419",
502 "= c #0F290F",
503 "- c #508350",
504 "; c #1B481B",
505 "> c #102B10",
506 ", c #568956",
507 "' c #1D4C1D",
508 ") c #112D11",
509 "! c #5D905D",
510 "~ c #205020",
511 "{ c #133013",
512 "] c #639663",
513 "^ c #235223",
514 "/ c #153115",
515 "( c #6B9E6B",
516 "_ c #275627",
517 ": c #173317",
518 "< c #73A673",
519 "[ c #2B5A2B",
520 "} c #2E612E",
521 "| c #193619",
522 "1 c #2E5D2E",
523 "2 c #152A15",
524 "3 c #326132",
525 "4 c #838383",
526 "5 c #9D9D9D",
527 "6 c #B3B3B3",
528 "7 c #979797",
529 "8 c #366536",
530 "9 c #585858",
531 "0 c #565656",
532 "a c #727272",
533 "b c #6A6A6A",
534 "c c #386838",
535 "d c #848484",
536 "e c #7A7A7A",
537 "f c #3C6B3C",
538 "g c #999999",
539 "h c #8E8E8E",
540 "i c #366036",
541 "j c #3F6E3F",
542 "k c #BDBDBD",
543 "l c #BABABA",
544 "m c #ADADAD",
545 "n c #A0A0A0",
546 "o c #162616",
547 "p c #264126",
548 "q c #949494",
549 "r c #696969",
550 "s c #424242",
551 "t c #676767",
552 " ",
553 " ",
554 " ................ ",
555 " .+@############@+. ",
556 " .$%&&&&&&&&&&&&%$. ",
557 " .*=------------=*. ",
558 " .;>,,,,,,,,,,,,>;. ",
559 " .')!!!!!!!!!!!!)'. ",
560 " .~{]]]]]]]]]]]]{~. ",
561 " .^/((((((((((((/^. ",
562 " ._:<<<<<<<<<<<<:_. ",
563 " .[}||||||||||||}[. ",
564 " .12............21. ",
565 " .3.444566666667.3. ",
566 " .8.9..0aaaaaaab.8. ",
567 " .c.d..eddddddde.c. ",
568 " .f.g..hgggggggh.i. ",
569 " .j.k..lmmmmmmmn.o. ",
570 " .p.qqqrssssssst.. ",
571 " ............... ",
572 " ",
573 " "};
574
575 /* XPM */
576 static char * stop_xpm[] = {
577 "22 22 77 1",
578 " c None",
579 ". c #000000",
580 "+ c #703434",
581 "@ c #E57F7F",
582 "# c #E68080",
583 "$ c #6F2929",
584 "% c #DB4343",
585 "& c #D31A1A",
586 "* c #CE0000",
587 "= c #DC4343",
588 "- c #D51A1A",
589 "; c #D00000",
590 "> c #702929",
591 ", c #DF4343",
592 "' c #D71A1A",
593 ") c #D30000",
594 "! c #6F1B1B",
595 "~ c #E14343",
596 "{ c #DA1A1A",
597 "] c #D60000",
598 "^ c #A66666",
599 "/ c #841717",
600 "( c #DA0404",
601 "_ c #DD1A1A",
602 ": c #D90000",
603 "< c #D56B6B",
604 "[ c #FFFFFF",
605 "} c #E7D5D5",
606 "| c #861717",
607 "1 c #CC0000",
608 "2 c #DC0000",
609 "3 c #E64C4C",
610 "4 c #FADEDE",
611 "5 c #EAD5D5",
612 "6 c #722C2C",
613 "7 c #CF0000",
614 "8 c #E00000",
615 "9 c #EB5C5C",
616 "0 c #FBDFDF",
617 "a c #FDF8F8",
618 "b c #E40000",
619 "c c #DE3535",
620 "d c #FEF8F8",
621 "e c #E70000",
622 "f c #971C1C",
623 "g c #EED5D5",
624 "h c #FEFAFA",
625 "i c #EB0000",
626 "j c #AA1C1C",
627 "k c #EFD5D5",
628 "l c #FDE3E3",
629 "m c #F69595",
630 "n c #C50000",
631 "o c #D50000",
632 "p c #EF0000",
633 "q c #F77777",
634 "r c #FDE4E4",
635 "s c #F56565",
636 "t c #4D0000",
637 "u c #9E0000",
638 "v c #D70000",
639 "w c #F20000",
640 "x c #FAA7A7",
641 "y c #F76969",
642 "z c #420000",
643 "A c #A00000",
644 "B c #DA0000",
645 "C c #F50000",
646 "D c #A20000",
647 "E c #DD0000",
648 "F c #F80000",
649 "G c #430000",
650 "H c #A40000",
651 "I c #DF0000",
652 "J c #FB0000",
653 "K c #380000",
654 "L c #570000",
655 " ",
656 " ",
657 " ......... ",
658 " .+@#####@+. ",
659 " .$%&*****&%$. ",
660 " .$=-;;;;;;;-=$. ",
661 " .>,')))))))))',>. ",
662 " .!~{]]^/]]]/^]]{~!. ",
663 " .(_::<[}|:|}[<::_(. ",
664 " .122234[565[432221. ",
665 " .7888890[a[0988887. ",
666 " .)bbbbbcd[dcbbbbb). ",
667 " .]eeeefg[h[gfeeee]. ",
668 " .:iiijk[lml[kjiii:. ",
669 " .noppq[rspsr[qppon. ",
670 " .tuvwwxywwwyxwwvut. ",
671 " .zABCCCCCCCCCBAz. ",
672 " .zDEFFFFFFFEDz. ",
673 " .GHIJJJJJIHG. ",
674 " .KLLLLLLLK. ",
675 " ......... ",
676 " "};
677 /* XPM */
678 static char * bm_xpm[] = {
679 "22 22 86 1",
680 " c None",
681 ". c #000000",
682 "+ c #150B0A",
683 "@ c #A46C6A",
684 "# c #B15652",
685 "$ c #C68380",
686 "% c #840600",
687 "& c #8F0600",
688 "* c #979797",
689 "= c #B3B3B3",
690 "- c #880600",
691 "; c #930600",
692 "> c #616161",
693 ", c #696969",
694 "' c #595959",
695 ") c #8D0600",
696 "! c #980600",
697 "~ c #666666",
698 "{ c #6E6E6E",
699 "] c #5E5E5E",
700 "^ c #920600",
701 "/ c #9E0700",
702 "( c #6B6B6B",
703 "_ c #747474",
704 ": c #636363",
705 "< c #970600",
706 "[ c #A30700",
707 "} c #727272",
708 "| c #7B7B7B",
709 "1 c #9D0600",
710 "2 c #AA0700",
711 "3 c #777777",
712 "4 c #818181",
713 "5 c #A40600",
714 "6 c #B10700",
715 "7 c #7F7F7F",
716 "8 c #898989",
717 "9 c #757575",
718 "0 c #A90700",
719 "a c #B70800",
720 "b c #868686",
721 "c c #919191",
722 "d c #7C7C7C",
723 "e c #BF0800",
724 "f c #8E8E8E",
725 "g c #999999",
726 "h c #828282",
727 "i c #C60900",
728 "j c #959595",
729 "k c #A1A1A1",
730 "l c #BD0800",
731 "m c #CC0900",
732 "n c #9C9C9C",
733 "o c #A9A9A9",
734 "p c #909090",
735 "q c #C40800",
736 "r c #D40900",
737 "s c #A3A3A3",
738 "t c #B0B0B0",
739 "u c #969696",
740 "v c #CA0900",
741 "w c #DA0A00",
742 "x c #B7B7B7",
743 "y c #D00900",
744 "z c #E10A00",
745 "A c #AFAFAF",
746 "B c #BDBDBD",
747 "C c #D70900",
748 "D c #E80A00",
749 "E c #B4B4B4",
750 "F c #C3C3C3",
751 "G c #A6A6A6",
752 "H c #DB0900",
753 "I c #E10900",
754 "J c #454545",
755 "K c #CD0900",
756 "L c #A00700",
757 "M c #7B1510",
758 "N c #A40700",
759 "O c #A20700",
760 "P c #3E0300",
761 "Q c #4A0300",
762 "R c #A90800",
763 "S c #540400",
764 "T c #010000",
765 "U c #660400",
766 " ...... ",
767 " +@#$$$#. ",
768 " .......%&&&%.... ",
769 " .*====.-;;;-.=*. ",
770 " .>,',,.)!!!).,>. ",
771 " .~{]{{.^///^.{~. ",
772 " .(_:__.<[[[<._(. ",
773 " .}|,||.12221.|}. ",
774 " .34{44.56665.43. ",
775 " .78988.0aaa0.87. ",
776 " .bcdcc.6eee6.cb. ",
777 " .fghgg.aiiia.gf. ",
778 " .jk8kk.lmmml.kj. ",
779 " .nopoo.qrrrq.on. ",
780 " .stutt.vwwwv.ts. ",
781 " .oxnxx.yzzzy.xo. ",
782 " .ABkBB.CDDDC.BA. ",
783 " .EFGFF.HIIIH.FE. ",
784 " .(JJJJ.KLMNy.J(. ",
785 " .......OP.QR.... ",
786 " .S. TU. ",
787 " . . "};
788
789 /* Small icons here */
790
791 /* XPM */
792 static char * s_left_xpm[] = {
793 "16 16 33 1",
794 " c None",
795 ". c #000000",
796 "+ c #1F120A",
797 "@ c #FF9A59",
798 "# c #DE7E42",
799 "$ c #FF7A26",
800 "% c #DE8247",
801 "& c #FF8335",
802 "* c #FF8232",
803 "= c #FF6A0C",
804 "- c #FF9D5E",
805 "; c #FFB382",
806 "> c #FF9755",
807 ", c #FF6605",
808 "' c #EC5E05",
809 ") c #1F0F05",
810 "! c #DE691E",
811 "~ c #FF7F2E",
812 "{ c #180A00",
813 "] c #A44103",
814 "^ c #CB5104",
815 "/ c #F86305",
816 "( c #120700",
817 "_ c #7E3203",
818 ": c #C54F04",
819 "< c #8C3803",
820 "[ c #582302",
821 "} c #893703",
822 "| c #B94A04",
823 "1 c #833403",
824 "2 c #C75004",
825 "3 c #110700",
826 "4 c #933B03",
827 " ",
828 " .. ",
829 " +@. ",
830 " +#$. ",
831 " +%&*.... ....",
832 " +%&=&-;>. .>>.",
833 " +%&=,,,,'. .''.",
834 ")!~=,,,,,'. .''.",
835 "{]^/,,,,,'. .''.",
836 " (_:/,,,,'. .''.",
837 " (_:/:<[}. .}}.",
838 " (_:|.... ....",
839 " (12. ",
840 " 34. .....",
841 " .. .;. ",
842 " . "};
843 /* XPM */
844 static char * s_right_xpm[] = {
845 "16 16 56 1",
846 " c None",
847 ". c #000000",
848 "+ c #569A59",
849 "@ c #0A120A",
850 "# c #228126",
851 "$ c #3F8342",
852 "% c #09130A",
853 "& c #2E9132",
854 "* c #319335",
855 "= c #448D47",
856 "- c #09140A",
857 "; c #52AC55",
858 "> c #80C282",
859 ", c #5BB15E",
860 "' c #319C35",
861 ") c #07880C",
862 "! c #449447",
863 "~ c #09150A",
864 "{ c #008905",
865 "] c #009405",
866 "^ c #07970C",
867 "/ c #31A935",
868 "( c #449E47",
869 "_ c #09160A",
870 ": c #009705",
871 "< c #00A305",
872 "[ c #07A60C",
873 "} c #2AB22E",
874 "| c #1B971E",
875 "1 c #041505",
876 "2 c #00A605",
877 "3 c #00B305",
878 "4 c #00AE05",
879 "5 c #008E04",
880 "6 c #007303",
881 "7 c #001100",
882 "8 c #00B405",
883 "9 c #00C205",
884 "0 c #00BD05",
885 "a c #009604",
886 "b c #006003",
887 "c c #000D00",
888 "d c #007003",
889 "e c #004802",
890 "f c #00A104",
891 "g c #00CB05",
892 "h c #006803",
893 "i c #000E00",
894 "j c #00A304",
895 "k c #00AD04",
896 "l c #006F03",
897 "m c #000F00",
898 "n c #00B804",
899 "o c #007A03",
900 "p c #001000",
901 "q c #008E03",
902 " ",
903 " .. ",
904 " .+@ ",
905 " .#$% ",
906 ".... ....&*=- ",
907 ".;;. .;>,')'!~ ",
908 ".{{. .{]]]]^/(_ ",
909 ".::. .:<<<<<[}|1",
910 ".22. .2333334567",
911 ".88. .899990abc ",
912 ".dd. .de6fgfhi ",
913 ".... ....jklm ",
914 " .nop ",
915 " .qpb....",
916 " .. .>. ",
917 " . "};
918 /* XPM */
919 static char * s_home_xpm[] = {
920 "16 16 54 1",
921 " c None",
922 ". c #170E0B",
923 "+ c #000000",
924 "@ c #170C09",
925 "# c #B0705B",
926 "$ c #B35636",
927 "% c #A13F20",
928 "& c #661B03",
929 "* c #AA3009",
930 "= c #190C09",
931 "- c #AC3F20",
932 "; c #721C03",
933 "> c #8E8875",
934 ", c #C24F2D",
935 "' c #1A0C09",
936 ") c #B93F20",
937 "! c #7C1C03",
938 "~ c #8F876F",
939 "{ c #A9A081",
940 "] c #A72403",
941 "^ c #CE4522",
942 "/ c #1B0C09",
943 "( c #C63F20",
944 "_ c #861C03",
945 ": c #A9A080",
946 "< c #FDEDB3",
947 "[ c #631503",
948 "} c #CC2903",
949 "| c #1B0703",
950 "1 c #1D0C08",
951 "2 c #D03E20",
952 "3 c #871902",
953 "4 c #847C67",
954 "5 c #FFEFB2",
955 "6 c #847C68",
956 "7 c #851902",
957 "8 c #CF3A1B",
958 "9 c #A81E02",
959 "0 c #721402",
960 "a c #9A9070",
961 "b c #8B8163",
962 "c c #9C926D",
963 "d c #FDEDB1",
964 "e c #EADBA4",
965 "f c #696249",
966 "g c #ECDDA5",
967 "h c #C25834",
968 "i c #FFFFFF",
969 "j c #BE2D00",
970 "k c #BF0000",
971 "l c #898060",
972 "m c #58523D",
973 "n c #D90000",
974 "o c #BB0000",
975 " ",
976 " .. +++ ",
977 " @##@ +$+ ",
978 " @%&&%@+*+ ",
979 " =-;>>;-=,+ ",
980 " ')!~{{~!]^+ ",
981 " /(_~:<<:~[}| ",
982 " 1234:<55<:6781 ",
983 "+90a{<5555<:b09+",
984 "+++cd555555dc+++",
985 " +e5f+f5f+fe+ ",
986 " +g5+h+5+i+g+ ",
987 " +g5+j+5f+fg+ ",
988 " +g5+k+5555g+ ",
989 " +lm+n+mmmml+ ",
990 " ++++o+++++++ "};
991 /* XPM */
992 static char * s_reload_xpm[] = {
993 "16 16 74 1",
994 " c None",
995 ". c #000000",
996 "+ c #282828",
997 "@ c #666666",
998 "# c #9B9B9B",
999 "$ c #BFBFBF",
1000 "% c #4F4F4F",
1001 "& c #A5A5A5",
1002 "* c #BDBDBD",
1003 "= c #B1B1B1",
1004 "- c #A9A9A9",
1005 "; c #4C4C4C",
1006 "> c #B5B5B5",
1007 ", c #B4B4B4",
1008 "' c #7F907F",
1009 ") c #436B43",
1010 "! c #144F14",
1011 "~ c #004800",
1012 "{ c #262626",
1013 "] c #9D9D9D",
1014 "^ c #B8B8B8",
1015 "/ c #839583",
1016 "( c #577A57",
1017 "_ c #7E917E",
1018 ": c #A7AAA7",
1019 "< c #426D42",
1020 "[ c #004600",
1021 "} c #5D5D5D",
1022 "| c #BEBEBE",
1023 "1 c #B7B7B7",
1024 "2 c #728C72",
1025 "3 c #B0B2B0",
1026 "4 c #8E8E8E",
1027 "5 c #C2C2C2",
1028 "6 c #BCBCBC",
1029 "7 c #8EA18E",
1030 "8 c #004B00",
1031 "9 c #AFAFAF",
1032 "0 c #C6C6C6",
1033 "a c #C4C4C4",
1034 "b c #CCCCCC",
1035 "c c #7A5200",
1036 "d c #B8AE9A",
1037 "e c #868686",
1038 "f c #D4D4D4",
1039 "g c #7F5500",
1040 "h c #D2D1CE",
1041 "i c #B4A585",
1042 "j c #575757",
1043 "k c #845800",
1044 "l c #A58A53",
1045 "m c #C3B79F",
1046 "n c #D9D7D3",
1047 "o c #B09A6E",
1048 "p c #C6BBA5",
1049 "q c #222222",
1050 "r c #878787",
1051 "s c #865A00",
1052 "t c #CCC1AA",
1053 "u c #AC915B",
1054 "v c #936C1C",
1055 "w c #E0E0E0",
1056 "x c #3A3A3A",
1057 "y c #8C8C8C",
1058 "z c #C3C3C3",
1059 "A c #E2E2E2",
1060 "B c #EAEAEA",
1061 "C c #323232",
1062 "D c #646464",
1063 "E c #9E9E9E",
1064 "F c #DCDCDC",
1065 "G c #191919",
1066 "H c #2F2F2F",
1067 "I c #404040",
1068 " ...... ",
1069 " .+@#$$#@+. ",
1070 " .%&*=--=*&%. ",
1071 " .;>,')!!)'~>;. ",
1072 " {]^/(_:_<[[^]{ ",
1073 ".}|123>>>~~~1|}.",
1074 ".45676668888654.",
1075 ".90aaaaaaaaaa09.",
1076 ".-abccccbbbdba-.",
1077 ".e$fgggfffhif$e.",
1078 ".j>fkklmnmopf>j.",
1079 " qr5stuvvutw5rq ",
1080 " .xyzABBBBAzyx. ",
1081 " .CDE0FF0EDC. ",
1082 " .GHI%%IHG. ",
1083 " ...... "};
1084 /* XPM */
1085 static char * s_save_xpm[] = {
1086 "16 16 51 1",
1087 " c None",
1088 ". c #000000",
1089 "+ c #638163",
1090 "@ c #8CA28C",
1091 "# c #A4BDA4",
1092 "$ c #184318",
1093 "% c #1A481A",
1094 "& c #4C7F4C",
1095 "* c #1A461A",
1096 "= c #1C4C1C",
1097 "- c #528552",
1098 "; c #1B4A1B",
1099 "> c #1D501D",
1100 ", c #598C59",
1101 "' c #1F4E1F",
1102 ") c #215421",
1103 "! c #619461",
1104 "~ c #225122",
1105 "{ c #255825",
1106 "] c #699C69",
1107 "^ c #275627",
1108 "/ c #2A5D2A",
1109 "( c #72A572",
1110 "_ c #2B5A2B",
1111 ": c #2E612E",
1112 "< c #2F5E2F",
1113 "[ c #152A15",
1114 "} c #336233",
1115 "| c #838383",
1116 "1 c #9D9D9D",
1117 "2 c #B3B3B3",
1118 "3 c #979797",
1119 "4 c #376637",
1120 "5 c #5A5A5A",
1121 "6 c #585858",
1122 "7 c #757575",
1123 "8 c #6C6C6C",
1124 "9 c #3A6A3A",
1125 "0 c #8C8C8C",
1126 "a c #828282",
1127 "b c #345E34",
1128 "c c #3E6D3E",
1129 "d c #B6B6B6",
1130 "e c #A5A5A5",
1131 "f c #999999",
1132 "g c #162616",
1133 "h c #264126",
1134 "i c #919191",
1135 "j c #676767",
1136 "k c #414141",
1137 "l c #656565",
1138 "................",
1139 ".+@##########@+.",
1140 ".$%&&&&&&&&&&%$.",
1141 ".*=----------=*.",
1142 ".;>,,,,,,,,,,>;.",
1143 ".')!!!!!!!!!!)'.",
1144 ".~{]]]]]]]]]]{~.",
1145 ".^/((((((((((/^.",
1146 "._::::::::::::_.",
1147 ".<[..........[<.",
1148 ".}.|||1222223.}.",
1149 ".4.5..6777778.4.",
1150 ".9.0..a00000a.b.",
1151 ".c.d..2eeeeef.g.",
1152 ".h.iiijkkkkkl.. ",
1153 " ............. "};
1154 /* XPM */
1155 static char * s_stop_xpm[] = {
1156 "16 16 65 1",
1157 " c None",
1158 ". c #000000",
1159 "+ c #5A3435",
1160 "@ c #BF7F80",
1161 "# c #BF8081",
1162 "$ c #562929",
1163 "% c #A54344",
1164 "& c #911A1C",
1165 "* c #850002",
1166 "= c #582929",
1167 "- c #AA4344",
1168 "; c #981A1C",
1169 "> c #8C0002",
1170 ", c #551B1C",
1171 "' c #B04345",
1172 ") c #9F1A1D",
1173 "! c #940003",
1174 "~ c #926667",
1175 "{ c #661C1E",
1176 "] c #A00407",
1177 "^ c #A81A1D",
1178 "/ c #9E0003",
1179 "( c #B86B6D",
1180 "_ c #FFFFFF",
1181 ": c #E2D5D5",
1182 "< c #581C1D",
1183 "[ c #9C0003",
1184 "} c #A90003",
1185 "| c #C24C4E",
1186 "1 c #F4DDDE",
1187 "2 c #E9DADA",
1188 "3 c #A70003",
1189 "4 c #B40003",
1190 "5 c #C4383A",
1191 "6 c #F5DEDE",
1192 "7 c #B10004",
1193 "8 c #BF0004",
1194 "9 c #9A1C1F",
1195 "0 c #F0D9D9",
1196 "a c #BB0004",
1197 "b c #CA0004",
1198 "c c #971C1F",
1199 "d c #ECD5D5",
1200 "e c #FAE6E6",
1201 "f c #AF0003",
1202 "g c #BE0004",
1203 "h c #D50004",
1204 "i c #E87779",
1205 "j c #FBE5E5",
1206 "k c #EA7E80",
1207 "l c #470001",
1208 "m c #930003",
1209 "n c #C70004",
1210 "o c #E00004",
1211 "p c #F5A7A9",
1212 "q c #EC6568",
1213 "r c #3F0001",
1214 "s c #990003",
1215 "t c #D00004",
1216 "u c #EA0004",
1217 "v c #410001",
1218 "w c #D70004",
1219 "x c #F20005",
1220 "y c #370001",
1221 "z c #560002",
1222 " ........ ",
1223 " .+@####@+. ",
1224 " .$%&****&%$. ",
1225 " .=-;>>>>>>;-=. ",
1226 ".,')!~{!!{~!)',.",
1227 ".]^/(_:<<:_(/^].",
1228 ".[}}|1_22_1|}}[.",
1229 ".344456__654443.",
1230 ".788890__098887.",
1231 ".abbcd_ee_dcbba.",
1232 ".fghi_jkkj_ihgf.",
1233 ".lmnopqooqponml.",
1234 " .rstuuuuuutsr. ",
1235 " .v/wxxxxw/v. ",
1236 " .yzzzzzzy. ",
1237 " ........ "};
1238
1239 /* XPM */
1240 static char * s_bm_xpm[] = {
1241 "16 16 63 1",
1242 " c None",
1243 ". c #000000",
1244 "+ c #B15652",
1245 "@ c #C68380",
1246 "# c #979797",
1247 "$ c #B3B3B3",
1248 "% c #999999",
1249 "& c #860600",
1250 "* c #910600",
1251 "= c #646464",
1252 "- c #6C6C6C",
1253 "; c #5C5C5C",
1254 "> c #8B0600",
1255 ", c #960600",
1256 "' c #6A6A6A",
1257 ") c #737373",
1258 "! c #626262",
1259 "~ c #920600",
1260 "{ c #9E0700",
1261 "] c #747474",
1262 "^ c #7D7D7D",
1263 "/ c #990600",
1264 "( c #A50700",
1265 "_ c #7E7E7E",
1266 ": c #888888",
1267 "< c #A10600",
1268 "[ c #AE0700",
1269 "} c #939393",
1270 "| c #A90700",
1271 "1 c #B70800",
1272 "2 c #9F9F9F",
1273 "3 c #B30700",
1274 "4 c #C10800",
1275 "5 c #9D9D9D",
1276 "6 c #AAAAAA",
1277 "7 c #919191",
1278 "8 c #BB0800",
1279 "9 c #CA0900",
1280 "0 c #A8A8A8",
1281 "a c #B5B5B5",
1282 "b c #9A9A9A",
1283 "c c #C40800",
1284 "d c #D40900",
1285 "e c #B0B0B0",
1286 "f c #BEBEBE",
1287 "g c #A2A2A2",
1288 "h c #CD0900",
1289 "i c #DD0A00",
1290 "j c #444444",
1291 "k c #3A3A3A",
1292 "l c #D50900",
1293 "m c #DA0900",
1294 "n c #C80800",
1295 "o c #9C0700",
1296 "p c #680500",
1297 "q c #A00700",
1298 "r c #CB0900",
1299 "s c #3D0300",
1300 "t c #490300",
1301 "u c #A70800",
1302 "v c #540400",
1303 "w c #010000",
1304 "x c #660400",
1305 " ....... ",
1306 " ......+@@@+... ",
1307 " .#$%$.&***&.#. ",
1308 " .=-;-.>,,,>.=. ",
1309 " .')!).~{{{~.'. ",
1310 " .]^'^./(((/.]. ",
1311 " ._:]:.<[[[<._. ",
1312 " .:}^}.|111|.:. ",
1313 " .}2:2.34443.}. ",
1314 " .5676.89998.5. ",
1315 " .0aba.cdddc.0. ",
1316 " .efgf.hiiih.e. ",
1317 " .'jkj.lmmml.'. ",
1318 " ......nopqr... ",
1319 " .qs.tu. ",
1320 " .v. wx. "};
1321
1322 /* XPM */
1323 static char * s_new_xpm[] = {
1324 "11 11 35 1",
1325 " c None",
1326 ". c #000000",
1327 "+ c #482929",
1328 "@ c #0F0808",
1329 "# c #390606",
1330 "$ c #A04242",
1331 "% c #925050",
1332 "& c #0F0707",
1333 "* c #0B0000",
1334 "= c #500000",
1335 "- c #8C0101",
1336 "; c #944444",
1337 "> c #221515",
1338 ", c #0A0000",
1339 "' c #560000",
1340 ") c #A10909",
1341 "! c #AE3636",
1342 "~ c #230000",
1343 "{ c #AE0000",
1344 "] c #B40000",
1345 "^ c #180808",
1346 "/ c #B43535",
1347 "( c #C40000",
1348 "_ c #960000",
1349 ": c #190606",
1350 "< c #C02B2B",
1351 "[ c #E00000",
1352 "} c #6B0000",
1353 "| c #120000",
1354 "1 c #590000",
1355 "2 c #A30000",
1356 "3 c #680000",
1357 "4 c #0F0000",
1358 "5 c #350000",
1359 "6 c #100000",
1360 " . . ",
1361 " .+@ @+. ",
1362 ".#$%& &%$#.",
1363 " *=-;>;-=* ",
1364 " ,')!)', ",
1365 " ~{]{~ ",
1366 " ^/(_(/^ ",
1367 " :<[}|}[<: ",
1368 ".1234 4321.",
1369 " .56 65. ",
1370 " . . "};
1371
1372 /* XPM */
1373 static char * search_xpm[] = {
1374 "14 16 11 1",
1375 " c None",
1376 ". c #000000",
1377 "+ c #EEEEEE",
1378 "[ c #EE0000",
1379 "} c #CC0000",
1380 "| c #BB0000",
1381 "1 c #AA0000",
1382 "2 c #880000",
1383 "3 c #660000",
1384 "4 c #440000",
1385 "5 c #330000",
1386 " ..... ",
1387 " . . ",
1388 " . + . ",
1389 ". +++ . ",
1390 ". + . ",
1391 ". . ",
1392 ". . ",
1393 " . . ",
1394 " . . ",
1395 " ...545 ",
1396 " 424 ",
1397 " 313 ",
1398 " 2|2 ",
1399 " 2}2 ",
1400 " 2}2 ",
1401 " 11 "};
1402
1403 /* XPM */
1404 static char * full_screen_on_xpm[] = {
1405 "13 15 2 1",
1406 " c None",
1407 ". c #000000",
1408 " ",
1409 ".............",
1410 ". . .",
1411 ". ... .",
1412 ". ..... .",
1413 ". ....... .",
1414 ". . .",
1415 ". . .",
1416 ". . .",
1417 ". ....... .",
1418 ". ..... .",
1419 ". ... .",
1420 ". . .",
1421 ".............",
1422 " "};
1423
1424 /* XPM */
1425 static char * full_screen_off_xpm[] = {
1426 "13 15 2 1",
1427 " c None",
1428 ". c #000000",
1429 " ",
1430 ".............",
1431 ". . . . . . .",
1432 ".. . . . . ..",
1433 ".............",
1434 ". .",
1435 ". .",
1436 ". .",
1437 ". .",
1438 ". .",
1439 ". .",
1440 ".............",
1441 ". . . . . . .",
1442 ".............",
1443 " "};
1444
1445 /* XPM */
1446 static char * mini_bug_xpm[] = {
1447 "15 16 6 1",
1448 " c None",
1449 ". c black",
1450 "X c #c0c0c0",
1451 "o c #808080",
1452 "b c #303030",
1453 "# c white"
1454 " ",
1455 " . . ",
1456 " ... ",
1457 " X.....X ",
1458 " o.#...o ",
1459 " o.#...o.o ",
1460 " ..#oXo... ",
1461 " .....X..... ",
1462 " ....X.... ",
1463 " .o...X...o. ",
1464 " ...X... ",
1465 " .X..X..X. ",
1466 " .o. ",
1467 " .bbb.",
1468 " .o. ",
1469 " . "};
1470
1471 /* XPM */
1472 static char * mini_ok_xpm[] = {
1473 "15 15 5 1",
1474 "@ c #000000",
1475 "a c #808080",
1476 "b c #303030",
1477 "c c #606060",
1478 " s none m none c none",
1479 " ",
1480 " ",
1481 " @ ",
1482 " @@ ",
1483 " @@@ ",
1484 " @@@ ",
1485 " @@ @@@ ",
1486 " @@@ @@@ ",
1487 " @@@ @@@ ",
1488 " @@@@@@ ",
1489 " @@@@@ ",
1490 " @@@@ ",
1491 " @@ @bbb@",
1492 " @a@ ",
1493 " @ "
1494 };
1495
1496 #endif /* __PIXMAPS_H__ */
0 /*
1 * File: plain.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * Module for decoding a text/plain object into a gtk_page widget.
14 */
15
16 #include <string.h> /* for memcpy and memmove */
17 #include <math.h> /* for rint() */
18 #include <gtk/gtk.h>
19
20 #include "prefs.h"
21 #include "dw_page.h"
22 #include "cache.h"
23 #include "browser.h"
24 #include "web.h"
25 #include "interface.h"
26 #include "progressbar.h"
27 #include "misc.h"
28 #include "history.h"
29 #include "nav.h"
30 #include "menu.h"
31
32
33 typedef struct _DilloPlain {
34 DwWidget *dw;
35 size_t Start_Ofs; /* Offset of where to start reading next */
36 DwStyle *style;
37 BrowserWindow *bw;
38 gint state;
39 } DilloPlain;
40
41 /* FSM states */
42 enum {
43 ST_SeekingEol,
44 ST_Eol,
45 ST_Eof
46 };
47
48 /*
49 * Forward declarations
50 */
51 static void Plain_write(DilloPlain *plain, void *Buf, guint BufSize, gint Eof);
52 static void Plain_callback(int Op, CacheClient_t *Client);
53
54 /* exported function */
55 DwWidget* a_Plain_text(const char *type, void *P, CA_Callback_t *Call,
56 void **Data);
57
58
59 /*
60 * Popup the page menu ("button_press_event" callback of the viewport)
61 */
62 static int Plain_page_menu(GtkWidget *viewport, GdkEventButton *event,
63 BrowserWindow *bw)
64 {
65 if (event->button == 3) {
66 a_Menu_popup_set_url(bw, a_History_get_url(NAV_TOP(bw)));
67 gtk_menu_popup(GTK_MENU(bw->menu_popup.over_page), NULL, NULL,
68 NULL, NULL, event->button, event->time);
69 return TRUE;
70 } else
71 return FALSE;
72 }
73
74 /*
75 * Create and initialize a new DilloPlain structure.
76 */
77 static DilloPlain *Plain_new(BrowserWindow *bw)
78 {
79 DilloPlain *plain;
80 DwPage *page;
81 DwStyle style_attrs;
82 DwStyleFont font_attrs;
83
84 plain = g_new(DilloPlain, 1);
85 plain->state = ST_SeekingEol;
86 plain->Start_Ofs = 0;
87 plain->bw = bw;
88 plain->dw = a_Dw_page_new();
89 page = (DwPage *) plain->dw;
90
91 /* Create the font and attribute for the page. */
92 font_attrs.name = prefs.fw_fontname;
93 font_attrs.size = rint(12.0 * prefs.font_factor);
94 font_attrs.weight = 400;
95 font_attrs.style = DW_STYLE_FONT_STYLE_NORMAL;
96
97 a_Dw_style_init_values (&style_attrs, plain->bw->main_window->window);
98 style_attrs.font = a_Dw_style_font_new (&font_attrs);
99 style_attrs.color = a_Dw_style_color_new (prefs.text_color,
100 plain->bw->main_window->window);
101 plain->style = a_Dw_style_new (&style_attrs,
102 plain->bw->main_window->window);
103 /* a_Dw_widget_set_style (plain->dw, plain->style); */
104
105 /* The context menu */
106 gtk_signal_connect_while_alive
107 (GTK_OBJECT(GTK_BIN(plain->bw->docwin)->child),"button_press_event",
108 GTK_SIGNAL_FUNC(Plain_page_menu), (gpointer)plain->bw,
109 GTK_OBJECT (page));
110
111 return plain;
112 }
113
114 /*
115 * Set callback function and callback data for "text/" MIME major-type.
116 */
117 DwWidget* a_Plain_text(const char *type, void *P, CA_Callback_t *Call,
118 void **Data)
119 {
120 DilloWeb *web = P;
121 DilloPlain *plain = Plain_new(web->bw);
122
123 *Call = (CA_Callback_t)Plain_callback;
124 *Data = (void*)plain;
125
126 return plain->dw;
127 }
128
129 /*
130 * This function is a cache client
131 */
132 static void Plain_callback(int Op, CacheClient_t *Client)
133 {
134 DilloPlain *plain= Client->CbData;
135 DwPage *page = (DwPage *)plain->dw;
136
137 if ( Op ) {
138 /* Do the last line: */
139 if (plain->Start_Ofs < Client->BufSize)
140 Plain_write(plain, Client->Buf, Client->BufSize, 1);
141 /* remove this client from our active list */
142 a_Interface_close_client(plain->bw, Client->Key);
143 /* set progress bar insensitive */
144 a_Progressbar_update(plain->bw->progress, NULL, 0);
145
146 a_Dw_style_unref (plain->style);
147 g_free(plain);
148 } else {
149 Plain_write(plain, Client->Buf, Client->BufSize, 0);
150 }
151
152 a_Dw_page_flush(page);
153 }
154
155 /*
156 * Here we parse plain text and put it into the page structure.
157 * (This function is called by Plain_callback whenever there's new data)
158 */
159 static void Plain_write(DilloPlain *plain, void *Buf, guint BufSize, gint Eof)
160 {
161 DwPage *page = (DwPage *)plain->dw;
162 char *Start;
163 char *data;
164 guint i, len, MaxBytes;
165
166 Start = (char*)Buf + plain->Start_Ofs;
167 MaxBytes = BufSize - plain->Start_Ofs;
168 i = len = 0;
169 while ( i < MaxBytes ) {
170 switch ( plain->state ) {
171 case ST_SeekingEol:
172 if ( Start[i] == '\n' || Start[i] == '\r' )
173 plain->state = ST_Eol;
174 else {
175 ++i; ++len;
176 }
177 break;
178 case ST_Eol:
179 data = g_strndup(Start + i - len, len);
180 a_Dw_page_add_text(page, a_Misc_expand_tabs(data), plain->style);
181 g_free(data);
182 a_Dw_page_add_parbreak(page, 0, plain->style);
183 if ( Start[i] == '\r' && Start[i + 1] == '\n' ) ++i;
184 if ( i < MaxBytes ) ++i;
185 plain->state = ST_SeekingEol;
186 len = 0;
187 break;
188 }
189 }
190 plain->Start_Ofs += i - len;
191 if ( Eof && len ) {
192 data = g_strndup(Start + i - len, len);
193 a_Dw_page_add_text(page, a_Misc_expand_tabs(data), plain->style);
194 g_free(data);
195 a_Dw_page_add_parbreak(page, 0, plain->style);
196 plain->Start_Ofs += len;
197 }
198
199 if ( plain->bw ) {
200 gchar completestr[32];
201 g_snprintf(
202 completestr, 32, "%s%.1f Kb",
203 PBAR_PSTR(prefs.panel_size == 1),
204 (float)plain->Start_Ofs/1024);
205 a_Progressbar_update(plain->bw->progress, completestr, 1);
206 }
207 }
0 /*
1 * The png decoder for Dillo. It is responsible for decoding PNG data
2 * and transferring it to the dicache.
3 *
4 * Geoff Lane nov 1999 zzassgl@twirl.mcc.ac.uk
5 * Luca Rota, Jorge Arellano Cid, Eric Gaudet 2000
6 *
7 * "PNG: The Definitive Guide" by Greg Roelofs, O'Reilly
8 * ISBN 1-56592-542-4
9 */
10
11 #include <config.h>
12 #ifdef ENABLE_PNG
13
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h> /* For abort() */
17
18 #include <zlib.h>
19
20 #ifdef HAVE_LIBPNG_PNG_H
21 #include <libpng/png.h>
22 #else
23 #include <png.h>
24 #endif
25
26 #include <gtk/gtk.h>
27 #include "msg.h"
28 #include "image.h"
29 #include "web.h"
30 #include "cache.h"
31 #include "dicache.h"
32 #include "prefs.h"
33
34 #define DEBUG_LEVEL 6
35 #include "debug.h"
36
37 enum prog_state {
38 IS_finished, IS_init, IS_nextdata
39 };
40
41 static char *prog_state_name[] =
42 {
43 "IS_finished", "IS_init", "IS_nextdata"
44 };
45
46 /*
47 * This holds the data that must be saved between calls to this module.
48 * Each time it is called it is supplied with a vector of data bytes
49 * obtained from the web server. The module can process any amount of the
50 * supplied data. The next time the module is called, the vector may be
51 * extended with additional data bytes to be processed. The module must
52 * keep track of the current start and cursor position of the input data
53 * vector. As complete output rasters are determined they are sent out of
54 * the module for additional processing.
55 *
56 * NOTE: There is no external control of the splitting of the input data
57 * vector (only this module understands PNG format data.) This means that
58 * the complete state of a PNG image being processed must be held in the
59 * structure below so that processing can be suspended or resumed at any
60 * point within an input image.
61 *
62 * In the case of the libpng library, it maintains it's own state in
63 * png_ptr and into_ptr so the FSM is very simple - much simpler than the
64 * ones for XBM and PNM are.
65 */
66
67 typedef
68 struct _DilloPng {
69 DilloImage *Image; /* Image meta data */
70 DilloUrl *url; /* Primary Key for the dicache */
71 gint version; /* Secondary Key for the dicache */
72
73 double display_exponent; /* gamma correction */
74 gulong width; /* png image width */
75 gulong height; /* png image height */
76 png_structp png_ptr; /* libpng private data */
77 png_infop info_ptr; /* libpng private info */
78 guchar *image_data; /* decoded image data */
79 guchar **row_pointers; /* pntr to row starts */
80 jmp_buf jmpbuf; /* png error processing */
81 gint error; /* error flag */
82 gint rowbytes; /* No. bytes in image row */
83 short passes;
84 short channels; /* No. image channels */
85
86 /*
87 * 0 last byte
88 * +-------+-+-----------------------------------+-+
89 * | | | -- data to be processed -- | |
90 * +-------+-+-----------------------------------+-+
91 * ^ ^ ^
92 * ipbuf ipbufstart ipbufsize
93 */
94
95 guchar *ipbuf; /* image data in buffer */
96 gint ipbufstart; /* first valid image byte */
97 gint ipbufsize; /* size of valid data in */
98
99 enum prog_state state; /* FSM current state */
100
101 guchar *linebuf; /* o/p raster data */
102
103 } DilloPng;
104
105 #define DATASIZE (png->ipbufsize - png->ipbufstart)
106 #define BLACK 0
107 #define WHITE 255
108
109 /*
110 * Forward declarations
111 */
112 /* exported function */
113 DwWidget *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
114 void **Data);
115
116
117 static
118 void Png_error_handling(png_structp png_ptr, png_const_charp msg)
119 {
120 DilloPng *png;
121
122 DEBUG_MSG(6, "Png_error_handling: %s\n", msg);
123 png = png_get_error_ptr(png_ptr);
124
125 png->error = 1;
126 png->state = IS_finished;
127
128 longjmp(png->jmpbuf, 1);
129 }
130
131 static void
132 Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
133 {
134 DilloPng *png;
135 gint color_type;
136 gint bit_depth;
137 gint interlace_type;
138 guint i;
139 double gamma;
140
141 DEBUG_MSG(5, "Png_datainfo_callback:\n");
142
143 png = png_get_progressive_ptr(png_ptr);
144 g_return_if_fail (png != NULL);
145
146 png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
147 &bit_depth, &color_type, &interlace_type, NULL, NULL);
148
149 DEBUG_MSG(5, "Png_datainfo_callback: png->width = %ld\n"
150 "Png_datainfo_callback: png->height = %ld\n",
151 png->width, png->height);
152
153 /* we need RGB/RGBA in the end */
154 if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) {
155 /* Convert indexed images to RGB */
156 png_set_expand (png_ptr);
157 } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
158 /* Convert grayscale to RGB */
159 png_set_expand (png_ptr);
160 } else if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
161 /* We have transparency header, convert it to alpha channel */
162 png_set_expand(png_ptr);
163 } else if (bit_depth < 8) {
164 png_set_expand(png_ptr);
165 }
166
167 if (bit_depth == 16) {
168 png_set_strip_16(png_ptr);
169 }
170
171 /* Get and set gamma information. Beware: gamma correction 2.2 will
172 only work on PC's. todo: select screen gamma correction for other
173 platforms. */
174 if (png_get_gAMA(png_ptr, info_ptr, &gamma))
175 png_set_gamma(png_ptr, 2.2, gamma);
176
177 /* Convert gray scale to RGB */
178 if (color_type == PNG_COLOR_TYPE_GRAY ||
179 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
180 png_set_gray_to_rgb(png_ptr);
181 }
182
183 /* Interlaced */
184 if (interlace_type != PNG_INTERLACE_NONE) {
185 png->passes = png_set_interlace_handling(png_ptr);
186 }
187
188 /* get libpng to update it's state */
189 png_read_update_info(png_ptr, info_ptr);
190
191 png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
192 &bit_depth, &color_type, &interlace_type, NULL, NULL);
193
194 png->rowbytes = png_get_rowbytes(png_ptr, info_ptr);
195 png->channels = png_get_channels(png_ptr, info_ptr);
196
197 /* init Dillo specifics */
198 DEBUG_MSG(5, "Png_datainfo_callback: rowbytes = %d\n"
199 "Png_datainfo_callback: width = %ld\n"
200 "Png_datainfo_callback: height = %ld\n",
201 png->rowbytes, png->width, png->height);
202
203 png->image_data = (guchar *) g_malloc(png->rowbytes * png->height);
204 png->row_pointers = (guchar **) g_malloc(png->height * sizeof(guchar *));
205
206 for (i = 0; i < png->height; i++)
207 png->row_pointers[i] = png->image_data + (i * png->rowbytes);
208
209 png->linebuf = g_malloc(3 * png->width);
210
211 /* Initialize the dicache-entry here */
212 a_Dicache_set_parms(png->url, png->version, png->Image,
213 (guint)png->width, (guint)png->height,
214 DILLO_IMG_TYPE_RGB);
215 }
216
217 static void
218 Png_datarow_callback(png_structp png_ptr, png_bytep new_row,
219 png_uint_32 row_num, gint pass)
220 {
221 DilloPng *png;
222 guint i;
223
224 if (!new_row) /* work to do? */
225 return;
226
227 DEBUG_MSG(5, "Png_datarow_callback: row_num = %ld\n", row_num);
228
229 png = png_get_progressive_ptr(png_ptr);
230
231 png_progressive_combine_row(png_ptr, png->row_pointers[row_num], new_row);
232
233 switch (png->channels) {
234 case 3:
235 a_Dicache_write(png->Image, png->url, png->version,
236 png->image_data + (row_num * png->rowbytes),
237 0, (guint)row_num);
238 break;
239 case 4:
240 {
241 /* todo: get the backgound color from the parent
242 * of the image widget -- Livio. */
243 gint a, bg_red, bg_green, bg_blue;
244 guchar *pl = png->linebuf;
245 guchar *data = png->image_data + (row_num * png->rowbytes);
246
247 /* todo: maybe change prefs.bg_color to `a_Dw_widget_get_bg_color`,
248 * when background colors are correctly implementated */
249 bg_blue = (png->Image->bg_color) & 0xFF;
250 bg_green = (png->Image->bg_color>>8) & 0xFF;
251 bg_red = (png->Image->bg_color>>16) & 0xFF;
252
253 for (i = 0; i < png->width; i++) {
254 a = *(data+3);
255
256 if ( a == 255 ) {
257 *(pl++) = *(data++);
258 *(pl++) = *(data++);
259 *(pl++) = *(data++);
260 data++;
261 } else if ( a == 0 ) {
262 *(pl++) = bg_red;
263 *(pl++) = bg_green;
264 *(pl++) = bg_blue;
265 data += 4;
266 } else {
267 png_composite(*(pl++), *(data++), a, bg_red);
268 png_composite(*(pl++), *(data++), a, bg_green);
269 png_composite(*(pl++), *(data++), a, bg_blue);
270 data++;
271 }
272 }
273 a_Dicache_write(png->Image, png->url, png->version,
274 png->linebuf, 0, (guint)row_num);
275 break;
276 }
277 default:
278 MSG("Png_datarow_callback: unexpected number of channels = %d\n",
279 png->channels);
280 abort();
281 }
282 }
283
284 static void
285 Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
286 {
287 DilloPng *png;
288
289 DEBUG_MSG(5, "Png_dataend_callback:\n");
290
291 png = png_get_progressive_ptr(png_ptr);
292 png->state = IS_finished;
293 }
294
295
296 /*
297 * Op: Operation to perform.
298 * If (Op == 0)
299 * start or continue processing an image if image data exists.
300 * else
301 * terminate processing, cleanup any allocated memory,
302 * close down the decoding process.
303 *
304 * Client->CbData : pointer to previously allocated DilloPng work area.
305 * This holds the current state of the image processing and is saved
306 * across calls to this routine.
307 * Client->Buf : Pointer to data start.
308 * Client->BufSize : the size of the data buffer.
309 *
310 * You have to keep track of where you are in the image data and
311 * how much has been processed.
312 *
313 * It's entirely possible that you will not see the end of the data. The
314 * user may terminate transfer via a Stop button or there may be a network
315 * failure. This means that you can't just wait for all the data to be
316 * presented before starting conversion and display.
317 */
318 static void Png_callback(int Op, CacheClient_t *Client)
319 {
320 DilloPng *png = Client->CbData;
321
322 if ( Op ) {
323 /* finished - free up the resources for this image */
324 a_Dicache_close(png->url, png->version, Client);
325 g_free(png->image_data);
326 g_free(png->row_pointers);
327 g_free(png->linebuf);
328
329 if (setjmp(png->jmpbuf))
330 g_warning("PNG: can't destroy read structure\n");
331 else if (png->png_ptr)
332 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
333 g_free(png);
334 return;
335 }
336
337 /* Let's make some sound if we have been called with no data */
338 g_return_if_fail ( Client->Buf != NULL && Client->BufSize > 0 );
339
340 DEBUG_MSG(5, "Png_callback BufSize = %d\n", Client->BufSize);
341
342 /* Keep local copies so we don't have to pass multiple args to
343 * a number of functions. */
344 png->ipbuf = Client->Buf;
345 png->ipbufsize = Client->BufSize;
346
347 /* start/resume the FSM here */
348 while (png->state != IS_finished && DATASIZE) {
349 DEBUG_MSG(5, "State = %s\n", prog_state_name[png->state]);
350
351 switch (png->state) {
352 case IS_init:
353 if (DATASIZE < 8) {
354 return; /* need MORE data */
355 }
356 /* check the image signature - DON'T update ipbufstart! */
357 if (!png_check_sig(png->ipbuf, DATASIZE)) {
358 /* you lied to me about it being a PNG image */
359 png->state = IS_finished;
360 break;
361 }
362 /* OK, it looks like a PNG image, lets do some set up stuff */
363 png->png_ptr = png_create_read_struct(
364 PNG_LIBPNG_VER_STRING,
365 png,
366 (png_error_ptr)Png_error_handling,
367 (png_error_ptr)Png_error_handling);
368 g_return_if_fail (png->png_ptr != NULL);
369 png->info_ptr = png_create_info_struct(png->png_ptr);
370 g_return_if_fail (png->info_ptr != NULL);
371
372 setjmp(png->jmpbuf);
373 if (!png->error) {
374 png_set_progressive_read_fn(
375 png->png_ptr,
376 png,
377 Png_datainfo_callback, /* performs local init functions */
378 Png_datarow_callback, /* performs per row action */
379 Png_dataend_callback); /* performs cleanup actions */
380 png->state = IS_nextdata;
381 }
382 break;
383
384 case IS_nextdata:
385 if ( setjmp(png->jmpbuf) ) {
386 png->state = IS_finished;
387 } else if (!png->error) {
388 png_process_data( png->png_ptr,
389 png->info_ptr,
390 png->ipbuf + png->ipbufstart,
391 (png_size_t)DATASIZE);
392
393 png->ipbufstart += DATASIZE;
394 }
395 break;
396
397 default:
398 g_warning("PNG decoder: bad state = %d\n", png->state);
399 abort();
400 }
401 }
402 }
403
404 /*
405 * Create the image state data that must be kept between calls
406 */
407 static DilloPng *Png_new(DilloImage *Image, DilloUrl *url, gint version)
408 {
409 DilloPng *png = g_new0(DilloPng, 1);
410
411 png->Image = Image;
412 png->url = url;
413 png->version = version;
414 png->error = 0;
415 png->ipbuf = NULL;
416 png->ipbufstart = 0;
417 png->ipbufsize = 0;
418 png->state = IS_init;
419 png->linebuf = NULL;
420 png->image_data = NULL;
421 png->row_pointers = NULL;
422
423 return png;
424 }
425
426 /*
427 * MIME handler for "image/png" type
428 * (Sets Png_callback or a_Dicache_callback as the cache-client)
429 */
430 DwWidget *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
431 void **Data)
432 {
433 /*
434 * Type: MIME type
435 * Ptr: points to a Web structure
436 * Call: Dillo calls this with more data/eod
437 * Data: raw image data
438 */
439
440 DilloWeb *web = Ptr;
441 DICacheEntry *DicEntry;
442
443 DEBUG_MSG(5, "a_Png_image: Type = %s\n"
444 "a_Png_image: libpng - Compiled %s; using %s.\n"
445 "a_Png_image: zlib - Compiled %s; using %s.\n",
446 Type, PNG_LIBPNG_VER_STRING, png_libpng_ver,
447 ZLIB_VERSION, zlib_version);
448
449 if ( !web->Image )
450 web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
451
452 /* Add an extra reference to the Image (for dicache usage) */
453 a_Image_ref(web->Image);
454
455 DicEntry = a_Dicache_get_entry(web->url);
456 if ( !DicEntry ) {
457 /* Let's create an entry for this image... */
458 DicEntry = a_Dicache_add_entry(web->url);
459
460 /* ... and let the decoder feed it! */
461 *Data = Png_new(web->Image, DicEntry->url, DicEntry->version);
462 *Call = (CA_Callback_t) Png_callback;
463 } else {
464 /* Let's feed our client from the dicache */
465 a_Dicache_ref(DicEntry->url, DicEntry->version);
466 *Data = web->Image;
467 *Call = (CA_Callback_t) a_Dicache_callback;
468 }
469 return DW_WIDGET (web->Image->dw);
470 }
471
472 #endif /* ENABLE_PNG */
0 /*
1 * Preferences for dillo
2 *
3 * Copyright (C) 2000 Luca Rota <lrota@cclinf.polito.it>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <glib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <stdlib.h>
24 #include <string.h> /* for strchr */
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <locale.h> /* for setlocale */
28 #include "prefs.h"
29 #include "colors.h"
30 #include "misc.h"
31
32 /* symbol array */
33 static const struct {
34 gchar *name;
35 guint token;
36 } symbols[] = {
37 { "geometry", DRC_TOKEN_GEOMETRY },
38 { "http_proxy", DRC_TOKEN_PROXY },
39 { "http_proxyuser", DRC_TOKEN_PROXYUSER },
40 { "no_proxy", DRC_TOKEN_NOPROXY },
41 { "link_color", DRC_TOKEN_LINK_COLOR },
42 { "visited_color", DRC_TOKEN_VISITED_COLOR, },
43 { "bg_color", DRC_TOKEN_BG_COLOR },
44 { "allow_white_bg", DRC_TOKEN_ALLOW_WHITE_BG },
45 { "force_my_colors", DRC_TOKEN_FORCE_MY_COLORS },
46 { "contrast_visited_color", DRC_TOKEN_CONTRAST_VISITED_COLOR },
47 { "text_color", DRC_TOKEN_TEXT_COLOR },
48 { "use_oblique", DRC_TOKEN_USE_OBLIQUE },
49 { "start_page", DRC_TOKEN_START_PAGE },
50 { "home", DRC_TOKEN_HOME },
51 { "show_tooltip", DRC_TOKEN_SHOW_TOOLTIP },
52 { "panel_size", DRC_TOKEN_PANEL_SIZE },
53 { "small_icons", DRC_TOKEN_SMALL_ICONS },
54 { "limit_text_width", DRC_TOKEN_LIMIT_TEXT_WIDTH },
55 { "w3c_plus_heuristics", DRC_TOKEN_W3C_PLUS_HEURISTICS },
56 { "font_factor", DRC_TOKEN_FONT_FACTOR },
57 { "use_dicache", DRC_TOKEN_USE_DICACHE },
58 { "show_back", DRC_TOKEN_SHOW_BACK },
59 { "show_forw", DRC_TOKEN_SHOW_FORW },
60 { "show_home", DRC_TOKEN_SHOW_HOME },
61 { "show_reload", DRC_TOKEN_SHOW_RELOAD },
62 { "show_save", DRC_TOKEN_SHOW_SAVE },
63 { "show_stop", DRC_TOKEN_SHOW_STOP },
64 { "show_bookmarks", DRC_TOKEN_SHOW_BOOKMARKS },
65 { "show_menubar", DRC_TOKEN_SHOW_MENUBAR },
66 { "show_clear_url", DRC_TOKEN_SHOW_CLEAR_URL },
67 { "show_url", DRC_TOKEN_SHOW_URL },
68 { "show_search", DRC_TOKEN_SHOW_SEARCH },
69 { "show_progress_box", DRC_TOKEN_SHOW_PROGRESS_BOX },
70 { "fullwindow_start", DRC_TOKEN_FULLWINDOW_START },
71 { "transient_dialogs", DRC_TOKEN_TRANSIENT_DIALOGS },
72 { "vw_fontname", DRC_TOKEN_VW_FONT },
73 { "fw_fontname", DRC_TOKEN_FW_FONT },
74 { "generate_submit", DRC_TOKEN_GENERATE_SUBMIT },
75 { "enterpress_forces_submit", DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT },
76 { "search_url", DRC_TOKEN_SEARCH_URL },
77 { "show_msg", DRC_TOKEN_SHOW_MSG },
78 { "show_extra_warnings", DRC_TOKEN_SHOW_EXTRA_WARNINGS }
79 };
80
81 static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
82
83 /*
84 * Read tokens from dillorc and set values in the prefs structure.
85 * (Although this function can be called several times, and not leak,
86 * preferences aren't yet enabled for on-the-fly changes).
87 */
88 static guint Prefs_parser(GScanner *scanner)
89 {
90 gint st;
91 guint symbol;
92
93 /* expect a valid symbol */
94 g_scanner_get_next_token(scanner);
95 symbol = scanner->token;
96 if (scanner->token == G_TOKEN_EQUAL_SIGN) {
97 g_scanner_get_next_token (scanner);
98 return G_TOKEN_NONE;
99 } else if (symbol < DRC_TOKEN_FIRST || symbol > DRC_TOKEN_LAST )
100 return G_TOKEN_SYMBOL;
101
102 /* expect '=' */
103 g_scanner_get_next_token(scanner);
104 if (scanner->token != G_TOKEN_EQUAL_SIGN)
105 return G_TOKEN_EQUAL_SIGN;
106
107 /* expect a string */
108 g_scanner_get_next_token(scanner);
109 if (scanner->token != G_TOKEN_STRING)
110 return G_TOKEN_STRING;
111
112 /* assign value and exit successfully */
113 switch (symbol) {
114 case DRC_TOKEN_GEOMETRY:
115 a_Misc_parse_geometry(scanner->value.v_string, &prefs.xpos, &prefs.ypos,
116 &prefs.width, &prefs.height);
117 break;
118 case DRC_TOKEN_PROXY:
119 a_Url_free(prefs.http_proxy);
120 prefs.http_proxy = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
121 break;
122 case DRC_TOKEN_PROXYUSER:
123 g_free(prefs.http_proxyuser);
124 prefs.http_proxyuser = g_strdup(scanner->value.v_string);
125 break;
126 case DRC_TOKEN_NOPROXY:
127 g_free(prefs.no_proxy);
128 prefs.no_proxy = g_strdup(scanner->value.v_string);
129 prefs.no_proxy_vec = g_strsplit(prefs.no_proxy, " ", 0);
130 break;
131 case DRC_TOKEN_LINK_COLOR:
132 prefs.link_color = a_Color_parse(scanner->value.v_string,
133 prefs.link_color, &st);
134 break;
135 case DRC_TOKEN_VISITED_COLOR:
136 prefs.visited_color = a_Color_parse(scanner->value.v_string,
137 prefs.visited_color, &st);
138 break;
139 case DRC_TOKEN_TEXT_COLOR:
140 prefs.text_color = a_Color_parse(scanner->value.v_string,
141 prefs.text_color, &st);
142 break;
143 case DRC_TOKEN_BG_COLOR:
144 prefs.bg_color = a_Color_parse(scanner->value.v_string,
145 prefs.bg_color, &st);
146 break;
147 case DRC_TOKEN_ALLOW_WHITE_BG:
148 prefs.allow_white_bg = (strcmp(scanner->value.v_string, "YES") == 0);
149 break;
150 case DRC_TOKEN_FORCE_MY_COLORS:
151 prefs.force_my_colors = (strcmp(scanner->value.v_string, "YES") == 0);
152 break;
153 case DRC_TOKEN_CONTRAST_VISITED_COLOR:
154 prefs.contrast_visited_color =
155 (strcmp(scanner->value.v_string, "YES") == 0);
156 break;
157 case DRC_TOKEN_USE_OBLIQUE:
158 prefs.use_oblique = (strcmp(scanner->value.v_string, "YES") == 0);
159 break;
160 case DRC_TOKEN_PANEL_SIZE:
161 if (!g_strcasecmp(scanner->value.v_string, "tiny"))
162 prefs.panel_size = 1;
163 else if (!g_strcasecmp(scanner->value.v_string, "small"))
164 prefs.panel_size = 2;
165 else if (!g_strcasecmp(scanner->value.v_string, "medium"))
166 prefs.panel_size = 3;
167 else /* default to "large" */
168 prefs.panel_size = 4;
169 break;
170 case DRC_TOKEN_SMALL_ICONS:
171 prefs.small_icons = (strcmp(scanner->value.v_string, "YES") == 0);
172 break;
173 case DRC_TOKEN_START_PAGE:
174 a_Url_free(prefs.start_page);
175 prefs.start_page = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
176 break;
177 case DRC_TOKEN_HOME:
178 a_Url_free(prefs.home);
179 prefs.home = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
180 break;
181 case DRC_TOKEN_SHOW_TOOLTIP:
182 prefs.show_tooltip = (strcmp(scanner->value.v_string, "YES") == 0);
183 break;
184 case DRC_TOKEN_FONT_FACTOR:
185 prefs.font_factor = strtod(scanner->value.v_string, NULL);
186 break;
187 case DRC_TOKEN_LIMIT_TEXT_WIDTH:
188 prefs.limit_text_width = (strcmp(scanner->value.v_string, "YES") == 0);
189 break;
190 case DRC_TOKEN_W3C_PLUS_HEURISTICS:
191 prefs.w3c_plus_heuristics = (strcmp(scanner->value.v_string,"YES") == 0);
192 break;
193 case DRC_TOKEN_USE_DICACHE:
194 prefs.use_dicache = (strcmp(scanner->value.v_string, "YES") == 0);
195 break;
196 case DRC_TOKEN_SHOW_BACK:
197 prefs.show_back = (strcmp(scanner->value.v_string, "YES") == 0);
198 break;
199 case DRC_TOKEN_SHOW_FORW:
200 prefs.show_forw = (strcmp(scanner->value.v_string, "YES") == 0);
201 break;
202 case DRC_TOKEN_SHOW_HOME:
203 prefs.show_home = (strcmp(scanner->value.v_string, "YES") == 0);
204 break;
205 case DRC_TOKEN_SHOW_RELOAD:
206 prefs.show_reload = (strcmp(scanner->value.v_string, "YES") == 0);
207 break;
208 case DRC_TOKEN_SHOW_SAVE:
209 prefs.show_save = (strcmp(scanner->value.v_string, "YES") == 0);
210 break;
211 case DRC_TOKEN_SHOW_STOP:
212 prefs.show_stop = (strcmp(scanner->value.v_string, "YES") == 0);
213 break;
214 case DRC_TOKEN_SHOW_BOOKMARKS:
215 prefs.show_bookmarks = (strcmp(scanner->value.v_string, "YES") == 0);
216 break;
217 case DRC_TOKEN_SHOW_MENUBAR:
218 prefs.show_menubar = (strcmp(scanner->value.v_string, "YES") == 0);
219 break;
220 case DRC_TOKEN_SHOW_CLEAR_URL:
221 prefs.show_clear_url = (strcmp(scanner->value.v_string, "YES") == 0);
222 break;
223 case DRC_TOKEN_SHOW_URL:
224 prefs.show_url = (strcmp(scanner->value.v_string, "YES") == 0);
225 break;
226 case DRC_TOKEN_SHOW_SEARCH:
227 prefs.show_search = (strcmp(scanner->value.v_string, "YES") == 0);
228 break;
229 case DRC_TOKEN_SHOW_PROGRESS_BOX:
230 prefs.show_progress_box = (strcmp(scanner->value.v_string, "YES") == 0);
231 break;
232 case DRC_TOKEN_FULLWINDOW_START:
233 prefs.fullwindow_start = (strcmp(scanner->value.v_string, "YES") == 0);
234 break;
235 case DRC_TOKEN_TRANSIENT_DIALOGS:
236 prefs.transient_dialogs = (strcmp(scanner->value.v_string, "YES") == 0);
237 break;
238 case DRC_TOKEN_FW_FONT:
239 g_free(prefs.fw_fontname);
240 prefs.fw_fontname = g_strdup(scanner->value.v_string);
241 break;
242 case DRC_TOKEN_VW_FONT:
243 g_free(prefs.vw_fontname);
244 prefs.vw_fontname = g_strdup(scanner->value.v_string);
245 break;
246 case DRC_TOKEN_GENERATE_SUBMIT:
247 prefs.generate_submit = (strcmp(scanner->value.v_string, "YES") == 0);
248 break;
249 case DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT:
250 prefs.enterpress_forces_submit =
251 (strcmp(scanner->value.v_string, "YES") == 0);
252 break;
253 case DRC_TOKEN_SEARCH_URL:
254 g_free(prefs.search_url);
255 prefs.search_url = g_strdup(scanner->value.v_string);
256 break;
257 case DRC_TOKEN_SHOW_MSG:
258 prefs.show_msg =
259 (strcmp(scanner->value.v_string, "YES") == 0);
260 break;
261 case DRC_TOKEN_SHOW_EXTRA_WARNINGS:
262 prefs.show_extra_warnings =
263 (strcmp(scanner->value.v_string, "YES") == 0);
264 break;
265 default:
266 break; /* Not reached */
267 }
268 return G_TOKEN_NONE;
269 }
270
271 static gint Prefs_load(void)
272 {
273 GScanner *scanner;
274 gint fd;
275 guint i, expected_token;
276 gchar *file;
277
278 /* Here we load and set options from dillorc */
279 file = a_Misc_prepend_user_home(".dillo/dillorc");
280 fd = open(file, O_RDONLY);
281 g_free(file);
282 if (fd < 0 && (fd = open(DILLORC_SYS, O_RDONLY)) < 0)
283 return FILE_NOT_FOUND;
284
285 fcntl(fd, F_SETFD, FD_CLOEXEC | fcntl(fd, F_GETFD));
286
287 scanner = g_scanner_new(NULL);
288
289 /* Adjust lexing behaviour to suit our needs */
290 /* Specifies the chars which can be used in identifiers */
291 scanner->config->cset_identifier_nth = (
292 G_CSET_a_2_z
293 "~+-_:&%#/.0123456789"
294 G_CSET_A_2_Z
295 G_CSET_LATINS /*??? I don't know if we need these two */
296 G_CSET_LATINC /*??? */
297 );
298 /* Specifies the chars which can start identifiers */
299 scanner->config->cset_identifier_first = (
300 G_CSET_a_2_z
301 G_CSET_A_2_Z
302 "_0123456789"
303 );
304 /* Don't return G_TOKEN_SYMBOL, but the symbol's value */
305 scanner->config->symbol_2_token = TRUE;
306 /* Don't return G_TOKEN_IDENTIFIER, but convert it to string */
307 scanner->config->identifier_2_string = TRUE;
308
309 /* load symbols into the scanner */
310 g_scanner_freeze_symbol_table(scanner);
311 for (i = 0; i < n_symbols; i++)
312 g_scanner_add_symbol(scanner, symbols[i].name,
313 GINT_TO_POINTER (symbols[i].token));
314 g_scanner_thaw_symbol_table(scanner);
315
316 /* feed in the text */
317 g_scanner_input_file(scanner, fd);
318
319 /* give the error handler an idea on how the input is named */
320 scanner->input_name = "dillorc";
321
322 /*
323 * Scanning loop, we parse the input until it's end is reached,
324 * the scanner encountered a lexing error, or our sub routine came
325 * across invalid syntax
326 */
327 do {
328 expected_token = Prefs_parser(scanner);
329
330 /* Give an error message upon syntax errors */
331 if (expected_token == G_TOKEN_SYMBOL)
332 g_scanner_unexp_token (scanner, expected_token, NULL, "symbol", NULL,
333 NULL, FALSE);
334 else if (expected_token == G_TOKEN_STRING)
335 g_scanner_unexp_token (scanner, expected_token, NULL, "string", NULL,
336 NULL, FALSE);
337 else if (expected_token == G_TOKEN_EQUAL_SIGN)
338 g_scanner_unexp_token (scanner, expected_token, NULL, "=", NULL,
339 NULL, FALSE);
340 g_scanner_peek_next_token (scanner);
341 } while ( /* expected_token == G_TOKEN_NONE && */
342 scanner->next_token != G_TOKEN_EOF &&
343 scanner->next_token != G_TOKEN_ERROR);
344
345 /* give an error message upon syntax errors */
346 if (expected_token != G_TOKEN_NONE)
347 g_scanner_unexp_token(scanner, expected_token, NULL, "symbol", NULL,
348 NULL, TRUE);
349
350 /* finish parsing */
351 g_scanner_destroy(scanner);
352 close(fd);
353 return PARSE_OK;
354 }
355
356 void a_Prefs_init(void)
357 {
358 gchar *old_locale;
359
360 prefs.width = D_GEOMETRY_DEFAULT_WIDTH;
361 prefs.height = D_GEOMETRY_DEFAULT_HEIGHT;
362 prefs.xpos = D_GEOMETRY_DEFAULT_XPOS;
363 prefs.ypos = D_GEOMETRY_DEFAULT_YPOS;
364 prefs.http_proxy = NULL;
365 prefs.http_proxyuser = NULL;
366 prefs.no_proxy = NULL;
367 prefs.no_proxy_vec = NULL;
368 prefs.link_color = DW_COLOR_DEFAULT_BLUE;
369 prefs.visited_color = DW_COLOR_DEFAULT_PURPLE;
370 prefs.bg_color = DW_COLOR_DEFAULT_BGND;
371 prefs.text_color = DW_COLOR_DEFAULT_BLACK;
372 prefs.use_oblique = FALSE;
373 prefs.start_page = a_Url_new(DILLO_START_PAGE, NULL, 0, 0, 0);
374 prefs.home = a_Url_new(DILLO_HOME, NULL, 0, 0, 0);
375 prefs.allow_white_bg = TRUE;
376 prefs.force_my_colors = FALSE;
377 prefs.contrast_visited_color = FALSE;
378 prefs.show_tooltip = FALSE;
379 prefs.panel_size = 1;
380 prefs.small_icons = FALSE;
381 prefs.limit_text_width = FALSE;
382 prefs.w3c_plus_heuristics = TRUE;
383 prefs.font_factor = 1.0;
384 prefs.use_dicache = FALSE;
385 prefs.show_back=TRUE;
386 prefs.show_forw=TRUE;
387 prefs.show_home=TRUE;
388 prefs.show_reload=TRUE;
389 prefs.show_save=TRUE;
390 prefs.show_stop=TRUE;
391 prefs.show_bookmarks=TRUE;
392 prefs.show_menubar=TRUE;
393 prefs.show_clear_url=TRUE;
394 prefs.show_url=TRUE;
395 prefs.show_search=TRUE;
396 prefs.show_progress_box=TRUE;
397 prefs.fullwindow_start=FALSE;
398 prefs.transient_dialogs=FALSE;
399 prefs.vw_fontname = g_strdup("helvetica");
400 prefs.fw_fontname = g_strdup("courier");
401 prefs.generate_submit = FALSE;
402 prefs.enterpress_forces_submit = FALSE;
403 prefs.search_url = g_strdup("http://www.google.com/search?q=%s");
404 prefs.show_msg = TRUE;
405 prefs.show_extra_warnings = FALSE;
406
407 /* this locale stuff is to avoid parsing problems with float numbers */
408 old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
409 setlocale (LC_NUMERIC, "C");
410
411 Prefs_load();
412
413 setlocale (LC_NUMERIC, old_locale);
414 g_free (old_locale);
415 }
416
417 /*
418 * Preferences memory-deallocation
419 * (Call this one at exit time)
420 */
421 void a_Prefs_freeall(void)
422 {
423 g_free(prefs.http_proxyuser);
424 g_free(prefs.no_proxy);
425 if (prefs.no_proxy_vec)
426 g_strfreev(prefs.no_proxy_vec);
427 a_Url_free(prefs.http_proxy);
428 g_free(prefs.fw_fontname);
429 g_free(prefs.vw_fontname);
430 a_Url_free(prefs.start_page);
431 a_Url_free(prefs.home);
432 g_free(prefs.search_url);
433 }
0 #ifndef __PREFS_H__
1 #define __PREFS_H__
2
3 #include "url.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DILLO_START_PAGE "about:splash"
10 #define DILLO_HOME "http://www.dillo.org/"
11 #define D_GEOMETRY_DEFAULT_WIDTH 640
12 #define D_GEOMETRY_DEFAULT_HEIGHT 550
13 #define D_GEOMETRY_DEFAULT_XPOS -9999
14 #define D_GEOMETRY_DEFAULT_YPOS -9999
15
16 #define DW_COLOR_DEFAULT_GREY 0xd6d6d6
17 #define DW_COLOR_DEFAULT_BLACK 0x000000
18 #define DW_COLOR_DEFAULT_BLUE 0x0000ff
19 #define DW_COLOR_DEFAULT_PURPLE 0x800080
20 #define DW_COLOR_DEFAULT_BGND 0xd6d6c0
21
22 /* define enumeration values to be returned */
23 enum {
24 PARSE_OK = 0,
25 FILE_NOT_FOUND
26 };
27
28 /* define enumeration values to be returned for specific symbols */
29 typedef enum {
30 DRC_TOKEN_FIRST = G_TOKEN_LAST,
31 DRC_TOKEN_GEOMETRY,
32 DRC_TOKEN_PROXY,
33 DRC_TOKEN_PROXYUSER,
34 DRC_TOKEN_NOPROXY,
35 DRC_TOKEN_LINK_COLOR,
36 DRC_TOKEN_VISITED_COLOR,
37 DRC_TOKEN_BG_COLOR,
38 DRC_TOKEN_ALLOW_WHITE_BG,
39 DRC_TOKEN_FORCE_MY_COLORS,
40 DRC_TOKEN_CONTRAST_VISITED_COLOR,
41 DRC_TOKEN_TEXT_COLOR,
42 DRC_TOKEN_USE_OBLIQUE,
43 DRC_TOKEN_START_PAGE,
44 DRC_TOKEN_HOME,
45 DRC_TOKEN_PANEL_SIZE,
46 DRC_TOKEN_SMALL_ICONS,
47 DRC_TOKEN_FONT_FACTOR,
48 DRC_TOKEN_SHOW_TOOLTIP,
49 DRC_TOKEN_LIMIT_TEXT_WIDTH,
50 DRC_TOKEN_W3C_PLUS_HEURISTICS,
51 DRC_TOKEN_USE_DICACHE,
52 DRC_TOKEN_SHOW_BACK,
53 DRC_TOKEN_SHOW_FORW,
54 DRC_TOKEN_SHOW_HOME,
55 DRC_TOKEN_SHOW_RELOAD,
56 DRC_TOKEN_SHOW_SAVE,
57 DRC_TOKEN_SHOW_STOP,
58 DRC_TOKEN_SHOW_BOOKMARKS,
59 DRC_TOKEN_SHOW_MENUBAR,
60 DRC_TOKEN_SHOW_CLEAR_URL,
61 DRC_TOKEN_SHOW_URL,
62 DRC_TOKEN_SHOW_SEARCH,
63 DRC_TOKEN_SHOW_PROGRESS_BOX,
64 DRC_TOKEN_FULLWINDOW_START,
65 DRC_TOKEN_TRANSIENT_DIALOGS,
66 DRC_TOKEN_FW_FONT,
67 DRC_TOKEN_VW_FONT,
68 DRC_TOKEN_GENERATE_SUBMIT,
69 DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT,
70 DRC_TOKEN_SEARCH_URL,
71 DRC_TOKEN_SHOW_MSG,
72 DRC_TOKEN_SHOW_EXTRA_WARNINGS,
73
74 DRC_TOKEN_LAST
75 } Dillo_Rc_TokenType;
76
77 typedef struct _DilloPrefs DilloPrefs;
78
79 struct _DilloPrefs {
80 gint width;
81 gint height;
82 gint xpos;
83 gint ypos;
84 DilloUrl *http_proxy;
85 gchar *http_proxyuser;
86 gchar *no_proxy;
87 gchar **no_proxy_vec;
88 DilloUrl *start_page;
89 DilloUrl *home;
90 gint32 link_color;
91 gint32 visited_color;
92 gint32 bg_color;
93 gint32 text_color;
94 gboolean allow_white_bg;
95 gboolean use_oblique;
96 gboolean force_my_colors;
97 gboolean contrast_visited_color;
98 gboolean show_tooltip;
99 gint panel_size;
100 gboolean small_icons;
101 gboolean limit_text_width;
102 gboolean w3c_plus_heuristics;
103 gdouble font_factor;
104 gboolean use_dicache;
105 gboolean show_back;
106 gboolean show_forw;
107 gboolean show_home;
108 gboolean show_reload;
109 gboolean show_save;
110 gboolean show_stop;
111 gboolean show_bookmarks;
112 gboolean show_menubar;
113 gboolean show_clear_url;
114 gboolean show_url;
115 gboolean show_search;
116 gboolean show_progress_box;
117 gboolean fullwindow_start;
118 gboolean transient_dialogs;
119 gchar *vw_fontname;
120 gchar *fw_fontname;
121 gboolean generate_submit;
122 gboolean enterpress_forces_submit;
123 gchar *search_url;
124 gboolean show_msg;
125 gboolean show_extra_warnings;
126 };
127
128 /* Global Data */
129 DilloPrefs prefs;
130
131 void a_Prefs_init(void);
132 void a_Prefs_freeall(void);
133
134 #ifdef __cplusplus
135 }
136 #endif /* __cplusplus */
137
138 #endif /* __PREFS_H__ */
0 /*
1 * File: progressbar.c
2 *
3 * Copyright (C) 2004 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "progressbar.h"
12
13
14 /*
15 * The progressbar is basically a GtkFrame with a text label.
16 */
17 GtkWidget* a_Progressbar_new(void)
18 {
19 GtkWidget *p_bar, *label;
20
21 p_bar = gtk_frame_new(NULL);
22 label = gtk_label_new("");
23 gtk_frame_set_shadow_type(GTK_FRAME(p_bar), GTK_SHADOW_IN);
24 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.4);
25 gtk_container_add(GTK_CONTAINER (p_bar), label);
26 gtk_widget_show(label);
27 return p_bar;
28 }
29
30 /*
31 * Update the specified progress bar.
32 * updatestr : String to display within the bar (NULL is ignored)
33 * sens : sensitivity (0 = set insensitive, 1 = set sensitive)
34 */
35 void a_Progressbar_update(GtkWidget *p_bar, const char *updatestr, gint sens)
36 {
37 gtk_widget_set_sensitive(p_bar, (sens == 0) ? FALSE : TRUE);
38
39 if ( updatestr != NULL )
40 gtk_label_set_text(GTK_LABEL(GTK_BIN(p_bar)->child), updatestr);
41 }
42
0 /* GTK - The GIMP Toolkit
1 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17 #ifndef __PROGRESSBAR_H__
18 #define __PROGRESSBAR_H__
19
20
21 #include <gdk/gdk.h>
22 #include <gtk/gtk.h>
23
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif /* __cplusplus */
28
29 #define PBAR_ISTR(s) (s) ? "" : "Images: "
30 #define PBAR_PSTR(s) (s) ? "" : "Page: "
31
32 GtkWidget* a_Progressbar_new(void);
33 void a_Progressbar_update(GtkWidget *pbar, const char *updatestr,
34 gint op);
35
36 #ifdef __cplusplus
37 }
38 #endif /* __cplusplus */
39
40
41 #endif /* __PROGRESSBAR_H__ */
42
0 /*
1 * File: selection.c
2 *
3 * Copyright 2003 Sebastian Geerken <s.geerken@ping.de>,
4 * Eric Gaudet <eric@rti-zone.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * See doc/Selection.txt for informations about this module.
14 */
15
16 #include "selection.h"
17 #include "strbuf.h"
18 #include "dw_gtk_viewport.h"
19 #include <stdio.h>
20 #include <string.h>
21 #include <gtk/gtk.h>
22
23 /*#define DEBUG_LEVEL 2*/
24 #include "debug.h"
25
26 static void Selection_reset_selection (Selection *selection);
27 static void Selection_reset_link (Selection *selection);
28 static void Selection_switch_link_to_selection (Selection *selection,
29 DwIterator *it,
30 gint32 char_pos);
31 static void Selection_adjust_selection (Selection *selection,
32 DwIterator *it,
33 gint32 char_pos);
34 static gint Selection_correct_char_pos (DwExtIterator *it,
35 gint32 char_pos);
36 static void Selection_highlight (Selection *selection,
37 gboolean fl);
38 static void Selection_highlight0 (gboolean fl,
39 DwExtIterator *from,
40 gint from_char,
41 DwExtIterator *to,
42 gint to_char);
43 static void Selection_copy (Selection *selection);
44
45 /* Not defined as static, to avoid compiler warnings. */
46 char *selection_state_descr[] = { "none", "selecting", "selected" };
47
48 Selection *a_Selection_new (void)
49 {
50 Selection *selection;
51
52 selection = g_new (Selection, 1);
53
54 selection->selection_state = SELECTION_NONE;
55 selection->from = NULL;
56 selection->to = NULL;
57
58 selection->link_state = SELECTION_LINK_NONE;
59 selection->link = NULL;
60
61 selection->dclick_callback = NULL;
62 selection->callback_data = NULL;
63
64 selection->owner = NULL;
65 return selection;
66 }
67
68
69 /*
70 * Sets a callback function for double click, this is normally
71 * the function toggling the full screen mode.
72 * (More than one is never needed, so this instead of a signal.)
73 */
74 void a_Selection_set_dclick_callback (Selection *selection,
75 void (*fn) (gpointer data),
76 gpointer data)
77 {
78 g_return_if_fail (selection->dclick_callback == NULL);
79 selection->dclick_callback = fn;
80 selection->callback_data = data;
81 }
82
83 /*
84 * Set the widget that owns the selection.
85 */
86 void a_Selection_set_owner(Selection *selection, gpointer owner)
87 {
88 selection->owner = owner;
89 a_Selection_init_selection(owner);
90 }
91
92 void a_Selection_reset (Selection *selection)
93 {
94 Selection_reset_selection (selection);
95 Selection_reset_link (selection);
96 }
97
98
99 void a_Selection_free (Selection *selection)
100 {
101 a_Selection_reset (selection);
102 g_free (selection);
103 }
104
105 static void Selection_reset_selection (Selection *selection)
106 {
107 if (selection->from)
108 a_Dw_ext_iterator_free(selection->from);
109 selection->from = NULL;
110 if (selection->to)
111 a_Dw_ext_iterator_free(selection->to);
112 selection->to = NULL;
113 selection->selection_state = SELECTION_NONE;
114 }
115
116
117 static void Selection_reset_link (Selection *selection)
118 {
119 if (selection->link)
120 a_Dw_ext_iterator_free(selection->link);
121 selection->link = NULL;
122 selection->link_state = SELECTION_LINK_NONE;
123 }
124
125 gint a_Selection_button_press (Selection *selection,
126 DwIterator *it,
127 gint char_pos,
128 gint link,
129 GdkEventButton *event,
130 gboolean within_content)
131 {
132 DwWidget *it_widget = it->widget;
133 gboolean ret = FALSE, dummy;
134
135 DEBUG_MSG (3, "PRESS%s(%s, %d, %d): %s -> ...\n",
136 (event && event->type == GDK_2BUTTON_PRESS) ? "2" : "",
137 a_Dw_iterator_text (it), char_pos, link,
138 selection_state_descr[selection->selection_state]);
139
140 #if defined (DEBUG_LEVEL) && 1 >= DEBUG_LEVEL
141 a_Dw_widget_print_tree (GTK_DW_VIEWPORT(it->widget->viewport)->child);
142 #endif
143
144 if (event && event->button == 1 &&
145 !within_content && event->type == GDK_2BUTTON_PRESS) {
146 /* When the user double-clicks on empty parts, call the callback
147 * function instead of normal processing. Used for full screen
148 * mode. */
149 if (selection->dclick_callback)
150 selection->dclick_callback (selection->callback_data);
151 /* Reset everything, so that a_Selection_release will ignore the
152 * "release" event following soon. */
153 Selection_highlight (selection, FALSE);
154 a_Selection_reset (selection);
155 ret = TRUE;
156 } else {
157 if (link != -1) {
158 /* link handling */
159 if (event) {
160 gtk_signal_emit_by_name (GTK_OBJECT (it_widget),
161 "link_pressed", link, -1, -1, event,
162 &dummy);
163 Selection_reset_link (selection);
164 selection->link_state = SELECTION_LINK_PRESSED;
165 selection->link_button = event->button;
166 selection->link = a_Dw_ext_iterator_new (it);
167
168 /* It may be that the user has pressed on something activatable
169 * (link != -1), but there is no contents, e.g. with images
170 * without ALTernative text. */
171 if (selection->link) {
172 selection->link_char =
173 Selection_correct_char_pos (selection->link, char_pos);
174 selection->link_number = link;
175 }
176 /* We do not return the value of the signal function (dummy),
177 * but we do actually process this event. */
178 ret = TRUE;
179 }
180 } else {
181 /* normal selection handling */
182 if (event && event->button == 1) {
183 Selection_highlight (selection, FALSE);
184 Selection_reset_selection (selection);
185
186 selection->from = a_Dw_ext_iterator_new (it);
187 /* a_Dw_ext_iterator_new may return NULL, if the page has no
188 * contents. */
189 if (selection->from) {
190 selection->selection_state = SELECTION_SELECTING;
191 selection->from_char =
192 Selection_correct_char_pos (selection->from, char_pos);
193 selection->to = a_Dw_ext_iterator_clone (selection->from);
194 selection->to_char =
195 Selection_correct_char_pos (selection->to, char_pos);
196 ret = TRUE;
197 } else
198 /* if there is no content */
199 ret = FALSE;
200 }
201 }
202 }
203
204 DEBUG_MSG (3, " ... -> %s, processed = %s\n",
205 selection_state_descr[selection->selection_state],
206 ret ? "TRUE" : "FALSE");
207 return ret;
208 }
209
210
211 gint a_Selection_button_release (Selection *selection,
212 DwIterator *it,
213 gint char_pos,
214 gint link,
215 GdkEventButton *event,
216 gboolean within_content)
217 {
218 DwWidget *it_widget = it->widget;
219 gboolean ret = FALSE, dummy;
220
221 DEBUG_MSG (3, "RELEASE(%s, %d, %d): %s -> ...\n",
222 a_Dw_iterator_text (it), char_pos, link,
223 selection_state_descr[selection->selection_state]);
224
225 if (selection->link_state == SELECTION_LINK_PRESSED &&
226 event && event->button == selection->link_button) {
227 /* link handling */
228 ret = TRUE;
229 if (link != -1)
230 gtk_signal_emit_by_name (GTK_OBJECT (it_widget),
231 "link_released", link, -1, -1, event,
232 &dummy);
233
234 /* The link where the user clicked the mouse button? */
235 if (link == selection->link_number) {
236 Selection_reset_link (selection);
237 gtk_signal_emit_by_name (GTK_OBJECT (it_widget),
238 "link_clicked", link, -1, -1, event,
239 &dummy);
240 } else {
241 if (event->button == 1)
242 /* Reset links and switch to selection mode. The selection
243 * state will be set to SELECTING, which is handled some lines
244 * below. */
245 Selection_switch_link_to_selection (selection, it, char_pos);
246 }
247 }
248
249 if (selection->selection_state == SELECTION_SELECTING &&
250 event && event->button == 1) {
251 /* normal selection */
252 ret = TRUE;
253 Selection_adjust_selection (selection, it, char_pos);
254
255 if (a_Dw_ext_iterator_compare (selection->from, selection->to) == 0 &&
256 selection->from_char == selection->to_char)
257 /* nothing selected */
258 Selection_reset_selection (selection);
259 else {
260 Selection_copy (selection);
261 selection->selection_state = SELECTION_SELECTED;
262 }
263 }
264
265 DEBUG_MSG (3, " ... -> %s, processed = %s\n",
266 selection_state_descr[selection->selection_state],
267 ret ? "TRUE" : "FALSE");
268 return ret;
269 }
270
271
272 gint a_Selection_button_motion (Selection *selection,
273 DwIterator *it,
274 gint char_pos,
275 gint link,
276 GdkEventButton *event,
277 gboolean within_content)
278 {
279 DEBUG_MSG (3, "MOTION (%s, %d, %d): %s -> ...\n",
280 a_Dw_iterator_text (it), char_pos, link,
281 selection_state_descr[selection->selection_state]);
282
283 if (selection->link_state == SELECTION_LINK_PRESSED) {
284 /* link handling */
285 if (link != selection->link_number)
286 /* No longer the link where the user clicked the mouse button.
287 * Reset links and switch to selection mode. */
288 Selection_switch_link_to_selection (selection, it, char_pos);
289 /* Still in link: do nothing. */
290 } else if (selection->selection_state == SELECTION_SELECTING) {
291 /* selection */
292 Selection_adjust_selection (selection, it, char_pos);
293 }
294
295 DEBUG_MSG (3, " ... -> %s, processed = TRUE\n",
296 selection_state_descr[selection->selection_state]);
297 return TRUE;
298 }
299
300 /*
301 * This function is called when the user decides not to activate a link,
302 * but instead select text.
303 */
304 static void Selection_switch_link_to_selection (Selection *selection,
305 DwIterator *it,
306 gint32 char_pos)
307 {
308 /* It may be that selection->link is NULL, see a_Selection_button_press. */
309 if (selection->link) {
310 /* Reset old selection. */
311 Selection_highlight (selection, FALSE);
312 Selection_reset_selection (selection);
313
314 /* Transfer link state into selection state. */
315 selection->from = a_Dw_ext_iterator_clone (selection->link);
316 selection->from_char = selection->link_char;
317 selection->to = a_Dw_ext_iterator_new_variant (selection->from, it);
318 selection->to_char =
319 Selection_correct_char_pos (selection->to, char_pos);
320 selection->selection_state = SELECTION_SELECTING;
321
322 /* Reset link status. */
323 Selection_reset_link (selection);
324
325 Selection_highlight (selection, TRUE);
326
327 DEBUG_MSG (2, " after switching: from (%s, %d) to (%s, %d)\n",
328 a_Dw_ext_iterator_text (selection->from),
329 selection->from_char,
330 a_Dw_ext_iterator_text (selection->to), selection->to_char);
331 } else {
332 /* A link was pressed on, but there is nothing to select. Reset
333 * everything. */
334 Selection_reset_selection (selection);
335 Selection_reset_link (selection);
336 }
337 }
338
339 /*
340 * This function is used by a_Selection_button_motion and
341 * a_Selection_button_release, and changes the second limit of the already
342 * existing selection region.
343 */
344 static void Selection_adjust_selection (Selection *selection,
345 DwIterator *it,
346 gint32 char_pos)
347 {
348 DwExtIterator *new_to;
349 gint new_to_char, cmp_old, cmp_new, cmp_diff, len;
350 gboolean brute_highlighting = FALSE;
351
352 new_to = a_Dw_ext_iterator_new_variant (selection->to, it);
353 new_to_char = Selection_correct_char_pos (new_to, char_pos);
354
355 cmp_old = a_Dw_ext_iterator_compare (selection->to, selection->from);
356 cmp_new = a_Dw_ext_iterator_compare (new_to, selection->from);
357
358 if (cmp_old == 0 || cmp_new == 0) {
359 /* Either before, or now, the limits differ only by the character
360 * position. */
361 brute_highlighting = TRUE;
362 } else if (cmp_old * cmp_new < 0) {
363 /* The selection order has changed, i.e. the user moved the selection
364 * end again beyond the position he started. */
365 brute_highlighting = TRUE;
366 } else {
367 /* Here, cmp_old and cmp_new are equivalent and != 0. */
368 cmp_diff = a_Dw_ext_iterator_compare (new_to, selection->to);
369
370 DEBUG_MSG (2, "Selection_adjust_selection: cmp_old = cmp_new = %d, "
371 "cmp_diff = %d\n", cmp_old, cmp_diff);
372
373 if (cmp_old * cmp_diff > 0) {
374 /* The user has enlarged the selection. Highlight the difference. */
375 if (cmp_diff < 0) {
376 len = Selection_correct_char_pos (selection->to, SELECTION_EOW);
377 Selection_highlight0 (TRUE, new_to, new_to_char, selection->to,
378 len + 1);
379 } else {
380 Selection_highlight0 (TRUE, selection->to, 0, new_to, new_to_char);
381 }
382 } else {
383 if (cmp_old * cmp_diff < 0) {
384 /* The user has reduced the selection. Unighlight the difference.
385 */
386 Selection_highlight0 (FALSE, selection->to, 0, new_to, 0);
387 }
388
389 /* Otherwise, the user has changed the position only slightly.
390 * In both cases, re-highlight the new position. */
391 if (cmp_old < 0) {
392 len = Selection_correct_char_pos (new_to, SELECTION_EOW);
393 a_Dw_ext_iterator_highlight (new_to, new_to_char, len + 1,
394 DW_HIGHLIGHT_SELECTION);
395 } else
396 a_Dw_ext_iterator_highlight (new_to, 0, new_to_char,
397 DW_HIGHLIGHT_SELECTION);
398 }
399 }
400
401 if (brute_highlighting)
402 Selection_highlight (selection, FALSE);
403
404 a_Dw_ext_iterator_free(selection->to);
405 selection->to = new_to;
406 selection->to_char = new_to_char;
407
408 if (brute_highlighting)
409 Selection_highlight (selection, TRUE);
410
411 DEBUG_MSG (2, " selection now from (%s, %d) to (%s, %d)\n",
412 a_Dw_ext_iterator_text (selection->from), selection->from_char,
413 a_Dw_ext_iterator_text (selection->to), selection->to_char);
414 }
415
416 /*
417 * This function deals especially with the case that a widget passes
418 * SELECTION_EOW. See Selection.txt in the doc dir for more informations.
419 */
420 static gint Selection_correct_char_pos (DwExtIterator *it,
421 gint32 char_pos)
422 {
423 DwIterator *top = it->stack[it->stack_top];
424 gint len;
425
426 if (top->content.type == DW_CONTENT_TEXT)
427 len = strlen(top->content.data.text);
428 else
429 len = 1;
430
431 return MIN(char_pos, len);
432 }
433
434
435 static void Selection_highlight (Selection *selection,
436 gboolean fl)
437 {
438 Selection_highlight0 (fl, selection->from, selection->from_char,
439 selection->to, selection->to_char);
440 }
441
442
443 static void Selection_highlight0 (gboolean fl,
444 DwExtIterator *from,
445 gint from_char,
446 DwExtIterator *to,
447 gint to_char)
448 {
449 DwExtIterator *a, *b, *i;
450 gint cmp, a_char, b_char;
451 gboolean start;
452
453 if (from && to) {
454 DEBUG_MSG (2, " %shighlighting from %s / %d to %s / %d\n",
455 fl ? "" : "un", a_Dw_ext_iterator_text (from),
456 from_char, a_Dw_ext_iterator_text (to), to_char);
457 cmp = a_Dw_ext_iterator_compare (from, to);
458 if (cmp == 0) {
459 if (fl) {
460 if (from_char < to_char)
461 a_Dw_ext_iterator_highlight (from, from_char, to_char,
462 DW_HIGHLIGHT_SELECTION);
463 else
464 a_Dw_ext_iterator_highlight (from, to_char, from_char,
465 DW_HIGHLIGHT_SELECTION);
466 } else
467 a_Dw_ext_iterator_unhighlight (from, DW_HIGHLIGHT_SELECTION);
468 return;
469 }
470
471 if (cmp < 0) {
472 a = from;
473 a_char = from_char;
474 b = to;
475 b_char = to_char;
476 } else {
477 a = to;
478 a_char = to_char;
479 b = from;
480 b_char = from_char;
481 }
482
483 for (i = a_Dw_ext_iterator_clone (a), start = TRUE;
484 (cmp = a_Dw_ext_iterator_compare (i, b)) <= 0;
485 a_Dw_ext_iterator_next (i), start = FALSE) {
486 if (i->content.type == DW_CONTENT_TEXT) {
487 if (fl) {
488 if (start) {
489 DEBUG_MSG (2, " highlighting %s from %d to %d\n",
490 a_Dw_ext_iterator_text (i), a_char,
491 strlen (i->content.data.text) + 1);
492 a_Dw_ext_iterator_highlight (i, a_char,
493 strlen (i->content.data.text)
494 + 1,
495 DW_HIGHLIGHT_SELECTION);
496 } else if (cmp == 0) {
497 /* the end */
498 DEBUG_MSG (2, " highlighting %s from %d to %d\n",
499 a_Dw_ext_iterator_text (i), 0, b_char);
500 a_Dw_ext_iterator_highlight (i, 0, b_char,
501 DW_HIGHLIGHT_SELECTION);
502 } else {
503 DEBUG_MSG (2, " highlighting %s from %d to %d\n",
504 a_Dw_ext_iterator_text (i), 0,
505 strlen (i->content.data.text) + 1);
506 a_Dw_ext_iterator_highlight (i, 0,
507 strlen (i->content.data.text)
508 + 1,
509 DW_HIGHLIGHT_SELECTION);
510 }
511 } else {
512 DEBUG_MSG (2, " unhighlighting %s\n",
513 a_Dw_ext_iterator_text (i));
514 a_Dw_ext_iterator_unhighlight (i, DW_HIGHLIGHT_SELECTION);
515 }
516 }
517 }
518 a_Dw_ext_iterator_free (i);
519 }
520 }
521
522
523 static void Selection_copy(Selection *selection)
524 {
525 DwIterator *si;
526 DwExtIterator *a, *b, *i;
527 gint cmp, a_char, b_char;
528 gboolean start;
529 gchar *tmp;
530 Strbuf_t *strbuf;
531
532 if (selection->from && selection->to) {
533 strbuf = a_Strbuf_new ();
534
535 cmp = a_Dw_ext_iterator_compare (selection->from, selection->to);
536 if (cmp == 0) {
537 if (selection->from->content.type == DW_CONTENT_TEXT) {
538 si = selection->from->stack[selection->from->stack_top];
539 if (selection->from_char < selection->to_char)
540 tmp = g_strndup(si->content.data.text + selection->from_char,
541 selection->to_char - selection->from_char);
542 else
543 tmp = g_strndup(si->content.data.text + selection->to_char,
544 selection->from_char - selection->to_char);
545 a_Strbuf_append (strbuf, tmp);
546 g_free (tmp);
547 }
548 } else {
549 if (cmp < 0) {
550 a = selection->from;
551 a_char = selection->from_char;
552 b = selection->to;
553 b_char = selection->to_char;
554 } else {
555 a = selection->to;
556 a_char = selection->to_char;
557 b = selection->from;
558 b_char = selection->from_char;
559 }
560
561 for (i = a_Dw_ext_iterator_clone (a), start = TRUE;
562 (cmp = a_Dw_ext_iterator_compare (i, b)) <= 0;
563 a_Dw_ext_iterator_next (i), start = FALSE) {
564 si = i->stack[i->stack_top];
565 switch (si->content.type) {
566 case DW_CONTENT_TEXT:
567 if (start) {
568 tmp = g_strndup(si->content.data.text + a_char,
569 strlen (i->content.data.text) - a_char);
570 a_Strbuf_append (strbuf, tmp);
571 g_free (tmp);
572 } else if (cmp == 0) {
573 /* the end */
574 tmp = g_strndup(si->content.data.text, b_char);
575 a_Strbuf_append (strbuf, tmp);
576 g_free (tmp);
577 } else
578 a_Strbuf_append (strbuf, si->content.data.text);
579
580 if (si->content.space && cmp != 0)
581 a_Strbuf_append (strbuf, " ");
582
583 break;
584
585 case DW_CONTENT_BREAK:
586 if (si->content.data.break_space > 0)
587 a_Strbuf_append (strbuf, "\n\n");
588 else
589 a_Strbuf_append (strbuf, "\n");
590 break;
591 default:
592 /* Make pedantic compilers happy. Especially
593 * DW_CONTENT_WIDGET is never returned by a DwExtIterator. */
594 break;
595 }
596 }
597 a_Dw_ext_iterator_free (i);
598 }
599 a_Selection_set_selection(selection->owner, a_Strbuf_chars(strbuf));
600 a_Strbuf_free (strbuf);
601 }
602 }
603
604
605 /* GENERAL SELECTION STUFF */
606
607 /*
608 * Local data
609 */
610 static char *selection = NULL;
611
612 /*
613 * Sets the widget to copy the selection from.
614 */
615 void a_Selection_init_selection(GtkWidget *widget)
616 {
617 gtk_signal_connect(GTK_OBJECT(widget), "selection_clear_event",
618 GTK_SIGNAL_FUNC(a_Selection_clear_selection_callback), NULL);
619 gtk_signal_connect(GTK_OBJECT(widget), "selection_get",
620 GTK_SIGNAL_FUNC(a_Selection_give_selection_callback), NULL);
621 gtk_selection_add_target(widget, GDK_SELECTION_PRIMARY,
622 GDK_SELECTION_TYPE_STRING, 1);
623 }
624
625 /*
626 * Sets the selection, and associates it with the current widget for
627 * copying.
628 */
629 void a_Selection_set_selection(GtkWidget *widget, gchar *str)
630 {
631 if (gtk_selection_owner_set(widget,
632 GDK_SELECTION_PRIMARY,
633 GDK_CURRENT_TIME)) {
634 /* --EG: Why would it fail? (this question points to that the
635 * GTK+ example tests the return value.) */
636
637 /* As the clear-selection callback is not being called automatically
638 * before setting the new value, it'll be cleared here. --Jcid
639 * todo: this selection code needs a revision */
640 g_free(selection);
641 selection = g_strdup(str);
642 }
643 }
644
645 /*
646 * Callback for Paste (when another application wants the selection)
647 */
648 void a_Selection_give_selection_callback(GtkWidget *widget,
649 GtkSelectionData *data, guint info, guint time)
650 {
651 gtk_selection_data_set(data, GDK_SELECTION_TYPE_STRING,
652 8, selection, strlen(selection));
653 }
654
655 /*
656 * Clear selection
657 */
658 gint a_Selection_clear_selection_callback(GtkWidget *widget,
659 GdkEventSelection *event)
660 {
661 g_free(selection);
662 selection = NULL;
663
664 /* here we should un-highlight the selected text */
665 return TRUE;
666 }
667
0 #ifndef __SELECTION_H__
1 #define __SELECTION_H__
2
3 #include "dw_ext_iterator.h"
4
5 #define SELECTION_EOW (1 << 30)
6
7 typedef struct
8 {
9 /* selection */
10 enum {
11 SELECTION_NONE,
12 SELECTION_SELECTING,
13 SELECTION_SELECTED
14 } selection_state;
15 DwExtIterator *from, *to;
16 gint from_char, to_char;
17
18 /* link handling */
19 enum {
20 SELECTION_LINK_NONE,
21 SELECTION_LINK_PRESSED
22 } link_state;
23 guint link_button;
24 DwExtIterator *link;
25 gint link_char, link_number;
26
27 /* "full screen" feature */
28 void (*dclick_callback) (gpointer data);
29 gpointer callback_data;
30
31 /* widget that owns this selection */
32 gpointer owner;
33
34 } Selection;
35
36 Selection* a_Selection_new (void);
37 void a_Selection_free (Selection *selection);
38 void a_Selection_reset (Selection *selection);
39 void a_Selection_set_dclick_callback (Selection *selection,
40 void (*fn) (gpointer data),
41 gpointer callback_data);
42 void a_Selection_set_owner (Selection *selection,
43 gpointer owner);
44 gint a_Selection_button_press (Selection *selection,
45 DwIterator *it,
46 gint char_pos,
47 gint link,
48 GdkEventButton *event,
49 gboolean within_content);
50 gint a_Selection_button_release (Selection *selection,
51 DwIterator *it,
52 gint char_pos,
53 gint link,
54 GdkEventButton *event,
55 gboolean within_content);
56 gint a_Selection_button_motion (Selection *selection,
57 DwIterator *it,
58 gint char_pos,
59 gint link,
60 GdkEventButton *event,
61 gboolean within_content);
62
63 void a_Selection_init_selection (GtkWidget *widget);
64 void a_Selection_set_selection (GtkWidget *widget,
65 gchar* str);
66 void a_Selection_give_selection_callback (GtkWidget *widget,
67 GtkSelectionData *data,
68 guint info,
69 guint time);
70 gint a_Selection_clear_selection_callback (GtkWidget *,
71 GdkEventSelection *);
72
73
74 #endif /* __SELECTION_H__ */
0 #!/bin/sh
1 #
2 # Find a token within source files ( *.[ch] )
3 # Enjoy!
4 # Jorge.-
5
6 if [ ! $# = 1 ]; then
7 echo "Usage:"
8 echo " srch <token>"
9 else
10 # find "./" -name "*.[ch]" -print -exec grep $1 {} \;
11 egrep "$1" *.[ch]
12 egrep "$1" IO/*.[ch]
13 egrep -H "$1" ../dpi/*.[ch]
14 egrep -H "$1" ../dpid/*.[ch]
15 egrep -H "$1" ../dpip/*.[ch]
16 fi
0 /*
1 * File: strbuf.c
2 *
3 * Copyright 2003 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * A simple structure for fast concatenation of a large number of strings.
13 */
14
15 #include <string.h>
16 #include "strbuf.h"
17
18
19 /*
20 * Create a new string buffer.
21 */
22 Strbuf_t *a_Strbuf_new (void)
23 {
24 Strbuf_t *strbuf;
25
26 strbuf = g_new (Strbuf_t, 1);
27 strbuf->list = NULL;
28 strbuf->num_chars = 0;
29 strbuf->str = NULL;
30 strbuf->str_valid = FALSE;
31 return strbuf;
32 }
33
34
35 /*
36 * Free a string buffer.
37 */
38 void a_Strbuf_free (Strbuf_t *strbuf)
39 {
40 GSList *it;
41
42 for (it = strbuf->list; it; it = it->next)
43 g_free (it->data);
44 g_slist_free (strbuf->list);
45 g_free (strbuf->str);
46 g_free (strbuf);
47 }
48
49 /*
50 * Append a NUL-terminated string to the buffer.
51 * A copy is kept in the buffer, so the caller does not have to care
52 * about memory management.
53 */
54 void a_Strbuf_append (Strbuf_t *strbuf,
55 gchar *str)
56 {
57 /* Remember that g_slist_prepend is faster than g_slist_prepend! */
58 strbuf->list = g_slist_prepend (strbuf->list, g_strdup (str));
59 strbuf->num_chars += strlen (str);
60 strbuf->str_valid = FALSE;
61 }
62
63 /*
64 * Return a NUL-terminated strings containing all appended strings.
65 * The caller does not have to free the string, this is done in
66 * a_Strbuf_free().
67 */
68 gchar* a_Strbuf_chars (Strbuf_t *strbuf)
69 {
70 GSList *it;
71 gchar *p, *s;
72 int l;
73
74 if (strbuf->str_valid)
75 return strbuf->str;
76
77 g_free (strbuf->str);
78 strbuf->str = g_new (gchar, strbuf->num_chars + 1);
79
80 /* The strings in the list are prepended so we have to start from the tail
81 * and work our way to the start of the new string */
82 p = strbuf->str + strbuf->num_chars;
83 for (it = strbuf->list; it; it = it->next) {
84 s = (gchar*)(it->data);
85 l = strlen (s);
86 p -= l;
87 memcpy (p, s, l * sizeof (gchar));
88 }
89
90 p[strbuf->num_chars] = 0;
91 strbuf->str_valid = TRUE;
92
93 return strbuf->str;
94 }
0 #ifndef __STRBUF_H__
1 #define __STRBUF_H__
2
3 #include <glib.h>
4
5 typedef struct _Strbuf Strbuf_t;
6
7 struct _Strbuf {
8 GSList *list;
9 int num_chars;
10 gchar *str;
11 gboolean str_valid;
12 };
13
14 Strbuf_t* a_Strbuf_new (void);
15 void a_Strbuf_free (Strbuf_t *strbuf);
16 void a_Strbuf_append (Strbuf_t *strbuf,
17 gchar *str);
18 gchar* a_Strbuf_chars (Strbuf_t *strbuf);
19
20
21
22 #endif /* __STRBUF_H__ */
0 /*
1 * File: url.c
2 *
3 * Copyright (C) 2001 Jorge Arellano Cid <jcid@dillo.org>
4 * 2001 Livio Baldini Soares <livio@linux.ime.usp.br>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * Parse and normalize all URL's inside Dillo.
14 * - <scheme> <authority> <path> <query> and <fragment> point to 'buffer'.
15 * - 'url_string' is built upon demand (transparent to the caller).
16 * - 'hostname' and 'port' are also being handled on demand.
17 */
18
19 /*
20 * Regular Expression as given in RFC2396 for URL parsing.
21 *
22 * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
23 * 12 3 4 5 6 7 8 9
24 *
25 * scheme = $2
26 * authority = $4
27 * path = $5
28 * query = $7
29 * fragment = $9
30 */
31
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <glib.h>
38
39 #include "url.h"
40
41 /*#define DEBUG_LEVEL 2 */
42 #include "debug.h"
43
44
45 /*
46 * Return the url as a string.
47 * (initializing 'url_string' camp if necessary)
48 */
49 gchar *a_Url_str(const DilloUrl *u)
50 {
51 /* Internal url handling IS transparent to the caller */
52 DilloUrl *url = (DilloUrl *) u;
53
54 g_return_val_if_fail (url != NULL, NULL);
55
56 if (!url->url_string) {
57 url->url_string = g_string_sized_new(60);
58 g_string_sprintf(
59 url->url_string, "%s%s%s%s%s%s%s%s%s%s",
60 url->scheme ? url->scheme : "",
61 url->scheme ? ":" : "",
62 url->authority ? "//" : "",
63 url->authority ? url->authority : "",
64 (url->path && url->path[0] != '/' && url->authority) ? "/" : "",
65 url->path ? url->path : "",
66 url->query ? "?" : "",
67 url->query ? url->query : "",
68 url->fragment ? "#" : "",
69 url->fragment ? url->fragment : "");
70 }
71
72 return url->url_string->str;
73 }
74
75 /*
76 * Return the hostname as a string.
77 * (initializing 'hostname' and 'port' camps if necessary)
78 * Note: a similar approach can be taken for user:password auth.
79 */
80 const gchar *a_Url_hostname(const DilloUrl *u)
81 {
82 gchar *p;
83 /* Internal url handling IS transparent to the caller */
84 DilloUrl *url = (DilloUrl *) u;
85
86 if (!url->hostname && url->authority) {
87 if ((p = strchr(url->authority, ':'))) {
88 url->port = strtol(p + 1, NULL, 10);
89 url->hostname = g_strndup(url->authority,(guint)(p - url->authority));
90 } else
91 url->hostname = url->authority;
92 }
93
94 return url->hostname;
95 }
96
97 /*
98 * Create a DilloUrl object and initialize it.
99 * (buffer, scheme, authority, path, query and fragment).
100 */
101 static DilloUrl *Url_object_new(const gchar *uri_str)
102 {
103 DilloUrl *url;
104 gchar *s, *p;
105
106 g_return_val_if_fail (uri_str != NULL, NULL);
107
108 url = g_new0(DilloUrl, 1);
109
110 /* remove leading & trailing space from buffer */
111 for (p = (gchar *)uri_str; isspace(*p); ++p);
112 url->buffer = g_strchomp(g_strdup(p));
113
114 s = (gchar *) url->buffer;
115 p = strpbrk(s, ":/?#");
116 if (p && p[0] == ':' && p > s) { /* scheme */
117 *p = 0;
118 url->scheme = s;
119 s = ++p;
120 }
121 /* p = strpbrk(s, "/"); */
122 if (p == s && p[0] == '/' && p[1] == '/') { /* authority */
123 s = p + 2;
124 p = strpbrk(s, "/?#");
125 if (p) {
126 memmove(s - 2, s, (size_t)MAX(p - s, 1));
127 url->authority = s - 2;
128 p[-2] = 0;
129 s = p;
130 } else if (*s) {
131 url->authority = s;
132 return url;
133 }
134 }
135
136 p = strpbrk(s, "?#");
137 if (p) { /* path */
138 url->path = (p > s) ? s : NULL;
139 s = p;
140 } else if (*s) {
141 url->path = s;
142 return url;
143 }
144
145 p = strpbrk(s, "?#");
146 if (p && p[0] == '?') { /* query */
147 *p = 0;
148 s = p + 1;
149 url->query = s;
150 p = strpbrk(s, "#");
151 }
152 if (p && p[0] == '#') { /* fragment */
153 *p = 0;
154 s = p + 1;
155 url->fragment = s;
156 }
157
158 return url;
159 }
160
161 /*
162 * Free a DilloUrl
163 */
164 void a_Url_free(DilloUrl *url)
165 {
166 if (url) {
167 if (url->url_string)
168 g_string_free(url->url_string, TRUE);
169 if (url->hostname != url->authority)
170 g_free((gchar *)url->hostname);
171 g_free((gchar *)url->buffer);
172 g_free((gchar *)url->data);
173 g_free((gchar *)url->alt);
174 g_free(url);
175 }
176 }
177
178 /*
179 * Resolve the URL as RFC2396 suggests.
180 */
181 static GString *Url_resolve_relative(const gchar *RelStr,
182 DilloUrl *BaseUrlPar,
183 const gchar *BaseStr)
184 {
185 gchar *p, *s, *e;
186 gint i;
187 GString *SolvedUrl, *Path;
188 DilloUrl *RelUrl, *BaseUrl = NULL;
189
190 /* parse relative URL */
191 RelUrl = Url_object_new(RelStr);
192
193 if (BaseUrlPar) {
194 BaseUrl = BaseUrlPar;
195 } else if (RelUrl->scheme == NULL) {
196 /* only required when there's no <scheme> in RelStr */
197 BaseUrl = Url_object_new(BaseStr);
198 }
199
200 SolvedUrl = g_string_sized_new(64);
201 Path = g_string_sized_new(64);
202
203 /* path empty && scheme, authority and query undefined */
204 if (!RelUrl->path && !RelUrl->scheme &&
205 !RelUrl->authority && !RelUrl->query) {
206 g_string_append(SolvedUrl, BaseStr);
207
208 if (RelUrl->fragment) { /* fragment */
209 if (BaseUrl->fragment)
210 g_string_truncate(SolvedUrl, BaseUrl->fragment-BaseUrl->buffer-1);
211 g_string_append_c(SolvedUrl, '#');
212 g_string_append(SolvedUrl, RelUrl->fragment);
213 }
214 goto done;
215
216 } else if (RelUrl->scheme) { /* scheme */
217 g_string_append(SolvedUrl, RelStr);
218 goto done;
219
220 } else if (RelUrl->authority) { /* authority */
221 /* Set the Path buffer and goto "STEP 7"; */
222 if (RelUrl->path)
223 g_string_append(Path, RelUrl->path);
224
225 } else if (RelUrl->path && RelUrl->path[0] == '/') { /* path */
226 g_string_append(Path, RelUrl->path);
227
228 } else {
229 /* solve relative path */
230 if (BaseUrl->path) {
231 g_string_append(Path, BaseUrl->path);
232 for (i = Path->len; --i >= 0 && Path->str[i] != '/'; );
233 if (Path->str[i] == '/')
234 g_string_truncate(Path, ++i);
235 }
236 if (RelUrl->path)
237 g_string_append(Path, RelUrl->path);
238
239 /* erase "./" */
240 while ((p=strstr(Path->str, "./")) &&
241 (p == Path->str || p[-1] == '/'))
242 g_string_erase(Path, p - Path->str, 2);
243 /* erase last "." */
244 if (Path->len && Path->str[Path->len - 1] == '.' &&
245 (Path->len == 1 || Path->str[Path->len - 2] == '/'))
246 g_string_truncate(Path, Path->len - 1);
247
248 /* erase "<segment>/../" and "<segment>/.." */
249 s = p = Path->str;
250 while ( (p = strstr(p, "/..")) != NULL ) {
251 if ((p[3] == '/' || !p[3]) && (p - s)) { /* "/../" | "/.." */
252
253 for (e = p + 3 ; p[-1] != '/' && p > s; --p);
254 if (p[0] != '.' || p[1] != '.' || p[2] != '/') {
255 g_string_erase(Path, p - Path->str, e - p + (*e != 0));
256 p -= (p > Path->str);
257 } else
258 p = e;
259 } else
260 p += 3;
261 }
262 }
263
264 /* STEP 7
265 */
266
267 /* scheme */
268 if (BaseUrl->scheme) {
269 g_string_append(SolvedUrl, BaseUrl->scheme);
270 g_string_append_c(SolvedUrl, ':');
271 }
272
273 /* authority */
274 if (RelUrl->authority) {
275 g_string_append(SolvedUrl, "//");
276 g_string_append(SolvedUrl, RelUrl->authority);
277 } else if (BaseUrl->authority) {
278 g_string_append(SolvedUrl, "//");
279 g_string_append(SolvedUrl, BaseUrl->authority);
280 }
281
282 /* path */
283 if ((RelUrl->authority || BaseUrl->authority) &&
284 ((Path->len == 0 && (RelUrl->query || RelUrl->fragment)) ||
285 (Path->len && Path->str[0] != '/')))
286 g_string_append_c(SolvedUrl, '/'); /* hack? */
287 g_string_append(SolvedUrl, Path->str);
288
289 /* query */
290 if (RelUrl->query) {
291 g_string_append_c(SolvedUrl, '?');
292 g_string_append(SolvedUrl, RelUrl->query);
293 }
294
295 /* fragment */
296 if (RelUrl->fragment) {
297 g_string_append_c(SolvedUrl, '#');
298 g_string_append(SolvedUrl, RelUrl->fragment);
299 }
300
301 done:
302 g_string_free(Path, TRUE);
303 a_Url_free(RelUrl);
304 if (BaseUrl != BaseUrlPar)
305 a_Url_free(BaseUrl);
306 return SolvedUrl;
307 }
308
309 /*
310 * Transform (and resolve) an URL string into the respective DilloURL.
311 * If URL = "http://dillo.sf.net:8080/index.html?long#part2"
312 * then the resulting DilloURL should be:
313 * DilloURL = {
314 * url_string = "http://dillo.sf.net:8080/index.html?long#part2"
315 * scheme = "http"
316 * authority = "dillo.sf.net:8080:
317 * path = "/index.html"
318 * query = "long"
319 * fragment = "part2"
320 * hostname = "dillo.sf.net"
321 * port = 8080
322 * flags = 0
323 * data = NULL
324 * alt = NULL
325 * ismap_url_len = 0
326 * scrolling_position = 0
327 * }
328 *
329 * Return NULL if URL is badly formed.
330 */
331 DilloUrl* a_Url_new(const gchar *url_str, const gchar *base_url,
332 gint flags, gint32 posx, gint32 posy)
333 {
334 DilloUrl *url;
335 gchar *urlstring, *p, *new_str = NULL;
336 GString *SolvedUrl;
337 gint n_ic, n_ic_spc;
338
339 g_return_val_if_fail (url_str != NULL, NULL);
340
341 /* Count illegal characters (0x00-0x1F, 0x7F and space) */
342 urlstring = (gchar *)url_str;
343 n_ic = n_ic_spc = 0;
344 for (p = urlstring; *p; p++) {
345 n_ic_spc += (*p == ' ') ? 1 : 0;
346 n_ic += (*p != ' ' && *p > 0x1F && *p != 0x7F) ? 0 : 1;
347 }
348 if (n_ic) {
349 /* Strip illegal characters (they could also be encoded).
350 * There's no standard for illegal chars; we chose to strip. */
351 for (p = new_str = g_strdup(urlstring); *urlstring; urlstring++)
352 if (*urlstring > 0x1F && *urlstring != 0x7F && *urlstring != ' ')
353 *p++ = *urlstring;
354 *p = 0;
355 urlstring = new_str;
356 }
357
358 /* let's use a heuristic to set http: as default */
359 if (!base_url) {
360 base_url = "http:";
361 if (urlstring[0] != '/') {
362 p = strpbrk(urlstring, "/#?:");
363 if (!p || *p != ':')
364 urlstring = g_strconcat("//", urlstring, NULL);
365 } else if (urlstring[1] != '/')
366 urlstring = g_strconcat("/", urlstring, NULL);
367 }
368
369 /* Resolve the URL */
370 SolvedUrl = Url_resolve_relative(urlstring, NULL, base_url);
371 DEBUG_MSG(2, "SolvedUrl = %s\n", SolvedUrl->str);
372 g_return_val_if_fail (SolvedUrl != NULL, NULL);
373
374 /* Fill url data */
375 url = Url_object_new(SolvedUrl->str);
376 url->url_string = SolvedUrl;
377 url->flags = flags;
378 url->scrolling_position_x = posx;
379 url->scrolling_position_y = posy;
380 url->illegal_chars = n_ic;
381 url->illegal_chars_spc = n_ic_spc;
382
383 g_free(new_str);
384 return url;
385 }
386
387
388 /*
389 * Duplicate a Url structure
390 */
391 DilloUrl* a_Url_dup(const DilloUrl *ori)
392 {
393 DilloUrl *url;
394
395 url = Url_object_new(URL_STR_(ori));
396 g_return_val_if_fail (url != NULL, NULL);
397
398 url->url_string = g_string_new(URL_STR(ori));
399 url->port = ori->port;
400 url->flags = ori->flags;
401 url->data = g_strdup(ori->data);
402 url->alt = g_strdup(ori->alt);
403 url->ismap_url_len = ori->ismap_url_len;
404 url->scrolling_position_x = ori->scrolling_position_x;
405 url->scrolling_position_y = ori->scrolling_position_y;
406 url->illegal_chars = ori->illegal_chars;
407 url->illegal_chars_spc = ori->illegal_chars_spc;
408
409 return url;
410 }
411
412 /*
413 * Compare two Url's to check if they are the same.
414 * The fields which are compared here are:
415 * <scheme>, <authority>, <path>, <query> and <data>
416 * Other fields are left for the caller to check
417 *
418 * Return value: 0 if equal, 1 otherwise
419 */
420 gint a_Url_cmp(const DilloUrl *A, const DilloUrl *B)
421 {
422 if (!A || !B)
423 return 1;
424
425 if (A == B ||
426 (URL_STRCAMP_I_EQ(A->authority, B->authority) &&
427 URL_STRCAMP_EQ(A->path, B->path) &&
428 URL_STRCAMP_EQ(A->query, B->query) &&
429 URL_STRCAMP_EQ(A->data, B->data) &&
430 URL_STRCAMP_I_EQ(A->scheme, B->scheme)))
431 return 0;
432 return 1;
433 }
434
435 /*
436 * Set DilloUrl flags
437 */
438 void a_Url_set_flags(DilloUrl *u, gint flags)
439 {
440 if (u)
441 u->flags = flags;
442 }
443
444 /*
445 * Set DilloUrl data (like POST info, etc.)
446 */
447 void a_Url_set_data(DilloUrl *u, gchar *data)
448 {
449 if (u) {
450 g_free((gchar *)u->data);
451 u->data = g_strdup(data);
452 }
453 }
454
455 /*
456 * Set DilloUrl alt (alternate text to the URL. Used by image maps)
457 */
458 void a_Url_set_alt(DilloUrl *u, const gchar *alt)
459 {
460 if (u) {
461 g_free((gchar *)u->alt);
462 u->alt = g_strdup(alt);
463 }
464 }
465
466 /*
467 * Set DilloUrl scrolling position
468 */
469 void a_Url_set_pos(DilloUrl *u, gint32 posx, gint32 posy)
470 {
471 if (u) {
472 u->scrolling_position_x = posx;
473 u->scrolling_position_y = posy;
474 }
475 }
476
477 /*
478 * Set DilloUrl ismap coordinates
479 * (this is optimized for not hogging the CPU)
480 */
481 void a_Url_set_ismap_coords(DilloUrl *u, gchar *coord_str)
482 {
483 g_return_if_fail(u && coord_str);
484
485 if ( !u->ismap_url_len ) {
486 /* Save base-url length (without coords) */
487 u->ismap_url_len = URL_STR_(u) ? u->url_string->len : 0;
488 a_Url_set_flags(u, URL_FLAGS(u) | URL_Ismap);
489 }
490 if (u->url_string) {
491 g_string_truncate(u->url_string, u->ismap_url_len);
492 g_string_append(u->url_string, coord_str);
493 u->query = u->url_string->str + u->ismap_url_len + 1;
494 }
495 }
496
497 /*
498 * Given an hex octet (e.g., e3, 2F, 20), return the corresponding
499 * character if the octet is valid, and -1 otherwise
500 */
501 static int Url_decode_hex_octet(const gchar *s)
502 {
503 gint hex_value;
504 gchar *tail, hex[3];
505
506 if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
507 hex[2] = 0;
508 hex_value = strtol(hex, &tail, 16);
509 if (tail - hex == 2)
510 return hex_value;
511 }
512 return -1;
513 }
514
515 /*
516 * Parse possible hexadecimal octets in the URI path.
517 * Returns a new allocated string.
518 */
519 gchar *a_Url_decode_hex_str(const gchar *str)
520 {
521 gchar *new_str, *dest;
522 int i, val;
523
524 if (!str)
525 return NULL;
526
527 /* most cases won't have hex octets */
528 if (!strchr(str, '%'))
529 return g_strdup(str);
530
531 dest = new_str = g_new(gchar, strlen(str) + 1);
532
533 for (i = 0; str[i]; i++) {
534 *dest++ = (str[i] == '%' && (val = Url_decode_hex_octet(str+i+1)) >= 0) ?
535 i+=2, val : str[i];
536 }
537 *dest++ = 0;
538
539 new_str = g_realloc(new_str, sizeof(gchar) * (dest - new_str));
540 return new_str;
541 }
542
543 /*
544 * Urlencode 'str'
545 * -RL :: According to the RFC 1738, only alphanumerics, the special
546 * characters "$-_.+!*'(),", and reserved characters ";/?:@=&" used
547 * for their *reserved purposes* may be used unencoded within a URL.
548 * We'll escape everything but alphanumeric and "-_.*" (as lynx). --Jcid
549 *
550 * Note: the content type "application/x-www-form-urlencoded" is used:
551 * i.e., ' ' -> '+' and '\n' -> CR LF (see HTML 4.01, Sec. 17.13.4)
552 */
553 gchar *a_Url_encode_hex_str(const gchar *str)
554 {
555 static const char *verbatim = "-_.*";
556 static const char *hex = "0123456789ABCDEF";
557 char *newstr, *c;
558
559 if (!str)
560 return NULL;
561
562 newstr = g_new(char, 6*strlen(str)+1);
563
564 for (c = newstr; *str; str++)
565 if ((isalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
566 /* we really need isalnum for the "C" locale */
567 *c++ = *str;
568 else if (*str == ' ')
569 *c++ = '+';
570 else if (*str == '\n') {
571 *c++ = '%';
572 *c++ = '0';
573 *c++ = 'D';
574 *c++ = '%';
575 *c++ = '0';
576 *c++ = 'A';
577 } else {
578 *c++ = '%';
579 *c++ = hex[(*str >> 4) & 15];
580 *c++ = hex[*str & 15];
581 }
582 *c = 0;
583
584 return newstr;
585 }
586
587
588 /*
589 * RFC-2396 suggests this stripping when "importing" URLs from other media.
590 * Strip: "URL:", enclosing < >, and embedded whitespace.
591 * (We also strip illegal chars: 00-1F and 7F)
592 */
593 gchar *a_Url_string_strip_delimiters(const gchar *str)
594 {
595 gchar *p, *new_str, *text;
596
597 new_str = text = g_strdup(str);
598
599 if (new_str) {
600 if (strncmp(new_str, "URL:", 4) == 0)
601 text += 4;
602 if (*text == '<')
603 text++;
604
605 for (p = new_str; *text; text++)
606 if (*text > 0x1F && *text != 0x7F && *text != ' ')
607 *p++ = *text;
608 if (p > new_str && p[-1] == '>')
609 --p;
610 *p = 0;
611 }
612 return new_str;
613 }
0 /*
1 * File : url.h - Dillo
2 *
3 * Copyright (C) 2001 Jorge Arellano Cid <jcid@users.sourceforge.net>
4 * 2001 Livio Baldini Soares <livio@linux.ime.usp.br>
5 *
6 * Parse and normalize all URL's inside Dillo.
7 */
8
9 #ifndef __URL_H__
10 #define __URL_H__
11
12 #include <string.h> /* for strcmp */
13 #include <glib.h>
14
15
16 #define DILLO_URL_HTTP_PORT 80
17 #define DILLO_URL_HTTPS_PORT 443
18 #define DILLO_URL_FTP_PORT 21
19 #define DILLO_URL_MAILTO_PORT 25
20 #define DILLO_URL_NEWS_PORT 119
21 #define DILLO_URL_TELNET_PORT 23
22 #define DILLO_URL_GOPHER_PORT 70
23
24
25 /*
26 * Values for DilloUrl->flags.
27 * Specifies which which action to perform with an URL.
28 */
29 #define URL_Get (1 << 0)
30 #define URL_Post (1 << 1)
31 #define URL_ISindex (1 << 2)
32 #define URL_Ismap (1 << 3)
33 #define URL_RealmAccess (1 << 4)
34
35 #define URL_E2EReload (1 << 5)
36 #define URL_ReloadImages (1 << 6)
37 #define URL_ReloadPage (1 << 7)
38 #define URL_ReloadFromCache (1 << 8)
39
40 #define URL_ReloadIncomplete (1 << 9)
41 #define URL_SpamSafe (1 << 10)
42
43 /*
44 * Access methods to fields inside DilloURL.
45 * (non '_'-ended macros MUST use these for initialization sake)
46 */
47 /* these MAY return NULL: */
48 #define URL_SCHEME_(u) u->scheme
49 #define URL_AUTHORITY_(u) u->authority
50 #define URL_PATH_(u) u->path
51 #define URL_QUERY_(u) u->query
52 #define URL_FRAGMENT_(u) u->fragment
53 #define URL_HOST_(u) a_Url_hostname(u)
54 #define URL_DATA_(u) u->data
55 #define URL_ALT_(u) u->alt
56 #define URL_STR_(u) a_Url_str(u)
57 /* these return an integer */
58 #define URL_PORT_(u) (URL_HOST(u) ? u->port : u->port)
59 #define URL_FLAGS_(u) u->flags
60 #define URL_POSX_(u) u->scrolling_position_x
61 #define URL_POSY_(u) u->scrolling_position_y
62 #define URL_ILLEGAL_CHARS_(u) url->illegal_chars
63 #define URL_ILLEGAL_CHARS_SPC_(u) url->illegal_chars_spc
64
65 /*
66 * Access methods that always return a string:
67 * When the "empty" and "undefined" concepts of RFC-2396 are irrelevant to
68 * the caller, and a string is required, use these methods instead:
69 */
70 #define NPTR2STR(p) ((p) ? (p) : "")
71 #define URL_SCHEME(u) NPTR2STR(URL_SCHEME_(u))
72 #define URL_AUTHORITY(u) NPTR2STR(URL_AUTHORITY_(u))
73 #define URL_PATH(u) NPTR2STR(URL_PATH_(u))
74 #define URL_QUERY(u) NPTR2STR(URL_QUERY_(u))
75 #define URL_FRAGMENT(u) NPTR2STR(URL_FRAGMENT_(u))
76 #define URL_HOST(u) NPTR2STR(URL_HOST_(u))
77 #define URL_DATA(u) NPTR2STR(URL_DATA_(u))
78 #define URL_ALT(u) NPTR2STR(URL_ALT_(u))
79 #define URL_STR(u) NPTR2STR(URL_STR_(u))
80 #define URL_PORT(u) URL_PORT_(u)
81 #define URL_FLAGS(u) URL_FLAGS_(u)
82 #define URL_POSX(u) URL_POSX_(u)
83 #define URL_POSY(u) URL_POSY_(u)
84 #define URL_ILLEGAL_CHARS(u) URL_ILLEGAL_CHARS_(u)
85 #define URL_ILLEGAL_CHARS_SPC(u) URL_ILLEGAL_CHARS_SPC_(u)
86
87
88
89 /* URL-camp compare methods */
90 #define URL_STRCAMP_EQ(s1,s2) \
91 ((!(s1) && !(s2)) || ((s1) && (s2) && !strcmp(s1,s2)))
92 #define URL_STRCAMP_I_EQ(s1,s2) \
93 ((!(s1) && !(s2)) || ((s1) && (s2) && !g_strcasecmp(s1,s2)))
94 #define URL_GSTRCAMP_EQ(s1,s2) \
95 ((!(s1) && !(s2)) || ((s1) && (s2) && !strcmp((s1)->str,(s2)->str)))
96 #define URL_GSTRCAMP_I_EQ(s1,s2) \
97 ((!(s1) && !(s2)) || ((s1) && (s2) && !g_strcasecmp((s1)->str,(s2)->str)))
98
99
100 typedef struct _DilloUrl DilloUrl;
101
102 #ifdef __cplusplus
103 extern "C" {
104 #endif /* __cplusplus */
105
106 struct _DilloUrl {
107 GString *url_string;
108 const gchar *buffer;
109 const gchar *scheme; /* */
110 const gchar *authority; /* */
111 const gchar *path; /* These are references only */
112 const gchar *query; /* (no need to free them) */
113 const gchar *fragment; /* */
114 const gchar *hostname; /* */
115 gint port;
116 gint flags;
117 const gchar *data; /* POST */
118 const gchar *alt; /* "alt" text (used by image maps) */
119 gint ismap_url_len; /* Used by server side image maps */
120 gint32 scrolling_position_x, scrolling_position_y;
121 /* remember position of visited urls */
122 gint illegal_chars; /* number of illegal chars */
123 gint illegal_chars_spc; /* number of illegal space chars */
124 };
125
126
127 DilloUrl* a_Url_new(const gchar *url_str, const gchar *base_url,
128 gint flags, gint32 posx, gint32 posy);
129 void a_Url_free(DilloUrl *u);
130 gchar *a_Url_str(const DilloUrl *url);
131 const gchar *a_Url_hostname(const DilloUrl *u);
132 DilloUrl* a_Url_dup(const DilloUrl *u);
133 gint a_Url_cmp(const DilloUrl *A, const DilloUrl *B);
134 void a_Url_set_flags(DilloUrl *u, gint flags);
135 void a_Url_set_data(DilloUrl *u, gchar *data);
136 void a_Url_set_alt(DilloUrl *u, const gchar *alt);
137 void a_Url_set_pos(DilloUrl *u, gint32 posx, gint32 posy);
138 void a_Url_set_ismap_coords(DilloUrl *u, gchar *coord_str);
139 gchar *a_Url_decode_hex_str(const gchar *str);
140 gchar *a_Url_encode_hex_str(const gchar *str);
141 gchar *a_Url_string_strip_delimiters(const gchar *str);
142
143 #ifdef __cplusplus
144 }
145 #endif /* __cplusplus */
146
147 #endif /* __URL_H__ */
0 /*
1 * File: web.c
2 *
3 * Copyright 2000 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <gtk/gtk.h>
14 #include <math.h> /* for rint */
15
16 #include "msg.h"
17 #include "browser.h"
18 #include "nav.h"
19 #include "interface.h"
20 #include "IO/IO.h"
21 #include "IO/mime.h"
22
23 #include "dw_widget.h"
24 #include "dw_gtk_scrolled_window.h"
25 #include "dw_embed_gtk.h"
26 #include "prefs.h"
27 #include "web.h"
28
29 #define DEBUG_LEVEL 5
30 #include "debug.h"
31
32 /*
33 * Local data
34 */
35 static GSList *ValidWebs = NULL; /* Active web structures list; it holds
36 * pointers to DilloWeb structures. */
37
38 /*
39 * Given the MIME content type, and a fd to read it from,
40 * this function connects the proper MIME viewer to it.
41 * Return value: 1 on success, -1 for unhandled MIME types
42 */
43 gint a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
44 CA_Callback_t *Call, void **Data)
45 {
46 DwWidget *dw = NULL;
47 DwStyle style_attrs, *style;
48 DwStyleFont font;
49
50 DEBUG_MSG(1, "a_Web_dispatch_by_type\n");
51
52 g_return_val_if_fail(Web->bw != NULL, -1);
53
54 if (Web->flags & WEB_RootUrl) {
55 /* We have RootUrl! */
56 dw = a_Mime_set_viewer(Type, Web, Call, Data);
57 if (dw == NULL)
58 return -1;
59
60 /* Set a style for the widget */
61 font.name = prefs.vw_fontname; /* must be defined */
62 font.size = rint(12.0 * prefs.font_factor);
63 font.weight = 400;
64 font.style = DW_STYLE_FONT_STYLE_NORMAL;
65
66 a_Dw_style_init_values (&style_attrs, Web->bw->main_window->window);
67 a_Dw_style_box_set_val (&style_attrs.margin, 5);
68 style_attrs.font = a_Dw_style_font_new (&font);
69 style_attrs.color =
70 a_Dw_style_color_new (prefs.text_color, Web->bw->main_window->window);
71 style_attrs.background_color =
72 a_Dw_style_color_new (prefs.bg_color, Web->bw->main_window->window);
73 style = a_Dw_style_new (&style_attrs, Web->bw->main_window->window);
74 a_Dw_widget_set_style (dw, style);
75 a_Dw_style_unref (style);
76
77 a_Dw_gtk_scrolled_window_set_dw(
78 GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), dw);
79
80 if (URL_POSX(Web->url) || URL_POSY(Web->url)) {
81 a_Dw_gtk_scrolled_window_set_scrolling_position(
82 GTK_DW_SCROLLED_WINDOW(Web->bw->docwin),
83 URL_POSX(Web->url), URL_POSY(Web->url));
84 } else {
85 gchar *pf = a_Url_decode_hex_str(URL_FRAGMENT_(Web->url));
86 a_Dw_gtk_scrolled_window_set_anchor(
87 GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), pf);
88 g_free(pf);
89 }
90
91 /* Clear the title bar for pages without a <TITLE> tag */
92 a_Interface_set_page_title(Web->bw, "");
93 a_Interface_set_location_text(Web->bw, URL_STR(Web->url));
94 a_Interface_reset_progress_bars(Web->bw);
95 /* Reset the bug meter */
96 a_Interface_bug_meter_update(Web->bw, 0);
97
98 /* Let the Nav module know... */
99 a_Nav_expect_done(Web->bw);
100
101 } else {
102 /* A non-RootUrl. At this moment we only handle image-children */
103 if (!g_strncasecmp(Type, "image/", 6))
104 dw = a_Mime_set_viewer(Type, Web, Call, Data);
105 }
106
107 if (!dw) {
108 MSG_HTTP("unhandled MIME type: \"%s\"\n", Type);
109 }
110 return (dw ? 1 : -1);
111 }
112
113
114 /*
115 * Allocate and set safe values for a DilloWeb structure
116 */
117 DilloWeb* a_Web_new(const DilloUrl *url)
118 {
119 DilloWeb *web= g_new(DilloWeb, 1);
120
121 _MSG(" a_Web_new: ValidWebs ==> %d\n", g_slist_length(ValidWebs));
122 web->url = a_Url_dup(url);
123 web->bw = NULL;
124 web->flags = 0;
125 web->Image = NULL;
126 web->stream = NULL;
127 web->SavedBytes = 0;
128
129 ValidWebs = g_slist_append(ValidWebs, (gpointer)web);
130 return web;
131 }
132
133 /*
134 * Validate a DilloWeb pointer
135 */
136 gint a_Web_valid(DilloWeb *web)
137 {
138 return (g_slist_find(ValidWebs, web) != NULL);
139 }
140
141 /*
142 * Deallocate a DilloWeb structure
143 */
144 void a_Web_free(DilloWeb *web)
145 {
146 if (!web) return;
147 if (web->url)
148 a_Url_free(web->url);
149 if (web->Image)
150 a_Image_unref(web->Image);
151 ValidWebs = g_slist_remove(ValidWebs, (gpointer)web);
152 g_free(web);
153 }
154
0 #ifndef __WEB_H__
1 #define __WEB_H__
2
3 #include <gtk/gtk.h>
4
5 #include "cache.h" /* for CA_Callback_t */
6 #include "html.h" /* for DilloHtmlLB */
7 #include "image.h" /* for DilloImage */
8
9 #ifdef __cplusplus
10 extern "C" {
11 #endif /* __cplusplus */
12
13 /*
14 * Flag defines
15 */
16 #define WEB_RootUrl 1
17 #define WEB_Image 2
18 #define WEB_Download 4 /* Half implemented... */
19
20
21 typedef struct _DilloWeb DilloWeb;
22
23 struct _DilloWeb {
24 DilloUrl *url; /* Requested URL */
25 BrowserWindow *bw; /* The requesting browser window [reference] */
26 gint flags; /* Additional info */
27
28 DilloImage *Image; /* For image urls [reference] */
29 FILE *stream; /* File stream for local saving */
30 gint SavedBytes; /* Also for local saving */
31 };
32
33
34 DilloWeb* a_Web_new (const DilloUrl* url);
35 gint a_Web_valid(DilloWeb *web);
36 void a_Web_free (DilloWeb*);
37 gint a_Web_dispatch_by_type (const char *Type, DilloWeb *web,
38 CA_Callback_t *Call, void **Data);
39
40 #ifdef __cplusplus
41 }
42 #endif /* __cplusplus */
43 #endif /* __WEB_H__ */