lynx2-8-8/000775 023711 023712 00000000000 12274305660 012762 5ustar00dickeylynx000000 000000 lynx2-8-8/ABOUT-NLS000644 023711 023712 00000033011 10004620410 014163 0ustar00dickeylynx000000 000000 Some of this discussion is obsolete - lynx does not bundle the "intl" directory, and consequently the "--with-included-gettext" configure option is not supported. ------------------------------------------------------------------------------ Notes on the Free Translation Project ************************************* Free software is going international! The Free Translation Project is a way to get maintainers of free software, translators, and users all together, so that will gradually become able to speak many languages. A few packages already provide translations for their messages. If you found this `ABOUT-NLS' file inside a distribution, you may assume that the distributed package does use GNU `gettext' internally, itself available at your nearest GNU archive site. But you do *not* need to install GNU `gettext' prior to configuring, installing or using this package with messages translated. Installers will find here some useful hints. These notes also explain how users should proceed for getting the programs to use the available translations. They tell how people wanting to contribute and work at translations should contact the appropriate team. When reporting bugs in the `intl/' directory or bugs which may be related to internationalization, you should tell about the version of `gettext' which is used. The information can be found in the `intl/VERSION' file, in internationalized packages. One advise in advance ===================== If you want to exploit the full power of internationalization, you should configure it using ./configure --with-included-gettext to force usage of internationalizing routines provided within this package, despite the existence of internationalizing capabilities in the operating system where this package is being installed. So far, only the `gettext' implementation in the GNU C library version 2 provides as many features (such as locale alias or message inheritance) as the implementation here. It is also not possible to offer this additional functionality on top of a `catgets' implementation. Future versions of GNU `gettext' will very likely convey even more functionality. So it might be a good idea to change to GNU `gettext' as soon as possible. So you need not provide this option if you are using GNU libc 2 or you have installed a recent copy of the GNU gettext package with the included `libintl'. INSTALL Matters =============== Some packages are "localizable" when properly installed; the programs they contain can be made to speak your own native language. Most such packages use GNU `gettext'. Other packages have their own ways to internationalization, predating GNU `gettext'. By default, this package will be installed to allow translation of messages. It will automatically detect whether the system provides usable `catgets' (if using this is selected by the installer) or `gettext' functions. If neither is available, the GNU `gettext' own library will be used. This library is wholly contained within this package, usually in the `intl/' subdirectory, so prior installation of the GNU `gettext' package is *not* required. Installers may use special options at configuration time for changing the default behaviour. The commands: ./configure --with-included-gettext ./configure --with-catgets ./configure --disable-nls will respectively bypass any pre-existing `catgets' or `gettext' to use the internationalizing routines provided within this package, enable the use of the `catgets' functions (if found on the locale system), or else, *totally* disable translation of messages. When you already have GNU `gettext' installed on your system and run configure without an option for your new package, `configure' will probably detect the previously built and installed `libintl.a' file and will decide to use this. This might be not what is desirable. You should use the more recent version of the GNU `gettext' library. I.e. if the file `intl/VERSION' shows that the library which comes with this package is more recent, you should use ./configure --with-included-gettext to prevent auto-detection. By default the configuration process will not test for the `catgets' function and therefore they will not be used. The reasons are already given above: the emulation on top of `catgets' cannot provide all the extensions provided by the GNU `gettext' library. If you nevertheless want to use the `catgets' functions use ./configure --with-catgets to enable the test for `catgets' (this causes no harm if `catgets' is not available on your system). If you really select this option we would like to hear about the reasons because we cannot think of any good one ourself. Internationalized packages have usually many `po/LL.po' files, where LL gives an ISO 639 two-letter code identifying the language. Unless translations have been forbidden at `configure' time by using the `--disable-nls' switch, all available translations are installed together with the package. However, the environment variable `LINGUAS' may be set, prior to configuration, to limit the installed set. `LINGUAS' should then contain a space separated list of two-letter codes, stating which languages are allowed. Using This Package ================== As a user, if your language has been installed for this package, you only have to set the `LANG' environment variable to the appropriate ISO 639 `LL' two-letter code prior to using the programs in the package. For example, let's suppose that you speak German. At the shell prompt, merely execute `setenv LANG de' (in `csh'), `export LANG; LANG=de' (in `sh') or `export LANG=de' (in `bash'). This can be done from your `.login' or `.profile' file, once and for all. An operating system might already offer message localization for many of its programs, while other programs have been installed locally with the full capabilities of GNU `gettext'. Just using `gettext' extended syntax for `LANG' would break proper localization of already available operating system programs. In this case, users should set both `LANGUAGE' and `LANG' variables in their environment, as programs using GNU `gettext' give preference to `LANGUAGE'. For example, some Swedish users would rather read translations in German than English for when Swedish is not available. This is easily accomplished by setting `LANGUAGE' to `sv:de' while leaving `LANG' to `sv'. Translating Teams ================= For the Free Translation Project to be a success, we need interested people who like their own language and write it well, and who are also able to synergize with other translators speaking the same language. Each translation team has its own mailing list, courtesy of Linux International. You may reach your translation team at the address `LL@li.org', replacing LL by the two-letter ISO 639 code for your language. Language codes are *not* the same as the country codes given in ISO 3166. The following translation teams exist, as of August 1998: Chinese `zh', Czech `cs', Danish `da', Dutch `nl', English `en', Esperanto `eo', Finnish `fi', French `fr', German `de', Hungarian `hu', Irish `ga', Italian `it', Indonesian `id', Japanese `ja', Korean `ko', Latin `la', Norwegian `no', Persian `fa', Polish `pl', Portuguese `pt', Russian `ru', Slovenian `sl', Spanish `es', Swedish `sv', and Turkish `tr'. For example, you may reach the Chinese translation team by writing to `zh@li.org'. If you'd like to volunteer to *work* at translating messages, you should become a member of the translating team for your own language. The subscribing address is *not* the same as the list itself, it has `-request' appended. For example, speakers of Swedish can send a message to `sv-request@li.org', having this message body: subscribe Keep in mind that team members are expected to participate *actively* in translations, or at solving translational difficulties, rather than merely lurking around. If your team does not exist yet and you want to start one, or if you are unsure about what to do or how to get started, please write to `translation@iro.umontreal.ca' to reach the coordinator for all translator teams. The English team is special. It works at improving and uniformizing the terminology in use. Proven linguistic skill are praised more than programming skill, here. Available Packages ================== Languages are not equally supported in all packages. The following matrix shows the current state of internationalization, as of August 1998. The matrix shows, in regard of each package, for which languages PO files have been submitted to translation coordination. Ready PO files cs da de el en es fi fr it .----------------------------. bash | [] [] | bison | [] [] | clisp | [] [] [] [] | cpio | [] [] [] | diffutils | [] [] [] | enscript | [] [] [] [] | fileutils | [] [] [] [] | findutils | [] [] [] [] | flex | [] [] | gcal | [] [] | gettext | [] [] [] [] [] | grep | [] [] [] [] | hello | [] [] [] [] [] | id-utils | [] [] | indent | [] [] | libc | [] [] [] | m4 | [] [] | make | [] [] [] | music | [] | ptx | [] [] [] | recode | [] [] [] [] | sed | | sh-utils | [] [] [] | sharutils | [] [] [] [] [] | tar | [] [] [] [] | texinfo | [] [] [] | textutils | [] [] [] [] | wdiff | [] [] [] [] | wget | [] [] [] [] | `----------------------------' cs da de el en es fi fr it 7 4 26 4 1 18 1 26 4 ja ko nl no pl pt ru sl sv .----------------------------. bash | [] | 3 bison | [] | 3 clisp | | 4 cpio | [] [] [] | 6 diffutils | [] [] | 5 enscript | [] [] | 6 fileutils | [] [] [] [] [] [] [] | 11 findutils | [] [] [] [] [] | 9 flex | [] [] | 4 gcal | [] [] [] | 5 gettext | [] [] [] [] [] [] [] | 13 grep | [] [] [] [] [] [] [] | 11 hello | [] [] [] [] [] [] [] | 12 id-utils | [] | 3 indent | [] [] [] | 5 libc | [] [] [] [] [] | 8 m4 | [] [] [] [] | 6 make | [] [] [] | 6 music | [] | 2 ptx | [] [] [] [] [] | 8 recode | [] [] [] [] [] | 9 sed | | 0 sh-utils | [] [] [] [] [] | 8 sharutils | [] [] | 7 tar | [] [] [] [] [] [] [] | 11 texinfo | [] | 4 textutils | [] [] [] [] [] | 9 wdiff | [] [] [] [] | 8 wget | [] | 5 `----------------------------' 18 teams ja ko nl no pl pt ru sl sv 29 domains 1 12 21 11 19 7 5 7 17 191 Some counters in the preceding matrix are higher than the number of visible blocks let us expect. This is because a few extra PO files are used for implementing regional variants of languages, or language dialects. For a PO file in the matrix above to be effective, the package to which it applies should also have been internationalized and distributed as such by its maintainer. There might be an observable lag between the mere existence a PO file and its wide availability in a distribution. If August 1998 seems to be old, you may fetch a more recent copy of this `ABOUT-NLS' file on most GNU archive sites. Using `gettext' in new packages =============================== If you are writing a freely available program and want to internationalize it you are welcome to use GNU `gettext' in your package. Of course the GNU Public License applies to your sources from then if you include `gettext' directly in your distribution on but since you are writing free software anyway this is no restriction. Once the sources are change appropriately and the setup can handle to use of `gettext' the only thing missing are the translations. The Free Translation Project is also available for packages which are not developed inside the GNU project. Therefore the information given above applies also for every other Free Software Project. Contact `translation@iro.umontreal.ca' to make the `.pot' files available to the translation teams. lynx2-8-8/AUTHORS000644 023711 023712 00000002164 11005212761 014021 0ustar00dickeylynx000000 000000 -- $LynxId: AUTHORS,v 1.6 2008/04/11 22:36:11 tom Exp $ -- vile:txtmode Most of the people who have contributed more than one patch to Lynx (as well as a few who have only one) are noted in the changelogs by their initials (to keep the changelog short). Here is a summary of those initials and the corresponding full names: AAC Andrey A Chernov BD Binh Do BJP Brian J Pardy BL Bela Lubkin CK Charles Karney DK Doug Kaufman DSB Scott Bigham DW David Woolley FLWM Frederic L W Meunier FM Foteos Macrides GN Glenn Nielsen GV Gisle Vanem HL Hiram Lester Jr HM Hynek Med HN Henry Nelson IC Ismael Cordeiro IZ Ilya Zakharevich JB John Bley JED John E Davis JES James E Spath JKT J Kevin Ternes JN John Nowlin KED Kim DeVaughn KW Klaus Weide LE Laura Eaves LP Leonid Pauzner LV Larry W Virden NSH nsh@horae.dti.ne.jp PBM Paul B Mahol PC Peter Canning PDS Paul D Smith PG Paul Gilmartin PHDM Philippe De Muyter PW Philip Webb RN Ryan Nielsen RS Rado Smiljanic SC Stefan Caunter SH Hiroyuki Senshu SKY Sinan Kaan Yerli SS Sergey Svishchev TD Thomas E Dickey TG Thorsten Glaser TH Hataguchi Takeshi VH Vlad Harchev WB Wayne Buttles WS Bill Schiavo lynx2-8-8/BUILD/000755 023711 023712 00000000000 12274305653 013621 5ustar00dickeylynx000000 000000 lynx2-8-8/CHANGES000644 023711 023712 00002114414 12274305312 013754 0ustar00dickeylynx000000 000000 -- $LynxId: CHANGES,v 1.730 2014/02/04 01:45:19 tom Exp $ =============================================================================== Changes since Lynx 2.8 release =============================================================================== 2014-02-04 (2.8.8pre.4) * modify the LOCALE_CHARSET feature to provide a default value for the ASSUMED_CHARSET feature aka "display-charset" (Debian #737416) -TD * fix two bugs in print-to-file from 2.8.8dev.10 changes -TD + suggested filename suffix for text/html was ".html" rather than ".txt" + using ^U to clear the filename to print to did not cancel the prompt (report by Klaus-Peter Wegge) * disable EXP_JAPANESEUTF8_SUPPORT if the system has no iconv support -TD * modify makefiles to perform the src/chrtrans rules from the top-level rather than via the src-level, solving the issue of "makefile races" -TD * reviewed minor fixes from OpenBSD CVS for these issues: - fix makefile races [espie] - read/write result checking fixes to avoid unsigned comparisons vs -1 [krw] However, the former is not an improvement; kept only the latter -TD 2014-01-11 (2.8.8pre.3) * apply analogous changes to tidytls.c interface -TD * apply openssl patch from openSUSE package for Lynx to modify the SSL options to omit the SSLv2 and compression features (report by BJP) -TD * add check for alternate package "libssl" also used with Fedora20 -TD * fix a check in configure-script for openssl subdirectory in includes. It happened to work in most cases due to a spurious blank in the pkg-config file; this was removed recently, e.g., for Fedora20 -TD * fill-in some dangling links in test-files -TD * build-fix for --disable-prettysrc (reported by Don Hsi-Yun Yang aka "omoikane") -TD * review/improve html helpfiles -TD * ensure that $(sysconfdir) exists in makefile as dependency of install-help rule -TD * update config.guess (2014-01-01), config.sub (2014-01-01) 2013-12-17 (2.8.8pre.2) * change makefile.msc and lynx-slang.iss to use dll for slang -TD * change URL for HELPFILE in lynx.cfg, etc., to omit version-specifics -TD * update example of options menu shown in user-guide -TD * restore ^Z maxscreen-toggle for Windows, omitted in 2.8.8dev.17 cleanup -TD * typographical fixes for manpage (Bjarni I. Gislason, Debian #732236). * allow fallback sleep() function to be used for MinGW -GV * remove special case in configure.in which added "-lcompat" to $LIBS for OpenBSD, MirBSD and EkkoBSD (Christian Weisgerber, Thorsten Glaser). * build-fix for --disable-forms-options -TD * omit request for admin-access in NullSoft installer, since lynx could be installed in user's directory -TD * change Windows default for LYNX_LSS_FILE to not use a directory-path -TD 2013-11-28 (2.8.8pre.1) 2013-11-28 (2.8.8dev.17) * revise/update counts in README.metrics, using a script replacing a manual procedure -TD * amend check for magic header bytes from 2.8.8dev.3 for "deflate" to limit it to the 3-bit block header described in RFC-1951 -TD * install the sample-files in the dpkg test-script -TD * add configure option --with-cfg-path and environment variables LYNX_CFG_PATH to provide search-list capability for the ".cfg" and ".lss" files -TD * modify configuration of COLOR_STYLE value in lynx.cfg, allowing multiple filenames to be specified and providing those as choices in the O'ptions menu (Debian #404893) -TD * updates for lynx_help_main.html -TD * update URLs in about_lynx.html -TD * add internal URL "LYNXEDITMAP:", which is (like "LYNXKEYMAP:") generated, making that the primary page for field-editing help -TD * improve DOSPATH-related logic in HomeEnv(), making this work properly with Windows Vista and 7. The feature is needed to read Lynx's bookmarks file from the user's "Personal" shell-folder (report by Manuel Nunez) -TD * modify samples/*.bat to work when running in a directory whose pathname contains spaces -TD * reduce required privileges for installing in lynx.iss -TD * improve sed expression appended to help_files.sed to fix a case for the edit-helpfiles which left a ".gz.gz" suffix for compressed filename URLs, overlooked since 2.8.1pre.3 -TD * modify logic in lkcstring_to_lkc() to allow named keys, e.g., from curses, to be used consistently in a KEYMAP directive -TD * add version-info to LYIcon.rc -TD * add symbols in Keysym_Strings[] and table in setup_vtXXX_keymap() for function keys 2-12, to improve keymap-configurability -TD * change extra-key #define's in LYStrings.h to enum -TD * cleanup pre-2.7 debris from LYStrings.c and LYStrings.h -TD * modify tables for key-bindings and edit-bindings to allow them to be reloaded to their initial values -TD * add check in get_connection() for ftp-connections to ensure that a password from a URL is non-empty -TD * add samples/oldlynx.bat to demonstrate how to use non-color-style -TD * add NSIS script, to allow building Windows installer via cross-compiling -TD * fixes to configure script and makefiles to work with empty $prefix, e.g., as used in MSYS -TD * improve configure check for sleep() for cross-compiling to MinGW -TD * modify configure check for inet_addr() for cross-compiling to MinGW -TD * add configure check for Win32 flavor of PDCurses when cross-compiling to MinGW using the "--with-screen=pdcurses" option -TD * improve color-style simulation of old color scheme by coloring input fields with color #5 -TD * correct search logic to match links which are wrapped on the right margin. Previous fixes to highlight arbitrarily long links overlooked this case (Debian #546264) -TD * modify the INFO page, normally bound to "=", to show decoded strings for URLs, e.g., which use %xy hexadecimal encoding. The decoded strings are shown on the line following the encoded URLs if the strings are different. Also if display-charset is UTF-8, modify -dump "References" URLs to show the corresponding decoded strings for consistency with the text which is already in UTF-8. Other URLs such as that shown in the status area are shown in encoded form per previous discussion which recommending doing this to address phishing attempts (Debian #398274) -TD * simplify file-URLs shown in reference list of -dump by trimming unnecessary "localhost", e.g., file://localhost/XXX becomes file:///XXX (Debian #334787) -TD * extend the "Bad HTML" warning feature to -dump option when the -stderr option is also set (Debian #398304) -TD * add -list_inline option, which modifies -dump output to put links inline with the text rather than in a list at the end of the dump (Debian #584080) -TD * add clarification in manpage regarding -force_html option versus -dump or -crawl (Debian #254603, Debian #295273) -TD * improve manpage descriptions of -reload, -get_data and -post_data (Debian #350853) -TD * modify manpage synopsis to make it clearer that Lynx accepts more than one path and/or URL on the command-line. The paragraph explaining this was added in 2.8.6dev.5 (Debian #350853) -TD * update COPYHEADER, clarifying license issues -TD * minor change to Content-Length logic from 2.8.8dev.13 to work with Amazon's cookies (Debian #720541) -TD * improve warning message for GNUTLS_CERT_SIGNER_NOT_FOUND (Debian #695653) -TD * ignore non-fatal return codes from gnutls_handshake introduced by SNI change in 2.8.8dev.15 (Debian #724812, patch by Hans Wurst). * updates for configure macros -TD + CF_ACVERSION_CHECK, fix from byacc for "newer" autoconf. + CF_ADD_LIB_AFTER, fix from xterm for problem with -Wl,xxx options + CF_CURSES_LIBS, modify to allow external script to set $cf_term_lib and/or $cf_curs_lib + CF_INTEL_COMPILER, $host_os needs AC_CANONICAL_HOST + CF_MIXEDCASE_FILENAMES, add msys / msysdll to known host/platform types + CF_RPATH_HACK, use sort and uniq rather than sort -u, to work with HPUX 11.11, etc. + CF_TRY_PKG_CONFIG, set variables for consistent usage of this macro + CF_XOPEN_SOURCE, add msys / msysdll to known host/platform types + CF_X_ATHENA, trim extra X libraries after updating lists, to work with ld --as-needed option which in effect uses only the first mention of the library. If that does not follow everything that depends on the library, ld will silently fail to resolve symbols. * update config.guess (2013-06-10), config.sub (2013-09-05) 2013-07-29 (2.8.8dev.16) * build-fix for setmode() definition on Cygwin -TD * modify HTUtils.h to work around header conflict with Cygwin w32api and openssl 1.0.1e (prompted by report/patch by Supriyo Biswas) -TD * protect redefined errno values for Windows port from redefinition warnings when using MinGW build, since the WSAxxx values are what the configuration actually uses -TD * modify
tag to treat it like

when used within a list -TD * update fi.po from http://translationproject.org/latest/lynx * add on/off toggles to options menu for the color-style and default-colors features, to help deal with packages which enable default colors without adjusting the color-style settings to avoid having yellow text on a white background (report by Stephen Isard) -TD * add -default-colors command-line option to allow toggling the state of the DEFAULT_COLORS setting from lynx.cfg -TD * improve discussion of -dump and -force_html in manpage (Ubuntu #1112568) -TD * cleanup quoting and use of ASCII "-" versus hyphen in manpage -TD * add checks for zero-length strings in a few places to prevent infinite loop when the focus moves to a text-field which is past the right margin due to improper placement for the nested-tables configuration (report by Rajeev V Pillai) -TD * update doctype for html documentation to 4.01 strict -TD * correct handling of backslash in TrimCommand() function introduced in 2.8.6dev.4, used to process the commands for "test=" in mime-types -TD * correct 2.8.3dev.13 check for permissible place to split UTF-8 encoded text, reported by Coverity -TD * make DONT_TRACK_INTERNAL_LINKS logic configurable via lynx.cfg as TRACK_INTERNAL_LINKS; the configure script now sets the default value -TD * fix most issues found by clang 3.2 analyze -TD * fix most issues found by Coverity scan -TD 2012-11-18 (2.8.8dev.15) * corrected position of highlighting from search/whereis function when using multibyte characters (Debian #673385) -TD * modify default case for HTLoadGopher() to use the file's suffix to obtain a MIME mapping rather than always storing unknown types to disk (suggested by Dario Niederman) -TD * modify ^X-e handling to not limit the result to the form field's length (report by Keith Bowes) -TD * modify the Inno Setup files to show lynx's version numbers. Development and prerelease versions are indicated in the numeric-only versions by prefixing a "10" or "20" -TD * ask for filesize when downloading via ftp, to use this in the read-progress ETA -TD * fix special case when -dont_wrap_pre option is used, to restore space between words which was lost when inserting a soft newline used to splice together segments of a long line (Ubuntu #806749) -TD * provide more readable ETA message as an option (prompted by patch by Joerg Hahn) -TD * add GNUTLS call to enable SNI (Server Name Indication) extension (Ubuntu #732177) -TD * correct typo for -bibhost option in manpage (Redhat #854574) -TD * revise nsl-fork logic for passing addrinfo and hostent data back to eliminate fixed limit on the number of records to return -TD * correct problem with loop logic in fill_addrinfo() exposed by multiple addresses from http://fbcdn-sphotos-d-a.akamaihd.net (report/analysis by TG) -TD * updates for configure script macros (TD): + add 3rd parameter to AC_DEFINE's to allow autoheader to run + remove unused macros * update nl.po from http://translationproject.org/latest/lynx * improve checking of certificates in the gnutls_certificate_verify_peers2() by handling special case where self-signed certificates should be reported (patch by Jamie Strandboge). * update config.guess (2012-09-25), config.sub (2012-08-18) 2012-08-22 (2.8.8dev.14) * reset anchor's actual-length calculation at the end of pumpData() to handle scenario where this is used for internal data movement, i.e., for decompressing files (report by Owen Leibman, Debian #681214) -TD * drop two files overlooked in previous commit (TD): lynx.rsp and WWW/Library/Implementation/HTFWriter.c 2012-08-15 (2.8.8dev.13) * make nsl-fork work for the DNS lookup using getaddrinfo, i.e., for IPv6 configurations (report by FLWM) -TD * add U+0218, U+0219, U+021a, and U+021b to 0x53, 0x73, 0x54, and 0x74, respectively, for Romanian s/t with cedilla in def7_uni.tbl (Ralph Babel) * modify handling of "set" in -cmd_script option to try both cfg-file and rc-file settings (prompted by discussion with Andrew Watts) -TD * update configure script to add --datarootdir option, which changes the default for man-page from /usr/lib to /usr/share -TD * modify configure check for sizeof(time_t), sizeof(off_t) to help recover if it is run in a deficient environment such as busybox -TD * limit downloaded files by Content-Length if any, to match behavior of IE, Firefox and some other browsers; this is not addressed in RFC 2616 (Debian #681214) -TD For more information, see "Content-Length in the Real World" by Eric Law: http://blogs.msdn.com/b/ieinternals/archive/2011/03/09/browsers-accommodate-incorrect-http-content-length-and-sites-depressingly-depend-on-it.aspx * fix an unbounded loop in restrictions_fun() which could cause a core dump (Debian #616107) -TD * add LDFLAGS to top-level makefile.in, for consistency with other recursive options (suggested by Naomasa Maruyama) -TD * modify makefile.in and src/makefile.in to pass make-flags, e.g., "-n" for POSIX make -TD * updated configure macros (TD): + add check for clang warning options + check for tinfo library, which may be present + omit -Wpointer-arith check for pre-3.0 gcc + add check for 'make' programs ${MAKEFLAGS} versus ${MFLAGS}, for recursive operation. * updated list for "$(TABLES)" in src/makefile.in so that parallel builds work properly (patch by Diego Elio Petteno) * remove extra "$(LDFLAGS)" from src/makefile.in when linking lynx (patch by Josef Sontgen) * correct formatting of large file-sizes in directory listing (Debian #666213) -TD * improve checking of certificates in the gnutls_certificate_verify_peers2() (report by Martin Georgiev) -TD * update de.po eo.po sv.po vi.po from http://translationproject.org/latest/lynx * use PDCurses "wide" variation in makefile.msc -TD * modified quoting for parameter values passed to blat mailer to ensure that it handles embedded blanks (report by pfourier) -TD * fix regression introduced by changes for Debian #603648 -TD * modify makew32.bat and makefile.bcb to use GnuWin32 packages to simplify builds with Borland 5.51 C++ compiler (prompted by discussion with pfourier) -TD * add configure check for windres needed for mingw build if cross-compiling -TD 2012-02-22 (2.8.8dev.12) * treat charsets ISO-8859-8-E and ISO-8859-8-I as aliases of ISO-8859-8 (Owen Leibman) * amend the dev.10 change to HTLoadDocument(), which broke caching of forms, to limit it to just the case where the user has pressed ^R, etc (report by TG) -TD 2012-02-19 (2.8.8dev.11) * correct help-message for -html5_charset option -Kihara Hideto * correct a typo in strtol change from dev.10 which caused hexadecimal numeric entities to be misrendered -TG * update eo.po, et.po and tr.po from http://translationproject.org/latest/lynx * correct dll name for bzip2 in lynx.iss package script -TD 2012-02-12 (2.8.8dev.10) * updated po/lynx.pot; there are a few new messages -TD * add "submit" and "reset" commands (Debian #603645) -TD * add "pwd" command, to show current working directory in the statusline -TD * modify check in HText_endForm() when a form contains only a single input field, to allow a return in any text-like field other than textarea to cause the form to be submitted (Debian #603648) -TD * add bzlib to win32 makefile.msc -TD * define WIN32_LEAN_AND_MEAN in makefile.msc to accommodate naming conflict in recent Win32 SDKs, which otherwise include winsock.h in windows.h -TD * fixes for the dev.9 Win32 feature to toggle between normal/fullscreen, by checking the actual screensize after maximizing the display -TD * use ASCII apostrophe for 7-bit approximation to U+02bd as well -TD * use ASCII apostrophe 0x27 for 7-bit approximation to Unicode apostrophe U+02bc (suggested by Ralph Babel) * update LYLeaks.c / LYLeaks.h to include the bstring allocation, copy and free functions -TD * several fixes for the -find-leaks option, e.g., include LYLeaks.h in a few modules, modify the StrAllocVsprintf function to update the bookkeeping, etc -TD * move call to LYCanWriteFile into LYValidateOutput, to make prompts for download, print and upload more alike -TD * correct an old bug in send_file_to_file(), used when printing a page to a file, which prevented its check for appending to an existing file -TD * modify LYValidateFilename to use LYTildeExpand -TD * modify LYConvertToURL to use LYTildeExpand for Unix, and further modify LYTildeExpand to lookup given user's home directory, thereby making commands such as "g ~root/tmp" work as expected -TD * replaced most LYgetStr calls with LYgetBString, except for LYMail.c and LYNews.c since those do use LYgetStr's limits as it was designed. Other calls generally did not need those limits -TD * modify finish_ExtEditForm to eliminate wrapping when an edited line is longer than MAX_LINE. The user is still offered the choice of wrapping to the displayed size of a TEXTAREA, but if declined, the TEXTAREA's content will not be wrapped -TD * modify LYgetstr, making it call revised function LYgetBString which handles bstring's, and allows editing fields which can grow without fixed buffer limits. In particular, forms all use the same calls, which means that their result is no longer limited by MAX_LINE -TD * extend ^X-e editing of textarea's to include single-line fields -TD * modify comparison for splitting lines to allow for long preformatted lines, e.g., using  's to not wrap when the line-wrap mode is disabled -TD * modify cfg2html.pl to handle options which contain a digit, e.g., HTML5_CHARSETS whose default value was not marked properly -TD * modify HTLoadDocument() to not retain a cached document if user is explicitly doing a refresh. This fixes the case for a #fragment url, which was otherwise treated as the same as the address without the #fragment -TD * clarify version of GPL used in README (request by Paul Menzel) -TD * modify HTLoad() to discard charset information before reloading a document, in case the server changes the content-type information between loads (report by Stanislav Brabec) -TD * use tidy to indent html documentation -TD * provide a way to substitute parameters in URLs for jumpfiles (adapted from patch by Mark Skilbeck -TD * ensure that button/input tags have a value for display, in case the tag is improperly terminated (report by Aki Helin) -TD * work around glibc bug in sscanf in SGML_character() using strtol() (report by Aki Helin) -TD * add check for charset attribute on meta element -Kihara Hideto * eliminate ON/OFF macros, using TRUE/FALSE both to work around breakage from zlib 1.2.5.1 changes as well as because they were unnecessary (GenToo #383113) -Nikos Chantziaras, TD * updated several configure script macros (TD): CF_ANSI_CC_CHECK, CF_CURSES_LIBS, CF_LD_RPATH_OPT, CF_NETLIBS, CF_XOPEN_SOURCE, CF_X_ATHENA_LIBS 2011-06-12 (2.8.8dev.9) * modfy cfg2html to add ".url" directive for referencing RFC's etc -TD * document blat/blatj usage in lynx.cfg -TD * add/use WriteStreamTitle(), to provide doctype for cookie-jar page, used to help validate the page -TD * improve readability of cookie-jar page by showing the unescaped cookie values, other minor formatting changes -TD * modify cookie domain-matching to accommodate RFC 6265, which states that a leading dot on a domain attribute should be discarded (report by Sebastien Hinderer) -TD * integrate most of the changes from patch in 2.8.6rel.4 package at http://en.sourceforge.jp/project/lynx-win32-pata -TH, TD + provide toggle between normal/fullscreen + ifdef'd changes for FEP. + correct a message translation in ja.po + modify Xsystem.c to not use MinGW's system() call. + add three items to lynx.cfg conv_jisx0201kana message_language wait_viewer_termination + replace ifdef's for CONV_JISX0201KANA_JISX0208KANA with configuration variable conv_jisx0201kana + modify makefile.bcb and lynx.rsp to use openssl and intl libraries. + add feature ifdef'd with USE_PROGRAM_DIR which adds fallback definitions for pathnames to use the directory of lynx.exe + add check for unsafe filenames in DOS/Windows, e.g., those that correspond to a device. * update command-line syntax for the blat mailer, to work with blat 2.6.2 -TD * change default in makelynx.bat to assume blat rather than blatj, because the latter does not provide a way to authenticate user/password on a mail server -TD * change #define's for addrlist-page and alt-bindings to reflect their non-experimental status -TD * change default for --enable-addrlist-page configure option to enabled -TD * modify blat/blatj configuration so that support for both is compiled-in for DOSPATH configurations. Add "-altblat" option to select blat vs blatj. Define USE_ALT_BLAT_MAILER to specify which is the default (prompted by report by LarryL) -TD * correct an interaction between LYCloseOutput() and LYRemoveTemp() as used in send_file_to_mail(), to allow a temporary file to be closed and used by external program before removing it -TD * modify lkcstring_to_lkc() to accept hex/octal values, allowing those in the KEYMAP configuration as suggested by the commented lines in lynx.cfg (report by Richie Wood) -TD * build-fix for DEC C 5.x with _DECC_V4_SOURCE defined, i.e., missing declaration of "off_t" (report/analysis by Rod Reiger) -TD * build-fixes for Alpha VMS V8.3 with C V7.1-015, based on lynx 2.8.7 development snapshot (report/analysis by Scott Harrod) -TD * improve scripts/tbl2html.pl, to handle translation of octal escapes in the approximation comments -TD * amend implementation of "readonly" attribute from 2.8.7dev.10 to distinguish it from "disabled" (report by David Paschal) -TD * amend change to cookie prefix matching from Debian #460108. The discussion overlooked this definition from RFC 2109: Path Defaults to the path of the request URL that generated the Set-Cookie response, up to, but not including, the right-most /. In that context, lynx was correct to extract the default "path" attribute of http://jukebox/cgi-bin/disorder as /cgi-bin rather than /cgi-bin/disorder as asserted in the report. However, lynx warned unnecessarily (according to the bug report) about the given path attribute. Deciding whether to suppress this warning is under control of the user via the lynx.cfg setting COOKIE_QUERY_INVALID_DOMAINS since 2.8.2dev.16 (report by Owen Leibman) -TD * add eo.po (Esperanto) from http://translationproject.org/latest/lynx * modify format of ADVANCED_COOKIE_CONFIRMATION message in nl.po per guideline to allow localized single-letter responses to prompt (report by Jurgen Gaeremy) -TD * add configure check for , used in Debian package -TD * modify src/tidy_tls.c to use gnutls_priority_set_direct() in preference to various access functions, to eliminate deprecation warnings (report by Andreas Metzler) -TD * updated several configure script macros (TD): CF_CURSES_CPPFLAGS, CF_CURSES_FUNCS, CF_CURSES_HEADER, CF_CURSES_LIBS, CF_CURSES_TERM_H, CF_DISABLE_RPATH_HACK, CF_PDCURSES_X11, CF_PKG_CONFIG, CF_RPATH_HACK, CF_STRUCT_TERMIOS, CF_XOPEN_CURSES, CF_XOPEN_SOURCE, CF_X_ATHENA_LIBS * update config.guess (2011-01-01), config.sub (2011-04-01) 2011-01-10 (2.8.8dev.8) * correct sense of menu-name parameter in add_item_to_list() from dev.7 changes (report by Larry Hart) -TD * remove duplicate copy of CF_TRY_PKG_CONFIG added to aclocal.m4 in 2.8.8dev.4, which caused autoconf-2.13 to emit weird error messages about undefined symbols (report by TG) -TD 2010-12-11 (2.8.8dev.7) * add PERSONAL_MAIL_NAME to options menu and .lynxrc (Debian #603647) -TD * remind user how to cancel message (Debian #292787) -TD * add HTML5_CHARSETS feature, which allows the user to choose whether to interpret pages without an explicit charset according to the HTML5 "compatibility" feature (Debian #604466, Debian #514897) -TD * add EXTERNAL_MENU feature to lynx.cfg, which allows the user to customize the menu-entry shown, e.g., to suppress the display of the URL (Debian #603646) -TD * cleanup URLs in lynx documentation -DK * add check in getfile() when handling a "mailto:" url, to prevent it when the user has requested a dump (Debian #563308) -TD * improve configure check for IDNA library, which may depend upon intllib, e.g., building with mingw on cygwin -DK * modify autoconf macros which look for X libraries, e.g., for PDCurses, to accommodate ongoing changes in xorg package scripts -TD * reorganize autoconf macro CF_WITH_CURSES_DIR, to make it usable for both curses and ncurses -TD * several changes to autoconf macros to lessen use of legacy shell feature "${name-value}" in favor of "${name:-value}", since the former is broken in recent versions of bash -TD * apply overlooked patch from pre-2.8.5, makes RMDIR_PATH configurable (report/patch by Frank Heckenbach). * correct one of the places where link-number is formatted, for form input anchors. This was broken in dev.6 by the -unique_urls changes (report by DK) -TD * undo a cleanup change to link-types from dev.6 which broke some uses of input-anchors (report by FLWM) -TD * minor formatting improvements to sources using cindent 2.0-20101107 -TD * restore \r to \n conversion in HTML_put_character(), broken in gcc warning cleanup (report by FLWM) -TD * fix a double-free in make_argv() (report by FLWM) -TD * add a memset in RestoreSession(), fixes uninitialized memory reference for the VLINK section -PBM * update config.guess (2010-09-24), config.sub (2010-09-11) 2010-10-04 (2.8.8dev.6) * amend change for Debian #514897 to exclude XML documents (Debian #592883) -TD * use HTParsePort() in a few places, e.g., HTFinger.c, to allow for IPv6 addresses with colons (Debian #587330) -TD * modify option -dump so it is parsed in the first pass, using that to suppress requirement for lss file if lynx is used only to dump output -TD * add option -unique-urls (Debian #586762) -TD * fix most gcc type-conversion warnings -TD * add configure check for ctags/etags programs, needed for some BSD ports -TD * add configure --with-textdomain option to allow overriding the "lynx" NLS textdomain, to help ensure that Lynx's build-script does not conflict with alternative packages -TD * use AC_ARG_PROGRAM in configure script, to support --program-suffix, etc., to help with packaging -TD * add Debian build script, for testing (adapted from lynx-cur package) -TD * add RPM build script, for testing -TD 2010-08-25 (2.8.8dev.5) * modify convert_to_idna() to check for malformed urls (Debian #594300 reports this as CVE-2010-2810) -TD * correct typo in po/makefile.inn from removal of mkdirs.sh in dev.4 (Debian #592078) -TD * correct a sign-extension error in UpdateBoundary(), used for MIME boundary computation, broken in dev.4 compiler-warning fixes -TD 2010-06-21 (2.8.8dev.4) * check for SSL error when reading response from "GET". This incidentally exposes a longstanding bug in GNUTLS: https://savannah.gnu.org/support/index.php?106987 (google the message "A TLS packet with unexpected length was received") which prevents connection to https://www.mynortonaccount.com/amsweb/default.do (report by Ignac Vucko) -TD * fix ifdef/define's in LYMain.c to show GNUTLS version in user-agent when built with tidy_tls.c -TD * improve format of X509_NAME_oneline() in tidy_tls.c, making it compatible with the OpenSSL function so that no post-processing is needed -TD * correct typo in configure --enable-gnutls-compat option, which sometimes made it enabled as a side-effect of setting --with-gnutls -TD * add configure option --enable-wais, for test-compiles with freeWAIS -TD * fixes to build with VMS -Christoph J Gartmann + created [.src]multinet_ucx.opt with a single line multinet_root:[multinet.library]ucx$ipc/LIBRARY + modified build.com to have an additional option "Multinet with UCX emulation" + modified libmake.com for the same reason + modified [.www.library.implementation]www_tcp.h for the same reason + provide definition of IS_CJK_TTY for HTWAIS.c by adding include of LYStrings.h * fix typo in users's guide -PBM * drop mkdirs.sh and MKINSTALLDIRS symbol from makefiles, using "mkdir -p" -TD * limit parsed URIs with new config parameter MAX_URI_SIZE, default 8192 (RedHat #605286, forwarded by Vincent Danen). For arbitrarily long URIs, alloca() could run out of stack space -TD * several changes to configure script, from ongoing work on xterm, etc -TD + workaround for broken ".pc" file for X Toolkit, which omits the ICE library. + modify CF_NCURSES_CONFIG to use CF_CURSES_HEADER to pick out the particular flavor of ncurses.h, e.g., + add parameter to CF_CURSES_HEADER to allow looking for specific subdirectory ncurses/ncursesw/etc + restructured CF_X_ATHENA to use pkg-config, if available. + use CF_ADD_LIB/CF_ADD_LIBS + CF_GNUTLS eliminates duplicate libraries when configuring with pkg-config + modified several macros to quote params of ifelse() + CF_AR_FLAGS allows $ARFLAGS to override the choice of ar-flags, in particular check if a given choice is part of the current $ARFLAGS + workaround CF_XOPEN_CURSES for (temporary) problem with ncurses headers, which did not account for the fact that _XOPEN_SOURCE_EXTENDED may be defined in a system header. * remove redundant updates for CFLAGS and LIBS in configure script which are now done in CF_FIND_LINKAGE macro -TD * fix a problem with configure script which broke "--with-gnutls=/usr" (report by Atsuhito Kohda) -TD * resolve warnings from "clang --analyze", tested with Fedora 12 and clang 2.6-0.5.pre1.fc12, 2.7-1.fc12 -TD * further improvements to print_wwwfile_to_fd() -TD + corrected length used for radio/checkboxes. + fill in wrapped fields. 2010-04-25 (2.8.8dev.3) * modify print_wwwfile_to_fd() to add field values to the printed form (Debian #574940) -TD * add check for magic (header bytes) before trying to decompress, since zlib does not provide this check (Redhat #503921) -TD * add workaround in CF_SSL configure macro for broken openssl pkg-config script on Redhat, CentOS -TD * add configure option --disable-rpath-hack -TD * allow IPv6 addresses without "http://" prefix (Redhat #425879, patch by Kamil Dudka) * build-fixes for OpenSolaris aka Solaris 11 -TD * add/use CF_RPATH_HACK, for constructing rpath references to libraries in nonstandard locations -TD * improve configure macros CF_CURSES_TERM_H and CF_FIND_LINKAGE -TD * add synopsis entries for -get_data and -post_data options to lynx.man (report by Dallas E. Legan II) -TD * fix a possible conflict between CF_HEADER_PATH and CF_LIBRARY_PATH by setting their respective target variables, not appending -TD * improve configure macro CF_XOPEN_SOURCE by removing rather than undefining preexisting symbols as they are added to the definitions -TD * add configure check for -lnetwork, from tin -TD * when renaming/copying a bookmark file, e.g., to delete a bookmark, modify its permissions for compatibility with IsOurFile() (Redhat #486070) -TD * fix most gcc writable-strings warnings -TD * update config.guess (2009-12-30), config.sub (2009-12-31) 2009-11-25 (2.8.8dev.2) * modify trimming of URI in LYSetCookie() to eliminate trimming of final leaf (Debian #460108) -TD * document the various xxx_PATH settings in lynx.cfg -TD * modify cfg2html.pl to improve formatting of cattoc.html -TD * split-up top-level makefile install-html rule to allow generating the htmlized cfg without doing an install -TD * suppress positioning for editor when using it to edit files via dired -TD * modify samples/lynx-demo.cfg to suppress external file-utilities, since the intent is to make the installer work standalone, but allow extension -TD * add traces for builtin dired operations -TD * modify built-in "touch" for dired to use binary mode when opening file -TD * remove check from 2.8.5dev.11 which prevents user from moving a directory in dired unless the external program "mv" is provided -TD * regularize use of isEmpty(), non_empty() -BL * match built-in "positionable" editor names more liberally -BL * promote some experimental options to normal, tidy up related EXP_xxx vs USE_xxx symbols -TD EXP_ASCII_CTYPES is now USE_ASCII_CTYPES EXP_JUSTIFY_ELTS is now USE_JUSTIFY_ELTS EXP_CHARSET_CHOICE is now USE_CHARSET_CHOICE The scrollbar, progressbar, sessions and session-cache options are now enabled by default. * modify scanning in HTRules.c to only trim comments where '#' is either at the beginning of a line, or follows whitespace (patch by Kihara Hideto). * correct a place where LYStrExtent2 was used where byte-count is needed (patch by Bake Timmons). * modify LYExecv() in LYLocal.c to allow win32 applications to use this function -TD * modify ok_stat() in LYLocal.c to retry with "." appended when the path syntax indicates that it is probably a directory name -TD * fix an old typo in configure macro CF_CHECK_FUNCDECL -TD * change library dependency for gnutls from crypt to gcrypt, originally in 2.8.5dev.15 (Debian #555579) -TD * update de.po from http://translationproject.org/latest/lynx * fix some tidy- and linklint-warnings in help-files -TD * amend change from 2.8.7dev.14 to not use clrscr() function in stop_curses() if using PDCurses, since clrscr() is not in the win32 api -TD * modify configure script to check if linkage for bzlib and zlib succeeded, before defining symbol which makes the compiler uses these libraries -TD * update lynx_help_main.html to point to "release/lynx2-8-7" documentation -TD 2009-08-28 (2.8.8dev.1) * add include in socklen_t configure check (from OpenBSD CVS) * eliminate UCPutUtf8ToBuffer() - redundant -TD * use memset's to simplify some initialization in HTML.c, extending a change made in 2.8.7dev.10 (prompted by issue in OpenBSD) -TD * add optional support for IDNA using GNU libidn (Debian #352596) -TD * ignore LEFT-TO-RIGHT-MARK (U+200E) in HTML files (Debian #408835) -TD * correct check for return-value from gnutls_certificate_verify_peers2(), which in conjunction with unclean internals of gnutls caused caused some sites to be treated as if they were version-1 X.509 CAs (Debian #231609, Ubuntu 293708) -TD * revise dired-mode's modify_tagged() function, correcting and extending the source-paths to validate against the target path. Before, lynx's current working directory was used to validate against target path, i.e., when moving all tagged files to a new location. Lynx's check to ensure that source/target paths are distinct prevented users from moving tagged files to the current directory (report by Jasper) -TD * change compiled-in default for SYSLOG_REQUESTED_URLS to false (prompted by Debian #537907) -TD * adjust ifdef in change_sug_filename() so that paths containing square brackets are trimmed on VMS only (report by Gaute Strokkenes) -TD * amend change to ifdef in LYMain.c (from 2.8.7dev.14), since it prevents build on NetBSD, whose libintl.h does not include locale.h (report by Thomas Klausner) -TD * modify configure macro CF_GCC_ATTRIBUTES to make it more self-contained -TD * improve configure check for _XOPEN_SOURCE for HPUX 11 to ensure mbstate_t is declared -TD * update config.guess (2009-08-19), config.sub (2009-08-19) 2009-07-05 (2.8.7rel.1) * update metrics for 2.8.7 release -TD * fix ifdef'ing for cfg_bad_html (report by Gabor Z Papp) -TD 2009-06-23 (2.8.7pre.6) * update lynx.pot file -TD * add "Bad HTML messages" to Options menu, letting the user disable the warning message, write the detailed messages to the LYNXMESSAGES: status buffer (suggested by BL) -TD * finish checkbox for send-useragent from pre.3 changes -TD * make the size of LYNXMESSAGES configurable in lynx.cfg with STATUS_BUFFER_SIZE -TD 2009-06-06 (2.8.7pre.5) * add missing ctrl_chars_on_previous_line when computing 'spare' value in split_line() for the wide-curses configuration. The justified line was shorter than expected when justifying text -TD * fix a special case of trailing whitespace not shown in pretty source, at the end of a closing tag -TD * remove "Bad HTML" warning for buttons outside a form, since those can be inline, according to the HTML 4 DTD -TD * provide navigation to script-buttons, to make them more visible, showing their name -TD * change default for send_useragent option to true (prompted by discussion on lynx-dev) -TD * correct check for default type of HTML BUTTON, which is "submit". The code treated this as "button" (prompted by discussion on lynx-dev) -TD 2009-05-25 (2.8.7pre.4) * update lynx.pot file -TD * fix a typo in change for Debian #388622 -TD 2009-05-25 (2.8.7pre.3) * suppress check for "disabled" attribute in a select, as a workaround (Debian #525934) -TD * accommodate (in)compatibility "feature" in HTML5 draft which replaces ISO-8859-1 with Windows-1252, as indicated here: http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#character-encodings-0 (Debian #514897) -TD * remove check for -dump option from HTHandleAuthInfo(), allowing Lynx to -dump or -source NNTP urls (report by Dallas E Legan II) -TD * amend fix for Debian #388622 to allow the user to save names to the home directory, e.g., ~/foo -TD * add an Options menu checkbox to tell if Lynx should send a user-agent string. Unless the useragent restriction is set, the default for this checkbox is off, so that Lynx will not send the string. The corresponding setting, send_useragent, may be saved to ~/.lynxrc, but normally is not -TD * correct type for HTNewsGetCharacter() in HTNews.c, which could return a sign-extended character mistaken for EOF (report by Dallas E Legan II) -TD 2009-04-26 (2.8.7pre.2) * update it.po from http://translationproject.org/latest/lynx/ * update lynx user's guide section on Options Menu -TD * modify prompt in LYLoadCGI() from 2.8.6dev.15 to always prompt user (from FEDORA-2008-9597), and modify compiled-in configuration default for consistency with other lynx.cfg settings to require that lynx.cfg be set to permit use of lynxcgi scripts -TD * correct parsing of "--" command-line parameter (Redhat #311031) -TD * check for malformed select before adding last-option (Redhat #152146) -TD * change default for --enable-ascii-ctypes to true -TD * modify Lynx's DTD information to allow it to display form-related tags that are inline, even without being in a form as indicated in http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd for %inline.forms and %misc.inline (Debian #398986) -TD * finish dtd_util, making it able to load and save data from a flat file that can be edited -TD * correct length of padding with underscores in LYhighlight, which used the wrong variable based on glyph count rather than cells (Debian #519199) -TD * fix a few cases where PUTC's intended for pretty-src would display in the HTML view (Debian #521489) -TD * fix some mismatched BOOL vs BOOLEAN from compiler warnings due to dev.13 change to LYStructs.h -TD * add TNS SNI support for the OpenSSL configuration -Phil Pennock * add docs/README.cookies and docs/README.options -Stefan Caunter * update docs/README.sslcerts -Stefan Caunter * add NO_PAUSE setting to lynx.cfg, .lynxrc and Options menu (request by Stefan Caunter) -TD 2009-03-17 (2.8.7pre.1) 2009-03-17 (2.8.7dev.14) * modify command-passing for Win32/DOS to use double-quotes -TD * adapt features from vile's install-script to set registry variables, etc. -TD * modify LYgetenv() for Win32 platforms to check also in the system registry for settings, adapted from vile -TD * modify SGML_write() to check for UCS-2 BOMs, to provide support for UCS-2 pages (prompted by comment by TG) -TD * modify SGML_write() to check for UTF-8 BOM, using that as a hint to set the default document charset to UTF-8 (prompted by mailing list comments) -TD * modify stop_curses() function to fix missing DJGPP and MinGW code -DK, TD * modify config.hin to fix prototype for getttimeofday() with MinGW -DK * ifdef to avoid conflicting definition of set_timeout() in DJGPP -DK * modify ifdef in LYMain.c to avoid possible conflict between libintl.h and locale.h -DK * modify configure script to avoid using symbolic links for MinGW, in case the script is run in Cygwin -DK * modify makefile.msc to add LYIcon object -TD * modify configure script to add LYIcon object for MinGW -DK * add sample scripts for configuring MinGW version using Cygwin -DK 2009-02-01 (2.8.7dev.13) * modify definitions in LYStructs.h for union to cast to a void* rather than a long, to help with 64-bit ports -TD * modify CF_SSL configure macro to check for -ldl needed for recent OpenSSL versions -TD * modify CF_SSL configure macro to build with MSYS for MinGW configuration -TD * modify scanning of floats from lynx.cfg to allow Lynx to read POSIX values in non-POSIX locales -TD * modify configure script to check for msginit, needed to generate "en" po file, and omit that target if msginit is not available, letting lynx build with older gettext versions -TD * improve drawing of menus for multibyte characters by changing the way the number of fill-characters is computed in LYpaddstr -TD * fix some uses of gettext in options menu and info page where a translated string might contain angle-brackets or ampersand -TD * modify AM_WITH_NLS configure macro to add the iconv library dependency for intl library when checking for the latter -DK * fix breakage of display of UTF-8 in UCTransUniChar() from cleanup in dev.12 (report by TG) -TD * rename variable defined by CF_PATHSP to PATH_SEPARATOR, use this consistently to ease use in later autoconf versions (prompted by patch by DK) -TD * fix a typo in CF_ADD_LIBDIR which broke check for libintl (report by Jose H Espinosa) -TD * modify configure script to append to CFLAGS or CPPFLAGS rather than prepend, to better match the user's intent -DK, TD * modify UCSetBoxChars() to let line-drawing work with PDCurses, as well as any fancy-curses implementation that is running in ASCII or Latin-1 -TD * add a configure check for curses type "chtype" -TD * fix for an ASCII dependency in LYKeymap.c -PG * update fr.po, id.po, nl.pl, sv.po, tr.po, vi.po from http://translationproject.org/latest/lynx/ * change configure script to check for "ar" archiver options, from tin bug report for FreeBSD 8.0 which requires a "-" before the options -TD * update config.guess (2008-12-19), config.sub (2008-12-11) * use putenv() in preference to unsetenv() in LYReadCFG.c, noting that it is a deprecated BSD interface -TD * miscellaneous fixes for gcc type-conversion warnings -TD * remove trailing comma from enumeration UCStatus which causes strict-compiler warning -TD 2008-12-31 (2.8.7dev.12) * add configure option --enable-ascii-ctypes to enable EXP_ASCII_CTYPES -TD * move EBCDIC tables to separate file LYebcdic.c to simplify building test driver for parsdate.c on z/OS -PG * fix an off-by-one difference between UTF-8/non-UTF-8 comparisons in HText_appendCharacter() which would leave an extra character wrapped in some cases, e.g., in the options menu when field-numbering was turned on -TD * remove logic in GridText.c for ignoring "excess" characters in a field when fields are numbered, since highlighting of wrapped multiline links works, and this feature is incompatible with it -TD * remove obsolete CF_VARARGS configure check -TD * modify configure script to build with MinGW and MSYS -TD * modify configure macro CF_XOPEN_SOURCE to define _ALL_SOURCE for AIX 6.x -TD * implement a LONG_LIST equivalent for ftp, configurable as FTP_FORMAT -TD * adapt some of the SH_EX ifdef'd features (TD): + extra keypad conversions in LYgetch_for() are for PDCurses. + add -show_cfg option. + entify embedded blanks in HTDOS_wwwName(). + treat null file-pointer in HTParseFile(HTFormat() as end-of-file. * change the phase during which "-help" option is processed, to allow it to reflect the configured values of the options in the help-message (prompted by discussion with Dan Jacobson regarding Debian #507083) -TD * modify havevisible() and UCTransToUni() functions to process UTF-8 input, fixing a problem saving bookmarks whose title is UTF-8 (prompted by patch by Takao Fujiwara) -TD * restore logic in HTLoadHTTP() to strip matching square brackets from host name and certificate host (comments by TG) -TD * modify UCdomap.c to work with Solaris iconv and handle additional encodings: + if "TRANSLIT" feature (an extension of glibc) does not succeed, retry the call to iconv_open without "TRANSLIT" + add check for any MIME name beginning "iso8859", mapping to "iso-8859" + recognize "eucjp" MIME name as alias for "euc-jp" + recognize "pck" MIME name as alias for "shift_jis" + recognize "ansi-1251" MIME name as alias for "windows-1251" (patch by Takao Fujiwara) * add "*.dbg" to suffixes to ignore in autoconf script when checking for executables and objects produced by C compile (report by PG, for z/OS) -TD * modify parsdate.y to convert between EBCDIC/ASCII to work on z/OS -PG, -TD * modify src/makefile.in to make it simple for a build script running in a separate directory to regenerate parsdate.c (prompted by discussion with PG regarding building on z/OS which uses EBCDIC) -TD * add configure check for yacc (report by PG) -TD * fixes to make configure --help agree with INSTALLATION (report by PW) -TD * add id.po from http://translationproject.org/latest/lynx/ * add a null-pointer check for content type, fix for dev.11 changes (Debian #509321) -TD * improve configurability with regard to pkg-config by adding --with-pkg-config option, which can be used to enable/disable use of the program as well as specify the pathname used (prompted by comments by TG) -TD * correct handling of --with-ssl option when pkg-config is not found -TD 2008-12-14 (2.8.7dev.11) * add support for HTML5 rel=author in link (mailing list comments) -TD * modify cookie-writing to not write if no cookies were read from the file and none are available (Debian #354282) -TD * fix src/tidy_tls.c X509_get_issuer_name to actually take the issuer DN of the present certificate and not hope that it is the same as taking the subject DN of the "next" certificate which may or may not exist (Debian #499945, patch by Thomas Viehmann) * modify exit code when doing a "-dump" to exit with error if the server returned an error status for the page (Debian #299711) -TD * fix ipv6 literal command-line parsing (Debian #180654, analysis by Fabio Massimo Di Nitto) -TD * extend configure macros CF_SSL and CF_GNUTLS to check for pkg-config, using that for the default if the corresponding openssl or gnutls packages are installed (suggested by PGNet) -TD * add fi.po from http://translationproject.org/latest/lynx/ * update fr.po from http://translationproject.org/latest/lynx/ * correct handling of the option value from configure "--with-ssl", which was not being used in the search-list (report by PGNet) -TD * update configure script macros -TD CF_CURSES_LIBS - add "pdcurses", e.g., to link with MinGW CF_UTF8_LIB - use CF_FIND_LINKAGE to better work with BSD ports. CF_XOPEN_SOURCE - add case for dragonfly * document width-limitation in manpage, fix a few stray 1024's still in the source (report by Barry Haddow) -TD * add "read_timeout" to lynx.cfg, and -read_timeout option to command-line (prompted by Dries De Moor report on mailing list) -TD * modify to handle a special case where the content-type is given as one of the compressed types, to check if the address (after stripping the file suffix for that compression) has a suffix that lynx could present -TD For example: http://foo/bar.html.gz would display the uncompressed "bar.html" rather than offering to download the file. This also allows one to add SUFFIX commands to lynx.cfg to display the corresponding plain files. For example: SUFFIX:CHANGES.*:text/plain:8bit SUFFIX:CHANGES:text/plain:8bit for http://foo/CHANGES.tmp.gz http://foo/CHANGES.gz * workaround for STRING redefinition in parsdate.c on DJGPP -GV * improve configure-check to determine proper fallback when no long-long printing format is available -TD * update win32 makefiles/build scripts to add LYmktime, parsdate modules -TD * update config.guess (2008-04-14), config.sub (2008-06-16) 2008-09-21 (2.8.7dev.10) * remove rw.po, since the translation project no longer supplies it -TD * implement "readonly" attribute for TEXTAREA and TEXT fields -TD * update Lynx's tables of HTML attributes to cover (except for events) the keywords from HTML 4.01 -TD * modify initial active link in download-page to be the first download action rather than the "help" link which is provided in Novice mode (Debian #376259) -PBM, TD * implement "chunked" transfer-encoding to work with servers that ignore the version number in HTTP get's -TD * change initialization of trace file, handing this during the first part of argument parsing along with -help and -version, to show steps done for initialization of presentors, etc -TD * minor fix to LYstartPopup() to handle a case where the popup menu was too large for the screen -TD * fixes for srcdir!=objdir (report by FLWM) -TD * modify man2hlp.sh to work from subdirectory, e.g., its parameter would be "../lynx.man" -TD * set POSIX locale for install-cfg.sh and man2hlp.sh since they use character ranges -TD * add switch -xhtml-parsing and lynx.cfg XHTML_PARSING setting to control whether the extensions for XHTML 1.0 are used (prompted by bug report by FLWM, as well as discussion on lynx-dev) -TD * modify external editing of TEXTAREA to not do tab-conversion -TD * modify LYhighlight() to limit display of multicolumn characters -TD * rewrite LYRefreshEdit(), to display multibyte/multicolumn characters properly when using wide-character curses -TD * revise introductory comment written to ".lynxrc" (Debian #461158) -TD * discard anchor's post_data field in HTLoadDocument() if Lynx is about to reload a document. That would happen if the result of the form includes a link back to the form. Removing the data causes Lynx to prompt the user, e.g., Resubmit POST content to http://localhost/cgi-bin/lynxtest.pl ? (y/n) to offer the user the choice between revisiting form or re-POST'ing the data that was on the form (report by Andreas K Foerster) -TD * fixes for LYHighlight() when the highlighted text is empty -TD * translate named entities, etc., for "content" field of refresh-URL, cf: 2.8.5dev.13 (report by Ivan Shmakov) -TD * suppress computation in TrimmedLength() for source-view, which is not needed for Debian #204515 (patch by Mike Knight) * add check-po rule to po/makefile -TD * modify top-level makefile to work with configure --srcdir -TD * add update-po rule to top-level makefile -TD * update fr.po, nl.po, tr.po, vi.po, zh_TW.po,sv.po from http://translationproject.org/latest/lynx/ * parse doctype for xhtml, to tell when empty tags such as "



Unterminated SELECT

OPTION not within SELECT

TEXTAREA ending without starting

lynx2-8-8/test/c1.html000644 023711 023712 00000007445 10164770217 015142 0ustar00dickeylynx000000 000000 Test of invalid NCRs 128-159

Test of invalid NCRs 128-159

Authoring tools on MS Windows, in particular MS FrontPage ("WYSIWYG" HTML editor), generate invalid Numerical Character References for characters commonly found in positions 128...159 (0x80...0x9f) in Windows fonts. Although these are valid codepoints for windows-1252 (and other windows-xxxx) charsets, valid NCRs always refer to the document character set in the SGML sense, not to the character encoding scheme (or charset). For HTML, the SGML document character set is fixed, it is always a subset of Unicode (or ISO 10646). In Unicode and its iso-8859-1 subset, values 128...159 are C1 control characters, they must not appear in HTML. Valid NCRs for the intended characters use Unicode values greater than 256.

Lynx tries to interpret some of the invalid codes, by assuming that they are windows-1252 codepoints.

 
 
You may want to press '\' to view the source of this test. 
 
Code      invalid NCR     valid NCR, description 
        normal   in ALT  				 
                             
0x80    €	€ €	#EURO SIGN 
0x81    	 	#NOT USED 
0x82    ‚	‚ ‚	#SINGLE LOW-9 QUOTATION MARK 
0x83    ƒ	ƒ ƒ	#LATIN SMALL LETTER F WITH HOOK 
0x84    „	„ „	#DOUBLE LOW-9 QUOTATION MARK 
0x85    …	… …	#HORIZONTAL ELLIPSIS 
0x86    †	† †	#DAGGER 
0x87    ‡	‡ ‡	#DOUBLE DAGGER 
0x88    ˆ	ˆ ˆ	#MODIFIER LETTER CIRCUMFLEX ACCENT 
0x89    ‰	‰ ‰	#PER MILLE SIGN 
0x8a    Š	Š Š	#LATIN CAPITAL LETTER S WITH CARON 
0x8b    ‹	‹ ‹	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK 
0x8c    Œ	Œ Œ	#LATIN CAPITAL LIGATURE OE 
0x8d    	 	#NOT USED 
0x8e    Ž	Ž 	#NOT USED 
0x8f    	 	#NOT USED 
0x90    	 	#NOT USED 
0x91    ‘	‘ ‘	#LEFT SINGLE QUOTATION MARK 
0x92    ’	’ ’	#RIGHT SINGLE QUOTATION MARK 
0x93    “	“ “	#LEFT DOUBLE QUOTATION MARK 
0x94    ”	” ”	#RIGHT DOUBLE QUOTATION MARK 
0x95    •	• •	#BULLET 
0x96    –	– –	#EN DASH 
0x97    —	— —	#EM DASH 
0x98    ˜	˜ ˜	#SMALL TILDE 
0x99    ™	™ ™	#TRADE MARK SIGN 
0x9a    š	š š	#LATIN SMALL LETTER S WITH CARON 
0x9b    ›	› ›	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 
0x9c    œ	œ œ	#LATIN SMALL LIGATURE OE 
0x9d    	 	#NOT USED 
0x9e    ž	ž 	#NOT USED 
0x9f    Ÿ	Ÿ Ÿ	#LATIN CAPITAL LETTER Y WITH DIAERESIS 
 
lynx2-8-8/test/cp-1252.html000644 023711 023712 00000032366 11206604033 015616 0ustar00dickeylynx000000 000000 Character table for cp-1252

cp-1252 table

Description                                 Code            Entity name   
===================================         ============    ==============
quotation mark                              "  --> "     "   --> "
ampersand                                   &  --> &     &    --> &
less-than sign                              &#60;  --> <     &lt;     --> <
greater-than sign                           &#62;  --> >     &gt;     --> >

Description                            Char Code            Entity name   
===================================    ==== ============    ==============
euro sign                                  &128; --> €
single low-9 quotation mark                &130; --> ‚
latin small letter f with hook             &131; --> ƒ
double low-9 quotation mark                &132; --> „
horizontal ellipsis                        &133; --> …
dagger                                     &134; --> †
double dagger                              &135; --> ‡
modifier letter circumflex accent          &136; --> ˆ
per mille sign                             &137; --> ‰
latin capital letter s with caron          &138; --> Š
single left-pointing angle quote mark      &139; --> ‹
latin capital ligature oe                  &140; --> Œ
latin capital letter z with caron          &142; --> Ž

left single quotation mark                 &145; --> ‘
right single quotation mark                &146; --> ’
left double quotation mark                 &147; --> “
right double quotation mark                &148; --> ”
bullet                                     &149; --> •
en dash                                    &150; --> –
em dash                                    &151; --> —
small tilde                                &152; --> ˜
trade mark sign                            &153; --> ™
latin small letter s with caron            &154; --> š
single right-pointing angle quote mark     &155; --> ›
latin small ligature oe                    &156; --> œ
latin small letter z with caron            &158; --> ž
latin capital letter y with diaeresis      &159; --> Ÿ

non-breaking space                         &#160; -->      &nbsp;   -->  
inverted exclamation                       &#161; --> ¡    &iexcl;  --> ¡
cent sign                                  &#162; --> ¢    &cent;   --> ¢
pound sterling                             &#163; --> £    &pound;  --> £
general currency sign                      &#164; --> ¤    &curren; --> ¤
yen sign                                   &#165; --> ¥    &yen;    --> ¥
broken vertical bar                        &#166; --> ¦    &brvbar; --> ¦
section sign                               &#167; --> §    &sect;   --> §
umlaut (dieresis)                          &#168; --> ¨    &uml;    --> ¨
copyright                                  &#169; --> ©    &copy;   --> ©
feminine ordinal                           &#170; --> ª    &ordf;   --> ª
left angle quote, guillemotleft            &#171; --> «    &laquo;  --> «
not sign                                   &#172; --> ¬    &not;    --> ¬
soft hyphen                                &#173; --> ­    &shy;    --> ­
registered trademark                       &#174; --> ®    &reg;    --> ®
macron accent                              &#175; --> ¯    &macr;   --> ¯

degree sign                                &#176; --> °    &deg;    --> °
plus or minus                              &#177; --> ±    &plusmn; --> ±
superscript two                            &#178; --> ²    &sup2;   --> ²
superscript three                          &#179; --> ³    &sup3;   --> ³
acute accent                               &#180; --> ´    &acute;  --> ´
micro sign                                 &#181; --> µ    &micro;  --> µ
paragraph sign                             &#182; --> ¶    &para;   --> ¶
middle dot                                 &#183; --> ·    &middot; --> ·
cedilla                                    &#184; --> ¸    &cedil;  --> ¸
superscript one                            &#185; --> ¹    &sup1;   --> ¹
masculine ordinal                          &#186; --> º    &ordm;   --> º
right angle quote, guillemotright          &#187; --> »    &raquo;  --> »
vulgar fraction one-quarter                &#188; --> ¼    &frac14; --> ¼
vulgar fraction one-half                   &#189; --> ½    &frac12; --> ½
vulgar fraction three-fourths              &#190; --> ¾    &frac34; --> ¾
inverted question mark                     &#191; --> ¿    &iquest; --> ¿

latin capital letter a with grave          &#192; --> À    &Agrave; --> À
latin capital letter a with acute          &#193; --> Á    &Aacute; --> Á
latin capital letter a with circumflex     &#194; --> Â    &Acirc;  --> Â
latin capital letter a with tilde          &#195; --> Ã    &Atilde; --> Ã
latin capital letter a with diaeresis      &#196; --> Ä    &Auml;   --> Ä
latin capital letter a with ring above     &#197; --> Å    &Aring;  --> Å
latin capital letter ae                    &#198; --> Æ    &AElig;  --> Æ
latin capital letter c with cedilla        &#199; --> Ç    &Ccedil; --> Ç
latin capital letter e with grave          &#200; --> È    &Egrave; --> È
latin capital letter e with acute          &#201; --> É    &Eacute; --> É
latin capital letter e with circumflex     &#202; --> Ê    &Ecirc;  --> Ê
latin capital letter e with diaeresis      &#203; --> Ë    &Euml;   --> Ë
latin capital letter i with grave          &#204; --> Ì    &Igrave; --> Ì
latin capital letter i with acute          &#205; --> Í    &Iacute; --> Í
latin capital letter i with circumflex     &#206; --> Î    &Icirc;  --> Î
latin capital letter i with diaeresis      &#207; --> Ï    &Iuml;   --> Ï

latin capital letter eth                   &#208; --> Ð    &ETH;    --> Ð
latin capital letter n with tilde          &#209; --> Ñ    &Ntilde; --> Ñ
latin capital letter o with grave          &#210; --> Ò    &Ograve; --> Ò
latin capital letter o with acute          &#211; --> Ó    &Oacute; --> Ó
latin capital letter o with circumflex     &#212; --> Ô    &Ocirc;  --> Ô
latin capital letter o with tilde          &#213; --> Õ    &Otilde; --> Õ
latin capital letter o with diaeresis      &#214; --> Ö    &Ouml;   --> Ö
multiplication sign                        &#215; --> ×    &times;  --> ×
latin capital letter o with stroke         &#216; --> Ø    &Oslash; --> Ø
latin capital letter u with grave          &#217; --> Ù    &Ugrave; --> Ù
latin capital letter u with acute          &#218; --> Ú    &Uacute; --> Ú
latin capital letter u with circumflex     &#219; --> Û    &Ucirc;  --> Û
latin capital letter u with diaeresis      &#220; --> Ü    &Uuml;   --> Ü
latin capital letter y with acute          &#221; --> Ý    &Yacute; --> Ý
latin capital letter thorn                 &#222; --> Þ    &THORN;  --> Þ
latin small letter sharp s                 &#223; --> ß    &szlig;  --> ß

latin small letter a with grave            &#224; --> à    &agrave; --> à
latin small letter a with acute            &#225; --> á    &aacute; --> á
latin small letter a with circumflex       &#226; --> â    &acirc;  --> â
latin small letter a with tilde            &#227; --> ã    &atilde; --> ã
latin small letter a with diaeresis        &#228; --> ä    &auml;   --> ä
latin small letter a with ring above       &#229; --> å    &aring;  --> å
latin small letter ae                      &#230; --> æ    &aelig;  --> æ
latin small letter c with cedilla          &#231; --> ç    &ccedil; --> ç
latin small letter e with grave            &#232; --> è    &egrave; --> è
latin small letter e with acute            &#233; --> é    &eacute; --> é
latin small letter e with circumflex       &#234; --> ê    &ecirc;  --> ê
latin small letter e with diaeresis        &#235; --> ë    &euml;   --> ë
latin small letter i with grave            &#236; --> ì    &igrave; --> ì
latin small letter i with acute            &#237; --> í    &iacute; --> í
latin small letter i with circumflex       &#238; --> î    &icirc;  --> î
latin small letter i with diaeresis        &#239; --> ï    &iuml;   --> ï

latin small letter eth                     &#240; --> ð    &eth;    --> ð
latin small letter n with tilde            &#241; --> ñ    &ntilde; --> ñ
latin small letter o with grave            &#242; --> ò    &ograve; --> ò
latin small letter o with acute            &#243; --> ó    &oacute; --> ó
latin small letter o with circumflex       &#244; --> ô    &ocirc;  --> ô
latin small letter o with tilde            &#245; --> õ    &otilde; --> õ
latin small letter o with diaeresis        &#246; --> ö    &ouml;   --> ö
division sign                              &#247; --> ÷    &divide; --> ÷
latin small letter o with stroke           &#248; --> ø    &oslash; --> ø
latin small letter u with grave            &#249; --> ù    &ugrave; --> ù
latin small letter u with acute            &#250; --> ú    &uacute; --> ú
latin small letter u with circumflex       &#251; --> û    &ucirc;  --> û
latin small letter u with diaeresis        &#252; --> ü    &uuml;   --> ü
latin small letter y with acute            &#253; --> ý    &yacute; --> ý
latin small letter thorn                   &#254; --> þ    &thorn;  --> þ
latin small letter y with diaeresis   {}  {&#255;}-->{ÿ}  {&yuml;}  -->{ÿ}

Some other characters of interest      Char Code            Entity name   
===================================    ==== ============    ==============
capital AE diphthong (ligature)        N/A  &#198; --> Æ    &AElig;  --> Æ
small ae diphthong (ligature)          N/A  &#230; --> æ    &aelig;  --> æ
capital OE ligature                    N/A {&#338;}-->{Œ}  {&OElig;} -->{Œ}
small oe ligature                      N/A {&#339;}-->{œ}  {&oelig;} -->{œ}
copyright                              N/A  &#169; --> ©    &copy;   --> ©
registered trademark                   N/A  &#174; --> ®    &reg;    --> ®
trademark sign                         N/A  &#8482;--> ™   &trade;  --> ™
em space                               N/A [&#8195;]->[ ] [&emsp;]  -->[ ]
en space                               N/A [&#8194;]->[ ] [&ensp;]  -->[ ]
1/3-em space                           N/A [&#8196;]->[ ] [&emsp13;] -->[ ]
1/4-em space                           N/A [&#8197;]->[ ] [&emsp14;] -->[ ]
thin space                             N/A [&#8201;]->[ ] [&thinsp;]-->[ ]
hair space                             N/A [&#8202;]->[ ] [&hairsp;]-->[ ]
em dash                                N/A [&#8212;]->[—] [&mdash;] -->[—]
en dash                                N/A [&#8211;]->[–] [&ndash;] -->[–]

lynx2-8-8/test/cp-1252a.html000644 023711 023712 00000033073 11206604033 015753 0ustar00dickeylynx000000 000000 Character table for cp-1252

cp-1252 table

Description                                 Code            Entity name   
===================================         ============    ==============
quotation mark                              &#34;  --> "     &quot;   --> "
ampersand                                   &#38;  --> &     &amp;    --> &
less-than sign                              &#60;  --> <     &lt;     --> <
greater-than sign                           &#62;  --> >     &gt;     --> >

Description                            Char Code            Entity name   
===================================    ==== ============    ==============
euro sign                                  &128; --> €
undefined                                  &129; --> 
single low-9 quotation mark                &130; --> ‚
latin small letter f with hook             &131; --> ƒ
double low-9 quotation mark                &132; --> „
horizontal ellipsis                        &133; --> …
dagger                                     &134; --> †
double dagger                              &135; --> ‡
modifier letter circumflex accent          &136; --> ˆ
per mille sign                             &137; --> ‰
latin capital letter s with caron          &138; --> Š
single left-pointing angle quote mark      &139; --> ‹
latin capital ligature oe                  &140; --> Œ
undefined                                  &141; --> 
latin capital letter z with caron          &142; --> Ž
undefined                                  &143; --> 

undefined                                  &144; --> 
left single quotation mark                 &145; --> ‘
right single quotation mark                &146; --> ’
left double quotation mark                 &147; --> “
right double quotation mark                &148; --> ”
bullet                                     &149; --> •
en dash                                    &150; --> –
em dash                                    &151; --> —
small tilde                                &152; --> ˜
trade mark sign                            &153; --> ™
latin small letter s with caron            &154; --> š
single right-pointing angle quote mark     &155; --> ›
latin small ligature oe                    &156; --> œ
undefined                                  &157; --> 
latin small letter z with caron            &158; --> ž
latin capital letter y with diaeresis      &159; --> Ÿ

non-breaking space                         &#160; -->      &nbsp;   -->  
inverted exclamation                       &#161; --> ¡    &iexcl;  --> ¡
cent sign                                  &#162; --> ¢    &cent;   --> ¢
pound sterling                             &#163; --> £    &pound;  --> £
general currency sign                      &#164; --> ¤    &curren; --> ¤
yen sign                                   &#165; --> ¥    &yen;    --> ¥
broken vertical bar                        &#166; --> ¦    &brvbar; --> ¦
section sign                               &#167; --> §    &sect;   --> §
umlaut (dieresis)                          &#168; --> ¨    &uml;    --> ¨
copyright                                  &#169; --> ©    &copy;   --> ©
feminine ordinal                           &#170; --> ª    &ordf;   --> ª
left angle quote, guillemotleft            &#171; --> «    &laquo;  --> «
not sign                                   &#172; --> ¬    &not;    --> ¬
soft hyphen                                &#173; --> ­    &shy;    --> ­
registered trademark                       &#174; --> ®    &reg;    --> ®
macron accent                              &#175; --> ¯    &macr;   --> ¯

degree sign                                &#176; --> °    &deg;    --> °
plus or minus                              &#177; --> ±    &plusmn; --> ±
superscript two                            &#178; --> ²    &sup2;   --> ²
superscript three                          &#179; --> ³    &sup3;   --> ³
acute accent                               &#180; --> ´    &acute;  --> ´
micro sign                                 &#181; --> µ    &micro;  --> µ
paragraph sign                             &#182; --> ¶    &para;   --> ¶
middle dot                                 &#183; --> ·    &middot; --> ·
cedilla                                    &#184; --> ¸    &cedil;  --> ¸
superscript one                            &#185; --> ¹    &sup1;   --> ¹
masculine ordinal                          &#186; --> º    &ordm;   --> º
right angle quote, guillemotright          &#187; --> »    &raquo;  --> »
vulgar fraction one-quarter                &#188; --> ¼    &frac14; --> ¼
vulgar fraction one-half                   &#189; --> ½    &frac12; --> ½
vulgar fraction three-fourths              &#190; --> ¾    &frac34; --> ¾
inverted question mark                     &#191; --> ¿    &iquest; --> ¿

latin capital letter a with grave          &#192; --> À    &Agrave; --> À
latin capital letter a with acute          &#193; --> Á    &Aacute; --> Á
latin capital letter a with circumflex     &#194; --> Â    &Acirc;  --> Â
latin capital letter a with tilde          &#195; --> Ã    &Atilde; --> Ã
latin capital letter a with diaeresis      &#196; --> Ä    &Auml;   --> Ä
latin capital letter a with ring above     &#197; --> Å    &Aring;  --> Å
latin capital letter ae                    &#198; --> Æ    &AElig;  --> Æ
latin capital letter c with cedilla        &#199; --> Ç    &Ccedil; --> Ç
latin capital letter e with grave          &#200; --> È    &Egrave; --> È
latin capital letter e with acute          &#201; --> É    &Eacute; --> É
latin capital letter e with circumflex     &#202; --> Ê    &Ecirc;  --> Ê
latin capital letter e with diaeresis      &#203; --> Ë    &Euml;   --> Ë
latin capital letter i with grave          &#204; --> Ì    &Igrave; --> Ì
latin capital letter i with acute          &#205; --> Í    &Iacute; --> Í
latin capital letter i with circumflex     &#206; --> Î    &Icirc;  --> Î
latin capital letter i with diaeresis      &#207; --> Ï    &Iuml;   --> Ï

latin capital letter eth                   &#208; --> Ð    &ETH;    --> Ð
latin capital letter n with tilde          &#209; --> Ñ    &Ntilde; --> Ñ
latin capital letter o with grave          &#210; --> Ò    &Ograve; --> Ò
latin capital letter o with acute          &#211; --> Ó    &Oacute; --> Ó
latin capital letter o with circumflex     &#212; --> Ô    &Ocirc;  --> Ô
latin capital letter o with tilde          &#213; --> Õ    &Otilde; --> Õ
latin capital letter o with diaeresis      &#214; --> Ö    &Ouml;   --> Ö
multiplication sign                        &#215; --> ×    &times;  --> ×
latin capital letter o with stroke         &#216; --> Ø    &Oslash; --> Ø
latin capital letter u with grave          &#217; --> Ù    &Ugrave; --> Ù
latin capital letter u with acute          &#218; --> Ú    &Uacute; --> Ú
latin capital letter u with circumflex     &#219; --> Û    &Ucirc;  --> Û
latin capital letter u with diaeresis      &#220; --> Ü    &Uuml;   --> Ü
latin capital letter y with acute          &#221; --> Ý    &Yacute; --> Ý
latin capital letter thorn                 &#222; --> Þ    &THORN;  --> Þ
latin small letter sharp s                 &#223; --> ß    &szlig;  --> ß

latin small letter a with grave            &#224; --> à    &agrave; --> à
latin small letter a with acute            &#225; --> á    &aacute; --> á
latin small letter a with circumflex       &#226; --> â    &acirc;  --> â
latin small letter a with tilde            &#227; --> ã    &atilde; --> ã
latin small letter a with diaeresis        &#228; --> ä    &auml;   --> ä
latin small letter a with ring above       &#229; --> å    &aring;  --> å
latin small letter ae                      &#230; --> æ    &aelig;  --> æ
latin small letter c with cedilla          &#231; --> ç    &ccedil; --> ç
latin small letter e with grave            &#232; --> è    &egrave; --> è
latin small letter e with acute            &#233; --> é    &eacute; --> é
latin small letter e with circumflex       &#234; --> ê    &ecirc;  --> ê
latin small letter e with diaeresis        &#235; --> ë    &euml;   --> ë
latin small letter i with grave            &#236; --> ì    &igrave; --> ì
latin small letter i with acute            &#237; --> í    &iacute; --> í
latin small letter i with circumflex       &#238; --> î    &icirc;  --> î
latin small letter i with diaeresis        &#239; --> ï    &iuml;   --> ï

latin small letter eth                     &#240; --> ð    &eth;    --> ð
latin small letter n with tilde            &#241; --> ñ    &ntilde; --> ñ
latin small letter o with grave            &#242; --> ò    &ograve; --> ò
latin small letter o with acute            &#243; --> ó    &oacute; --> ó
latin small letter o with circumflex       &#244; --> ô    &ocirc;  --> ô
latin small letter o with tilde            &#245; --> õ    &otilde; --> õ
latin small letter o with diaeresis        &#246; --> ö    &ouml;   --> ö
division sign                              &#247; --> ÷    &divide; --> ÷
latin small letter o with stroke           &#248; --> ø    &oslash; --> ø
latin small letter u with grave            &#249; --> ù    &ugrave; --> ù
latin small letter u with acute            &#250; --> ú    &uacute; --> ú
latin small letter u with circumflex       &#251; --> û    &ucirc;  --> û
latin small letter u with diaeresis        &#252; --> ü    &uuml;   --> ü
latin small letter y with acute            &#253; --> ý    &yacute; --> ý
latin small letter thorn                   &#254; --> þ    &thorn;  --> þ
latin small letter y with diaeresis   {}  {&#255;}-->{ÿ}  {&yuml;}  -->{ÿ}

Some other characters of interest      Char Code            Entity name   
===================================    ==== ============    ==============
capital AE diphthong (ligature)        N/A  &#198; --> Æ    &AElig;  --> Æ
small ae diphthong (ligature)          N/A  &#230; --> æ    &aelig;  --> æ
capital OE ligature                    N/A {&#338;}-->{Œ}  {&OElig;} -->{Œ}
small oe ligature                      N/A {&#339;}-->{œ}  {&oelig;} -->{œ}
copyright                              N/A  &#169; --> ©    &copy;   --> ©
registered trademark                   N/A  &#174; --> ®    &reg;    --> ®
trademark sign                         N/A  &#8482;--> ™   &trade;  --> ™
em space                               N/A [&#8195;]->[ ] [&emsp;]  -->[ ]
en space                               N/A [&#8194;]->[ ] [&ensp;]  -->[ ]
1/3-em space                           N/A [&#8196;]->[ ] [&emsp13;] -->[ ]
1/4-em space                           N/A [&#8197;]->[ ] [&emsp14;] -->[ ]
thin space                             N/A [&#8201;]->[ ] [&thinsp;]-->[ ]
hair space                             N/A [&#8202;]->[ ] [&hairsp;]-->[ ]
em dash                                N/A [&#8212;]->[—] [&mdash;] -->[—]
en dash                                N/A [&#8211;]->[–] [&ndash;] -->[–]

lynx2-8-8/test/square.html000644 023711 023712 00000000373 12264313047 016125 0ustar00dickeylynx000000 000000 Test ImageMap - square

SQUARE

lynx2-8-8/test/iso-8859-1.html000644 023711 023712 00000035173 10164770217 016201 0ustar00dickeylynx000000 000000 Martin Ramsch - iso8859-1 table

iso8859-1 table

Description                               Code            Entity name   
===================================       ============    ==============
quotation mark                            &#34;  --> "    &quot;   --> "
ampersand                                 &#38;  --> &    &amp;    --> &
less-than sign                            &#60;  --> <    &lt;     --> <
greater-than sign                         &#62;  --> >    &gt;     --> >

Description                          Char Code            Entity name   
===================================  ==== ============    ==============
non-breaking space                       &#160; -->      &nbsp;   -->  
inverted exclamation                     &#161; --> ¡    &iexcl;  --> ¡
cent sign                                &#162; --> ¢    &cent;   --> ¢
pound sterling                           &#163; --> £    &pound;  --> £
general currency sign                    &#164; --> ¤    &curren; --> ¤
yen sign                                 &#165; --> ¥    &yen;    --> ¥
broken vertical bar                      &#166; --> ¦    &brvbar; --> ¦
                                             Non-standard &brkbar; --> &brkbar;
section sign                             &#167; --> §    &sect;   --> §
umlaut (dieresis)                        &#168; --> ¨    &uml;    --> ¨
                                             Non-standard &die;    --> ¨
copyright                                &#169; --> ©    &copy;   --> ©
feminine ordinal                         &#170; --> ª    &ordf;   --> ª
left angle quote, guillemotleft          &#171; --> «    &laquo;  --> «
not sign                                 &#172; --> ¬    &not;    --> ¬
soft hyphen                              &#173; --> ­    &shy;    --> ­
registered trademark                     &#174; --> ®    &reg;    --> ®
macron accent                            &#175; --> ¯    &macr;   --> ¯
                                             Non-standard &hibar;  --> &hibar;
degree sign                              &#176; --> °    &deg;    --> °
plus or minus                            &#177; --> ±    &plusmn; --> ±
superscript two                          &#178; --> ²    &sup2;   --> ²
superscript three                        &#179; --> ³    &sup3;   --> ³
acute accent                             &#180; --> ´    &acute;  --> ´
micro sign                               &#181; --> µ    &micro;  --> µ
paragraph sign                           &#182; --> ¶    &para;   --> ¶
middle dot                               &#183; --> ·    &middot; --> ·
cedilla                                  &#184; --> ¸    &cedil;  --> ¸
superscript one                          &#185; --> ¹    &sup1;   --> ¹
masculine ordinal                        &#186; --> º    &ordm;   --> º
right angle quote, guillemotright        &#187; --> »    &raquo;  --> »
fraction one-fourth                      &#188; --> ¼    &frac14; --> ¼
fraction one-half                        &#189; --> ½    &frac12; --> ½
fraction three-fourths                   &#190; --> ¾    &frac34; --> ¾
inverted question mark                   &#191; --> ¿    &iquest; --> ¿
capital A, grave accent                  &#192; --> À    &Agrave; --> À
capital A, acute accent                  &#193; --> Á    &Aacute; --> Á
capital A, circumflex accent             &#194; --> Â    &Acirc;  --> Â
capital A, tilde                         &#195; --> Ã    &Atilde; --> Ã
capital A, dieresis or umlaut mark       &#196; --> Ä    &Auml;   --> Ä
capital A, ring                          &#197; --> Å    &Aring;  --> Å
capital AE diphthong (ligature)          &#198; --> Æ    &AElig;  --> Æ
capital C, cedilla                       &#199; --> Ç    &Ccedil; --> Ç
capital E, grave accent                  &#200; --> È    &Egrave; --> È
capital E, acute accent                  &#201; --> É    &Eacute; --> É
capital E, circumflex accent             &#202; --> Ê    &Ecirc;  --> Ê
capital E, dieresis or umlaut mark       &#203; --> Ë    &Euml;   --> Ë
capital I, grave accent                  &#204; --> Ì    &Igrave; --> Ì
capital I, acute accent                  &#205; --> Í    &Iacute; --> Í
capital I, circumflex accent             &#206; --> Î    &Icirc;  --> Î
capital I, dieresis or umlaut mark       &#207; --> Ï    &Iuml;   --> Ï
capital Eth, Icelandic                   &#208; --> Ð    &ETH;    --> Ð
                                             Non-standard &Dstrok; --> Đ
capital N, tilde                         &#209; --> Ñ    &Ntilde; --> Ñ
capital O, grave accent                  &#210; --> Ò    &Ograve; --> Ò
capital O, acute accent                  &#211; --> Ó    &Oacute; --> Ó
capital O, circumflex accent             &#212; --> Ô    &Ocirc;  --> Ô
capital O, tilde                         &#213; --> Õ    &Otilde; --> Õ
capital O, dieresis or umlaut mark       &#214; --> Ö    &Ouml;   --> Ö
multiply sign                            &#215; --> ×    &times;  --> ×
capital O, slash                         &#216; --> Ø    &Oslash; --> Ø
capital U, grave accent                  &#217; --> Ù    &Ugrave; --> Ù
capital U, acute accent                  &#218; --> Ú    &Uacute; --> Ú
capital U, circumflex accent             &#219; --> Û    &Ucirc;  --> Û
capital U, dieresis or umlaut mark       &#220; --> Ü    &Uuml;   --> Ü
capital Y, acute accent                  &#221; --> Ý    &Yacute; --> Ý
capital THORN, Icelandic                 &#222; --> Þ    &THORN;  --> Þ
small sharp s, German (sz ligature)      &#223; --> ß    &szlig;  --> ß
small a, grave accent                    &#224; --> à    &agrave; --> à
small a, acute accent                    &#225; --> á    &aacute; --> á
small a, circumflex accent               &#226; --> â    &acirc;  --> â
small a, tilde                           &#227; --> ã    &atilde; --> ã
small a, dieresis or umlaut mark         &#228; --> ä    &auml;   --> ä
small a, ring                            &#229; --> å    &aring;  --> å
small ae diphthong (ligature)            &#230; --> æ    &aelig;  --> æ
small c, cedilla                         &#231; --> ç    &ccedil; --> ç
small e, grave accent                    &#232; --> è    &egrave; --> è
small e, acute accent                    &#233; --> é    &eacute; --> é
small e, circumflex accent               &#234; --> ê    &ecirc;  --> ê
small e, dieresis or umlaut mark         &#235; --> ë    &euml;   --> ë
small i, grave accent                    &#236; --> ì    &igrave; --> ì
small i, acute accent                    &#237; --> í    &iacute; --> í
small i, circumflex accent               &#238; --> î    &icirc;  --> î
small i, dieresis or umlaut mark         &#239; --> ï    &iuml;   --> ï
small eth, Icelandic                     &#240; --> ð    &eth;    --> ð
small n, tilde                           &#241; --> ñ    &ntilde; --> ñ
small o, grave accent                    &#242; --> ò    &ograve; --> ò
small o, acute accent                    &#243; --> ó    &oacute; --> ó
small o, circumflex accent               &#244; --> ô    &ocirc;  --> ô
small o, tilde                           &#245; --> õ    &otilde; --> õ
small o, dieresis or umlaut mark         &#246; --> ö    &ouml;   --> ö
division sign                            &#247; --> ÷    &divide; --> ÷
small o, slash                           &#248; --> ø    &oslash; --> ø
small u, grave accent                    &#249; --> ù    &ugrave; --> ù
small u, acute accent                    &#250; --> ú    &uacute; --> ú
small u, circumflex accent               &#251; --> û    &ucirc;  --> û
small u, dieresis or umlaut mark         &#252; --> ü    &uuml;   --> ü
small y, acute accent                    &#253; --> ý    &yacute; --> ý
small thorn, Icelandic                   &#254; --> þ    &thorn;  --> þ
small y, dieresis or umlaut mark         &#255; --> ÿ    &yuml;   --> ÿ

How to read this table. The columns are
1st:
textual description of the character
2nd:
character inserted directly into the HTML page as one byte
3rd:
character written as numeric HTML entity, in the format:
"how it looks literally" --> "what your browser does with it"
4th:
character written as symbolic HTML entity, in the format:
"how it looks literally" --> "what your browser does with it"
So for example, if you see something like "&divide; --> &divide;" in the 4th column, this means your browser doesn't know about the entity name "divide" and just puts it literally.

This table grew out of an overview of the "ISO Latin-1 Character Set" overview related to the Hyper-G Text Format (HTF). The entity names &brkbar; and &Dstrok; seem to be unique to HTF. The entity name &hibar; has been supported by X Mosaic but seems to be replaced with &macr;. The entity names &uml; and &die; should be equivalent.

The standards stuff: The HTML 2.0 Standard includes a section on Character Entity Sets and an overview on the HTML Coded Character Set (The entity names are derived from ISO 8879).
Or have a look at the Latin-1 Character Entities as listed in an draft for the HTML 3.0 specification.
The Appendix II of CERN's HTML+ Discussion Document contains a table (in PostScript format) of the proposed character entities for HTML+ and their corresponding character codes for Unicode and the Adobe Latin-1 & Symbol character sets.

Please note that there is nothing wrong with using characters of ISO Latin-1 above 127: the normal transmission protocol for the WWW, HTTP/1.0, uses the 8bit ISO latin-1 as default encoding. (Thanks to Roman Czyborra for pointing this out!)

Other information:


Martin Ramsch, 16.02.1994, 07.01.1996, 01.07.1996, 1998-10-09, 2000-05-15
lynx2-8-8/test/iso-8859-1a.html000644 023711 023712 00000041253 11206604033 016324 0ustar00dickeylynx000000 000000 Martin Ramsch - iso8859-1 table

iso8859-1 table, with cp-1252

Description                               Code            Entity name   
===================================       ============    ==============
quotation mark                            &#34;  --> "    &quot;   --> "
ampersand                                 &#38;  --> &    &amp;    --> &
less-than sign                            &#60;  --> <    &lt;     --> <
greater-than sign                         &#62;  --> >    &gt;     --> >

Description                          Char Code            Entity name   
===================================  ==== ============    ==============
euro sign                                  &128; --> €
undefined                                  &129; --> 
single low-9 quotation mark                &130; --> ‚
latin small letter f with hook             &131; --> ƒ
double low-9 quotation mark                &132; --> „
horizontal ellipsis                        &133; --> …
dagger                                     &134; --> †
double dagger                              &135; --> ‡
modifier letter circumflex accent          &136; --> ˆ
per mille sign                             &137; --> ‰
latin capital letter s with caron          &138; --> Š
single left-pointing angle quote mark      &139; --> ‹
latin capital ligature oe                  &140; --> Œ
undefined                                  &141; --> 
latin capital letter z with caron          &142; --> Ž
undefined                                  &143; --> 

undefined                                  &144; --> 
left single quotation mark                 &145; --> ‘
right single quotation mark                &146; --> ’
left double quotation mark                 &147; --> “
right double quotation mark                &148; --> ”
bullet                                     &149; --> •
en dash                                    &150; --> –
em dash                                    &151; --> —
small tilde                                &152; --> ˜
trade mark sign                            &153; --> ™
latin small letter s with caron            &154; --> š
single right-pointing angle quote mark     &155; --> ›
latin small ligature oe                    &156; --> œ
undefined                                  &157; --> 
latin small letter z with caron            &158; --> ž
latin capital letter y with diaeresis      &159; --> Ÿ

non-breaking space                       &#160; -->      &nbsp;   -->  
inverted exclamation                     &#161; --> ¡    &iexcl;  --> ¡
cent sign                                &#162; --> ¢    &cent;   --> ¢
pound sterling                           &#163; --> £    &pound;  --> £
general currency sign                    &#164; --> ¤    &curren; --> ¤
yen sign                                 &#165; --> ¥    &yen;    --> ¥
broken vertical bar                      &#166; --> ¦    &brvbar; --> ¦
                                             Non-standard &brkbar; --> &brkbar;
section sign                             &#167; --> §    &sect;   --> §
umlaut (dieresis)                        &#168; --> ¨    &uml;    --> ¨
                                             Non-standard &die;    --> ¨
copyright                                &#169; --> ©    &copy;   --> ©
feminine ordinal                         &#170; --> ª    &ordf;   --> ª
left angle quote, guillemotleft          &#171; --> «    &laquo;  --> «
not sign                                 &#172; --> ¬    &not;    --> ¬
soft hyphen                              &#173; --> ­    &shy;    --> ­
registered trademark                     &#174; --> ®    &reg;    --> ®
macron accent                            &#175; --> ¯    &macr;   --> ¯
                                             Non-standard &hibar;  --> &hibar;
degree sign                              &#176; --> °    &deg;    --> °
plus or minus                            &#177; --> ±    &plusmn; --> ±
superscript two                          &#178; --> ²    &sup2;   --> ²
superscript three                        &#179; --> ³    &sup3;   --> ³
acute accent                             &#180; --> ´    &acute;  --> ´
micro sign                               &#181; --> µ    &micro;  --> µ
paragraph sign                           &#182; --> ¶    &para;   --> ¶
middle dot                               &#183; --> ·    &middot; --> ·
cedilla                                  &#184; --> ¸    &cedil;  --> ¸
superscript one                          &#185; --> ¹    &sup1;   --> ¹
masculine ordinal                        &#186; --> º    &ordm;   --> º
right angle quote, guillemotright        &#187; --> »    &raquo;  --> »
fraction one-fourth                      &#188; --> ¼    &frac14; --> ¼
fraction one-half                        &#189; --> ½    &frac12; --> ½
fraction three-fourths                   &#190; --> ¾    &frac34; --> ¾
inverted question mark                   &#191; --> ¿    &iquest; --> ¿
capital A, grave accent                  &#192; --> À    &Agrave; --> À
capital A, acute accent                  &#193; --> Á    &Aacute; --> Á
capital A, circumflex accent             &#194; --> Â    &Acirc;  --> Â
capital A, tilde                         &#195; --> Ã    &Atilde; --> Ã
capital A, dieresis or umlaut mark       &#196; --> Ä    &Auml;   --> Ä
capital A, ring                          &#197; --> Å    &Aring;  --> Å
capital AE diphthong (ligature)          &#198; --> Æ    &AElig;  --> Æ
capital C, cedilla                       &#199; --> Ç    &Ccedil; --> Ç
capital E, grave accent                  &#200; --> È    &Egrave; --> È
capital E, acute accent                  &#201; --> É    &Eacute; --> É
capital E, circumflex accent             &#202; --> Ê    &Ecirc;  --> Ê
capital E, dieresis or umlaut mark       &#203; --> Ë    &Euml;   --> Ë
capital I, grave accent                  &#204; --> Ì    &Igrave; --> Ì
capital I, acute accent                  &#205; --> Í    &Iacute; --> Í
capital I, circumflex accent             &#206; --> Î    &Icirc;  --> Î
capital I, dieresis or umlaut mark       &#207; --> Ï    &Iuml;   --> Ï
capital Eth, Icelandic                   &#208; --> Ð    &ETH;    --> Ð
                                             Non-standard &Dstrok; --> Đ
capital N, tilde                         &#209; --> Ñ    &Ntilde; --> Ñ
capital O, grave accent                  &#210; --> Ò    &Ograve; --> Ò
capital O, acute accent                  &#211; --> Ó    &Oacute; --> Ó
capital O, circumflex accent             &#212; --> Ô    &Ocirc;  --> Ô
capital O, tilde                         &#213; --> Õ    &Otilde; --> Õ
capital O, dieresis or umlaut mark       &#214; --> Ö    &Ouml;   --> Ö
multiply sign                            &#215; --> ×    &times;  --> ×
capital O, slash                         &#216; --> Ø    &Oslash; --> Ø
capital U, grave accent                  &#217; --> Ù    &Ugrave; --> Ù
capital U, acute accent                  &#218; --> Ú    &Uacute; --> Ú
capital U, circumflex accent             &#219; --> Û    &Ucirc;  --> Û
capital U, dieresis or umlaut mark       &#220; --> Ü    &Uuml;   --> Ü
capital Y, acute accent                  &#221; --> Ý    &Yacute; --> Ý
capital THORN, Icelandic                 &#222; --> Þ    &THORN;  --> Þ
small sharp s, German (sz ligature)      &#223; --> ß    &szlig;  --> ß
small a, grave accent                    &#224; --> à    &agrave; --> à
small a, acute accent                    &#225; --> á    &aacute; --> á
small a, circumflex accent               &#226; --> â    &acirc;  --> â
small a, tilde                           &#227; --> ã    &atilde; --> ã
small a, dieresis or umlaut mark         &#228; --> ä    &auml;   --> ä
small a, ring                            &#229; --> å    &aring;  --> å
small ae diphthong (ligature)            &#230; --> æ    &aelig;  --> æ
small c, cedilla                         &#231; --> ç    &ccedil; --> ç
small e, grave accent                    &#232; --> è    &egrave; --> è
small e, acute accent                    &#233; --> é    &eacute; --> é
small e, circumflex accent               &#234; --> ê    &ecirc;  --> ê
small e, dieresis or umlaut mark         &#235; --> ë    &euml;   --> ë
small i, grave accent                    &#236; --> ì    &igrave; --> ì
small i, acute accent                    &#237; --> í    &iacute; --> í
small i, circumflex accent               &#238; --> î    &icirc;  --> î
small i, dieresis or umlaut mark         &#239; --> ï    &iuml;   --> ï
small eth, Icelandic                     &#240; --> ð    &eth;    --> ð
small n, tilde                           &#241; --> ñ    &ntilde; --> ñ
small o, grave accent                    &#242; --> ò    &ograve; --> ò
small o, acute accent                    &#243; --> ó    &oacute; --> ó
small o, circumflex accent               &#244; --> ô    &ocirc;  --> ô
small o, tilde                           &#245; --> õ    &otilde; --> õ
small o, dieresis or umlaut mark         &#246; --> ö    &ouml;   --> ö
division sign                            &#247; --> ÷    &divide; --> ÷
small o, slash                           &#248; --> ø    &oslash; --> ø
small u, grave accent                    &#249; --> ù    &ugrave; --> ù
small u, acute accent                    &#250; --> ú    &uacute; --> ú
small u, circumflex accent               &#251; --> û    &ucirc;  --> û
small u, dieresis or umlaut mark         &#252; --> ü    &uuml;   --> ü
small y, acute accent                    &#253; --> ý    &yacute; --> ý
small thorn, Icelandic                   &#254; --> þ    &thorn;  --> þ
small y, dieresis or umlaut mark         &#255; --> ÿ    &yuml;   --> ÿ

How to read this table. The columns are
1st:
textual description of the character
2nd:
character inserted directly into the HTML page as one byte
3rd:
character written as numeric HTML entity, in the format:
"how it looks literally" --> "what your browser does with it"
4th:
character written as symbolic HTML entity, in the format:
"how it looks literally" --> "what your browser does with it"
So for example, if you see something like "&divide; --> &divide;" in the 4th column, this means your browser doesn't know about the entity name "divide" and just puts it literally.

This table grew out of an overview of the "ISO Latin-1 Character Set" overview related to the Hyper-G Text Format (HTF). The entity names &brkbar; and &Dstrok; seem to be unique to HTF. The entity name &hibar; has been supported by X Mosaic but seems to be replaced with &macr;. The entity names &uml; and &die; should be equivalent.

The standards stuff: The HTML 2.0 Standard includes a section on Character Entity Sets and an overview on the HTML Coded Character Set (The entity names are derived from ISO 8879).
Or have a look at the Latin-1 Character Entities as listed in an draft for the HTML 3.0 specification.
The Appendix II of CERN's HTML+ Discussion Document contains a table (in PostScript format) of the proposed character entities for HTML+ and their corresponding character codes for Unicode and the Adobe Latin-1 & Symbol character sets.

Please note that there is nothing wrong with using characters of ISO Latin-1 above 127: the normal transmission protocol for the WWW, HTTP/1.0, uses the 8bit ISO latin-1 as default encoding. (Thanks to Roman Czyborra for pointing this out!)

Other information:


Martin Ramsch, 16.02.1994, 07.01.1996, 01.07.1996, 1998-10-09, 2000-05-15
lynx2-8-8/test/iso-8859-2.html000644 023711 023712 00000031363 10164770217 016177 0ustar00dickeylynx000000 000000 Martin Ramsch's character table modified and enhanced for iso8859-2

iso8859-2 plus table

Description                               Code            Entity name   
===================================       ============    ==============
quotation mark                            &#34;  --> "     &quot;   --> "
ampersand                                 &#38;  --> &     &amp;    --> &
less-than sign                            &#60;  --> <     &lt;     --> <
greater-than sign                         &#62;  --> >     &gt;     --> >

Description                          Char Code            Entity name   
===================================  ==== ============    ==============
non-breaking space                       &#160; -->      &nbsp;   -->  
capital A, ogonek                        &#260; --> Ą    &Aogon;  --> Ą
breve                               {}  {&#728;}-->{˘}  {&breve;} -->{˘}
capital L, stroke                        &#321; --> Ł    &Lstrok; --> Ł
general currency sign                    &#164; --> ¤    &curren; --> ¤
capital L, caron                         &#317; --> Ľ    &Lcaron; --> Ľ
capital S, acute accent                  &#346; --> Ś    &Sacute; --> Ś
section sign                             &#167; --> §    &sect;   --> §
umlaut (dieresis)                        &#168; --> ¨    &uml;    --> ¨
                                                          &die;    --> ¨
capital S, caron                         &#352; --> Š    &Scaron; --> Š
capital S, cedilla                       &#350; --> Ş    &Scedil; --> Ş
capital T, caron                         &#356; --> Ť    &Tcaron; --> Ť
capital Z, acute accent                  &#377; --> Ź    &Zacute; --> Ź
soft hyphen                         []  [&#173;]-->[­]  [&shy;]   -->[­]
capital Z, caron                         &#381; --> Ž    &Zcaron; --> Ž
capital Z, dot above                     &#379; --> Ż    &Zdot;   --> Ż
degree sign                              &#176; --> °    &deg;    --> °
small a, ogonek                          &#261; --> ą    &aogon;  --> ą
ogonek                              {}  {&#731;}-->{˛}  {&ogon;}  -->{˛}
small l, stroke                          &#322; --> ł    &lstrok; --> ł
acute accent                             &#180; --> ´    &acute;  --> ´
small l, caron                           &#318; --> ľ    &lcaron; --> ľ
small s, acute accent                    &#347; --> ś    &sacute; --> ś
caron                               {}  {&#711;}-->{ˇ}  {&caron;} -->{ˇ}
cedilla                                  &#184; --> ¸    &cedil;  --> ¸
small s, caron                           &#353; --> š    &scaron; --> š
small s, cedilla                         &#351; --> ş    &scedil; --> ş
small t, caron                           &#357; --> ť    &tcaron; --> ť
small z, acute accent                    &#378; --> ź    &zacute; --> ź
double acute accent                 {}  {&#733;}-->{˝}  {&dblac;} -->{˝}
small z, caron                           &#382; --> ž    &zcaron; --> ž
small z, dot above                       &#380; --> ż    &zdot;   --> ż  
capital R, acute accent                  &#340; --> Ŕ    &Racute; --> Ŕ
capital A, acute accent                  &#193; --> Á    &Aacute; --> Á
capital A, circumflex accent             &#194; --> Â    &Acirc;  --> Â
capital A, breve                         &#258; --> Ă    &Abreve; --> Ă
capital A, dieresis or umlaut mark       &#196; --> Ä    &Auml;   --> Ä
capital L, acute accent                  &#313; --> Ĺ    &Lacute; --> Ĺ
capital C, acute accent                  &#262; --> Ć    &Cacute; --> Ć
capital C, cedilla                       &#199; --> Ç    &Ccedil; --> Ç
capital C, caron                         &#268; --> Č    &Ccaron; --> Č
capital E, acute accent                  &#201; --> É    &Eacute; --> É
capital E, ogonek                        &#280; --> Ę    &Eogon;  --> Ę
capital E, dieresis or umlaut mark       &#203; --> Ë    &Euml;   --> Ë
capital E, caron                         &#282; --> Ě    &Ecaron; --> Ě
capital I, acute accent                  &#205; --> Í    &Iacute; --> Í
capital I, circumflex accent             &#206; --> Î    &Icirc;  --> Î
capital D, caron                         &#270; --> Ď    &Dcaron; --> Ď
capital D, stroke                        &#272; --> Đ    &Dstrok; --> Đ
capital Eth, Icelandic               N/A  &#208; --> Ð    &ETH;    --> Ð
capital N, acute accent                  &#323; --> Ń    &Nacute; --> Ń
capital N, caron                         &#327; --> Ň    &Ncaron; --> Ň
capital O, acute accent                  &#211; --> Ó    &Oacute; --> Ó
capital O, circumflex accent             &#212; --> Ô    &Ocirc;  --> Ô
capital O, double acute accent           &#368; --> Ű    &Odblac; --> Ő
capital O, dieresis or umlaut mark       &#214; --> Ö    &Ouml;   --> Ö
multiply sign                            &#215; --> ×    &times;  --> ×
capital R, caron                         &#344; --> Ř    &Rcaron; --> Ř
capital U, ring                          &#366; --> Ů    &Uring;  --> Ů
capital U, acute accent                  &#218; --> Ú    &Uacute; --> Ú
capital U, double acute accent           &#368; --> Ű    &Udblac; --> Ű
capital U, dieresis or umlaut mark       &#220; --> Ü    &Uuml;   --> Ü
capital Y, acute accent                  &#221; --> Ý    &Yacute; --> Ý
capital T, cedilla                       &#354; --> Ţ    &Tcedil; --> Ţ
small sharp s, German (sz ligature)      &#223; --> ß    &szlig;  --> ß
small r, acute accent                    &#341; --> ŕ    &racute; --> ŕ
small a, acute accent                    &#225; --> á    &aacute; --> á
small a, circumflex accent               &#226; --> â    &acirc;  --> â
small a, breve                           &#259; --> ă    &abreve; --> ă
small a, dieresis or umlaut mark         &#228; --> ä    &auml;   --> ä
small l, acute accent                    &#314; --> ĺ    &lacute; --> ĺ
small c, acute accent                    &#263; --> ć    &cacute; --> ć
small c, cedilla                         &#231; --> ç    &ccedil; --> ç
small c, caron                           &#269; --> č    &ccaron; --> č
small e, acute accent                    &#233; --> é    &eacute; --> é
small e, ogonek                          &#281; --> ę    &eogon;  --> ę
small e, dieresis or umlaut mark         &#235; --> ë    &euml;   --> ë
small e, caron                           &#283; --> ě    &ecaron; --> ě
small i, acute accent                    &#237; --> í    &iacute; --> í
small i, circumflex accent               &#238; --> î    &icirc;  --> î
small d, caron                           &#271; --> ď    &dcaron; --> ď
small d, stroke                          &#273; --> đ    &dstrok; --> đ
small eth, Icelandic                 N/A  &#240; --> ð    &eth;    --> ð
small n, acute accent                    &#324; --> ń    &nacute; --> ń
small n, caron                           &#328; --> ň    &ncaron; --> ň
small o, acute accent                    &#243; --> ó    &oacute; --> ó
small o, circumflex accent               &#244; --> ô    &ocirc;  --> ô
small o, double acute accent             &#369; --> ű    &odblac; --> ő
small o, dieresis or umlaut mark         &#246; --> ö    &ouml;   --> ö
division sign                            &#247; --> ÷    &divide; --> ÷
small r, caron                           &#345; --> ř    &rcaron; --> ř
small u, ring                            &#367; --> ů    &uring;  --> ů
small u, acute accent                    &#250; --> ú    &uacute; --> ú
small u, double acute accent             &#369; --> ű    &udblac; --> ű
small u, dieresis or umlaut mark         &#252; --> ü    &uuml;   --> ü
small y, acute accent                    &#253; --> ý    &yacute; --> ý
small t, cedilla                         &#355; --> ţ    &tcedil; --> ţ
dot above                           {}  {&#729;}-->{˙}  {&dot;}   -->{˙}

Some other characters of interest    Char Code            Entity name   
===================================  ==== ============    ==============
capital AE diphthong (ligature)      N/A  &#198; --> Æ    &AElig;  --> Æ
small ae diphthong (ligature)        N/A  &#230; --> æ    &aelig;  --> æ
capital OE ligature                  N/A {&#338;}-->{Œ}  {&OElig;} -->{Œ}
small oe ligature                    N/A {&#339;}-->{œ}  {&oelig;} -->{œ}
copyright                            N/A  &#169; --> ©    &copy;   --> ©
registered trademark                 N/A  &#174; --> ®    &reg;    --> ®
trademark sign                       N/A  &#8482;--> ™   &trade;  --> ™
em space                             N/A [&#8195;]->[ ] [&emsp;]  -->[ ]
en space                             N/A [&#8194;]->[ ] [&ensp;]  -->[ ]
1/3-em space                         N/A [&#8196;]->[ ] [&emsp13;] -->[ ]
1/4-em space                         N/A [&#8197;]->[ ] [&emsp14;] -->[ ]
thin space                           N/A [&#8201;]->[ ] [&thinsp;]-->[ ]
hair space                           N/A [&#8202;]->[ ] [&hairsp;]-->[ ]
em dash                              N/A [&#8212;]->[—] [&mdash;] -->[—]
en dash                              N/A [&#8211;]->[–] [&ndash;] -->[–]


Characters not found in ISO-8859-2 have "N/A" in the Char column. Some characters for which I could not find entity names in either RFC 2070 or the ISOlat1, ISOlat2, ISOnum, ISOpub and ISOtech sets (the ones included by Peter Flynn's HTML Pro DTD) are shown enclosed in {braces}.

There also is a variation of this table which tests ISO-8859-2 characters and entities in ALT attributes.

See Martin Ramsch's original ISO-8859-1 Table for related info and links, and for some notes on entity names. This file is mostly just an adaptation of his table to the ISO-8859-2 character set.


kweide@tezcat.com 1997-03-09
lynx2-8-8/test/iso-8859-2a.html000644 023711 023712 00000035442 11206604033 016330 0ustar00dickeylynx000000 000000 Martin Ramsch's character table modified and enhanced for iso8859-2

iso8859-2 plus table, and cp-1252

Description                               Code            Entity name   
===================================       ============    ==============
quotation mark                            &#34;  --> "     &quot;   --> "
ampersand                                 &#38;  --> &     &amp;    --> &
less-than sign                            &#60;  --> <     &lt;     --> <
greater-than sign                         &#62;  --> >     &gt;     --> >

Description                          Char Code            Entity name   
===================================  ==== ============    ==============
euro sign                                  &128; --> €
undefined                                  &129; --> 
single low-9 quotation mark                &130; --> ‚
latin small letter f with hook             &131; --> ƒ
double low-9 quotation mark                &132; --> „
horizontal ellipsis                        &133; --> …
dagger                                     &134; --> †
double dagger                              &135; --> ‡
modifier letter circumflex accent          &136; --> ˆ
per mille sign                             &137; --> ‰
latin capital letter s with caron          &138; --> Š
single left-pointing angle quote mark      &139; --> ‹
latin capital ligature oe                  &140; --> Œ
undefined                                  &141; --> 
latin capital letter z with caron          &142; --> Ž
undefined                                  &143; --> 

undefined                                  &144; --> 
left single quotation mark                 &145; --> ‘
right single quotation mark                &146; --> ’
left double quotation mark                 &147; --> “
right double quotation mark                &148; --> ”
bullet                                     &149; --> •
en dash                                    &150; --> –
em dash                                    &151; --> —
small tilde                                &152; --> ˜
trade mark sign                            &153; --> ™
latin small letter s with caron            &154; --> š
single right-pointing angle quote mark     &155; --> ›
latin small ligature oe                    &156; --> œ
undefined                                  &157; --> 
latin small letter z with caron            &158; --> ž
latin capital letter y with diaeresis      &159; --> Ÿ

non-breaking space                       &#160; -->      &nbsp;   -->  
capital A, ogonek                        &#260; --> Ą    &Aogon;  --> Ą
breve                               {}  {&#728;}-->{˘}  {&breve;} -->{˘}
capital L, stroke                        &#321; --> Ł    &Lstrok; --> Ł
general currency sign                    &#164; --> ¤    &curren; --> ¤
capital L, caron                         &#317; --> Ľ    &Lcaron; --> Ľ
capital S, acute accent                  &#346; --> Ś    &Sacute; --> Ś
section sign                             &#167; --> §    &sect;   --> §
umlaut (dieresis)                        &#168; --> ¨    &uml;    --> ¨
                                                          &die;    --> ¨
capital S, caron                         &#352; --> Š    &Scaron; --> Š
capital S, cedilla                       &#350; --> Ş    &Scedil; --> Ş
capital T, caron                         &#356; --> Ť    &Tcaron; --> Ť
capital Z, acute accent                  &#377; --> Ź    &Zacute; --> Ź
soft hyphen                         []  [&#173;]-->[­]  [&shy;]   -->[­]
capital Z, caron                         &#381; --> Ž    &Zcaron; --> Ž
capital Z, dot above                     &#379; --> Ż    &Zdot;   --> Ż
degree sign                              &#176; --> °    &deg;    --> °
small a, ogonek                          &#261; --> ą    &aogon;  --> ą
ogonek                              {}  {&#731;}-->{˛}  {&ogon;}  -->{˛}
small l, stroke                          &#322; --> ł    &lstrok; --> ł
acute accent                             &#180; --> ´    &acute;  --> ´
small l, caron                           &#318; --> ľ    &lcaron; --> ľ
small s, acute accent                    &#347; --> ś    &sacute; --> ś
caron                               {}  {&#711;}-->{ˇ}  {&caron;} -->{ˇ}
cedilla                                  &#184; --> ¸    &cedil;  --> ¸
small s, caron                           &#353; --> š    &scaron; --> š
small s, cedilla                         &#351; --> ş    &scedil; --> ş
small t, caron                           &#357; --> ť    &tcaron; --> ť
small z, acute accent                    &#378; --> ź    &zacute; --> ź
double acute accent                 {}  {&#733;}-->{˝}  {&dblac;} -->{˝}
small z, caron                           &#382; --> ž    &zcaron; --> ž
small z, dot above                       &#380; --> ż    &zdot;   --> ż  
capital R, acute accent                  &#340; --> Ŕ    &Racute; --> Ŕ
capital A, acute accent                  &#193; --> Á    &Aacute; --> Á
capital A, circumflex accent             &#194; --> Â    &Acirc;  --> Â
capital A, breve                         &#258; --> Ă    &Abreve; --> Ă
capital A, dieresis or umlaut mark       &#196; --> Ä    &Auml;   --> Ä
capital L, acute accent                  &#313; --> Ĺ    &Lacute; --> Ĺ
capital C, acute accent                  &#262; --> Ć    &Cacute; --> Ć
capital C, cedilla                       &#199; --> Ç    &Ccedil; --> Ç
capital C, caron                         &#268; --> Č    &Ccaron; --> Č
capital E, acute accent                  &#201; --> É    &Eacute; --> É
capital E, ogonek                        &#280; --> Ę    &Eogon;  --> Ę
capital E, dieresis or umlaut mark       &#203; --> Ë    &Euml;   --> Ë
capital E, caron                         &#282; --> Ě    &Ecaron; --> Ě
capital I, acute accent                  &#205; --> Í    &Iacute; --> Í
capital I, circumflex accent             &#206; --> Î    &Icirc;  --> Î
capital D, caron                         &#270; --> Ď    &Dcaron; --> Ď
capital D, stroke                        &#272; --> Đ    &Dstrok; --> Đ
capital Eth, Icelandic               N/A  &#208; --> Ð    &ETH;    --> Ð
capital N, acute accent                  &#323; --> Ń    &Nacute; --> Ń
capital N, caron                         &#327; --> Ň    &Ncaron; --> Ň
capital O, acute accent                  &#211; --> Ó    &Oacute; --> Ó
capital O, circumflex accent             &#212; --> Ô    &Ocirc;  --> Ô
capital O, double acute accent           &#368; --> Ű    &Odblac; --> Ő
capital O, dieresis or umlaut mark       &#214; --> Ö    &Ouml;   --> Ö
multiply sign                            &#215; --> ×    &times;  --> ×
capital R, caron                         &#344; --> Ř    &Rcaron; --> Ř
capital U, ring                          &#366; --> Ů    &Uring;  --> Ů
capital U, acute accent                  &#218; --> Ú    &Uacute; --> Ú
capital U, double acute accent           &#368; --> Ű    &Udblac; --> Ű
capital U, dieresis or umlaut mark       &#220; --> Ü    &Uuml;   --> Ü
capital Y, acute accent                  &#221; --> Ý    &Yacute; --> Ý
capital T, cedilla                       &#354; --> Ţ    &Tcedil; --> Ţ
small sharp s, German (sz ligature)      &#223; --> ß    &szlig;  --> ß
small r, acute accent                    &#341; --> ŕ    &racute; --> ŕ
small a, acute accent                    &#225; --> á    &aacute; --> á
small a, circumflex accent               &#226; --> â    &acirc;  --> â
small a, breve                           &#259; --> ă    &abreve; --> ă
small a, dieresis or umlaut mark         &#228; --> ä    &auml;   --> ä
small l, acute accent                    &#314; --> ĺ    &lacute; --> ĺ
small c, acute accent                    &#263; --> ć    &cacute; --> ć
small c, cedilla                         &#231; --> ç    &ccedil; --> ç
small c, caron                           &#269; --> č    &ccaron; --> č
small e, acute accent                    &#233; --> é    &eacute; --> é
small e, ogonek                          &#281; --> ę    &eogon;  --> ę
small e, dieresis or umlaut mark         &#235; --> ë    &euml;   --> ë
small e, caron                           &#283; --> ě    &ecaron; --> ě
small i, acute accent                    &#237; --> í    &iacute; --> í
small i, circumflex accent               &#238; --> î    &icirc;  --> î
small d, caron                           &#271; --> ď    &dcaron; --> ď
small d, stroke                          &#273; --> đ    &dstrok; --> đ
small eth, Icelandic                 N/A  &#240; --> ð    &eth;    --> ð
small n, acute accent                    &#324; --> ń    &nacute; --> ń
small n, caron                           &#328; --> ň    &ncaron; --> ň
small o, acute accent                    &#243; --> ó    &oacute; --> ó
small o, circumflex accent               &#244; --> ô    &ocirc;  --> ô
small o, double acute accent             &#369; --> ű    &odblac; --> ő
small o, dieresis or umlaut mark         &#246; --> ö    &ouml;   --> ö
division sign                            &#247; --> ÷    &divide; --> ÷
small r, caron                           &#345; --> ř    &rcaron; --> ř
small u, ring                            &#367; --> ů    &uring;  --> ů
small u, acute accent                    &#250; --> ú    &uacute; --> ú
small u, double acute accent             &#369; --> ű    &udblac; --> ű
small u, dieresis or umlaut mark         &#252; --> ü    &uuml;   --> ü
small y, acute accent                    &#253; --> ý    &yacute; --> ý
small t, cedilla                         &#355; --> ţ    &tcedil; --> ţ
dot above                           {}  {&#729;}-->{˙}  {&dot;}   -->{˙}

Some other characters of interest    Char Code            Entity name   
===================================  ==== ============    ==============
capital AE diphthong (ligature)      N/A  &#198; --> Æ    &AElig;  --> Æ
small ae diphthong (ligature)        N/A  &#230; --> æ    &aelig;  --> æ
capital OE ligature                  N/A {&#338;}-->{Œ}  {&OElig;} -->{Œ}
small oe ligature                    N/A {&#339;}-->{œ}  {&oelig;} -->{œ}
copyright                            N/A  &#169; --> ©    &copy;   --> ©
registered trademark                 N/A  &#174; --> ®    &reg;    --> ®
trademark sign                       N/A  &#8482;--> ™   &trade;  --> ™
em space                             N/A [&#8195;]->[ ] [&emsp;]  -->[ ]
en space                             N/A [&#8194;]->[ ] [&ensp;]  -->[ ]
1/3-em space                         N/A [&#8196;]->[ ] [&emsp13;] -->[ ]
1/4-em space                         N/A [&#8197;]->[ ] [&emsp14;] -->[ ]
thin space                           N/A [&#8201;]->[ ] [&thinsp;]-->[ ]
hair space                           N/A [&#8202;]->[ ] [&hairsp;]-->[ ]
em dash                              N/A [&#8212;]->[—] [&mdash;] -->[—]
en dash                              N/A [&#8211;]->[–] [&ndash;] -->[–]


Characters not found in ISO-8859-2 have "N/A" in the Char column. Some characters for which I could not find entity names in either RFC 2070 or the ISOlat1, ISOlat2, ISOnum, ISOpub and ISOtech sets (the ones included by Peter Flynn's HTML Pro DTD) are shown enclosed in {braces}.

There also is a variation of this table which tests ISO-8859-2 characters and entities in ALT attributes.

See Martin Ramsch's original ISO-8859-1 Table for related info and links, and for some notes on entity names. This file is mostly just an adaptation of his table to the ISO-8859-2 character set.


kweide@tezcat.com 1997-03-09
lynx2-8-8/test/koi8-r.html000644 023711 023712 00000036464 07550154260 015752 0ustar00dickeylynx000000 000000 Test of the KOI8-R symbols

    This table prepared from KOI8-R.TXT available at ftp.unicode.org

         ftp://ftp.unicode.org/MAPPINGS/VENDORS/MISC/KOI8-R.TXT
         (if doing ftp, try cd Public/MAPPINGS/VENDORS/MISC)


original comment:

#
#       Name:             KOI8-R (RFC1489) to Unicode
#       Unicode version:  3.0
#       Table version:    1.0
#       Table format:     Format A
#       Date:             18 August 1999
#       Authors:          Helmut Richter <richter@lrz.de>
#
#       Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
#
#       This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
#       No claims are made as to fitness for any particular purpose.  No
#       warranties of any kind are expressed or implied.  The recipient
#       agrees to determine applicability of information provided.  If this
#       file has been provided on optical media by Unicode, Inc., the sole
#       remedy for any claim will be exchange of defective media within 90
#       days of receipt.
#
#       Unicode, Inc. hereby grants the right to freely use the information
#       supplied in this file in the creation of products supporting the
#       Unicode Standard, and to make copies of this file in any form for
#       internal or external distribution as long as this notice remains
#       attached.
#
#       General notes:
#
#       This table contains the data the Unicode Consortium has on how
#       KOI8-R characters map into Unicode. The underlying document is the
#       mapping described in RFC 1489. No statements are made as to whether
#       this mapping is the same as the mapping defined as "Code Page 878"
#       with some vendors.
#
#       Format:  Three tab-separated columns
#                Column #1 is the KOI8-R code (in hex as 0xXX)
#                Column #2 is the Unicode (in hex as 0xXXXX)
#                Column #3 the Unicode name (follows a comment sign, '#')
#
#       The entries are in KOI8-R order.
#
#       Version history
#       1.0 version: created.
#
#       Any comments or problems, contact <errata@unicode.org>
#       Please note that <errata@unicode.org> is an archival address;
#       notices will be checked, but do not expect an immediate response.
#
0x00    0x0000 "�"	  # NULL
0x01    0x0001 ""	  # START OF HEADING
0x02    0x0002 ""	  # START OF TEXT
0x03    0x0003 ""	  # END OF TEXT
0x04    0x0004 ""	  # END OF TRANSMISSION
0x05    0x0005 ""	  # ENQUIRY
0x06    0x0006 ""	  # ACKNOWLEDGE
0x07    0x0007 ""	  # BELL
0x08    0x0008 ""	  # BACKSPACE
0x09    0x0009 "	"	  # HORIZONTAL TABULATION
0x0A    0x000A "
"	  # LINE FEED
0x0B    0x000B ""	  # VERTICAL TABULATION
0x0C    0x000C ""	  # FORM FEED
0x0D    0x000D "
"	  # CARRIAGE RETURN
0x0E    0x000E ""	  # SHIFT OUT
0x0F    0x000F ""	  # SHIFT IN
0x10    0x0010 ""	  # DATA LINK ESCAPE
0x11    0x0011 ""	  # DEVICE CONTROL ONE
0x12    0x0012 ""	  # DEVICE CONTROL TWO
0x13    0x0013 ""	  # DEVICE CONTROL THREE
0x14    0x0014 ""	  # DEVICE CONTROL FOUR
0x15    0x0015 ""	  # NEGATIVE ACKNOWLEDGE
0x16    0x0016 ""	  # SYNCHRONOUS IDLE
0x17    0x0017 ""	  # END OF TRANSMISSION BLOCK
0x18    0x0018 ""	  # CANCEL
0x19    0x0019 ""	  # END OF MEDIUM
0x1A    0x001A ""	  # SUBSTITUTE
0x1B    0x001B ""	  # ESCAPE
0x1C    0x001C ""	  # FILE SEPARATOR
0x1D    0x001D ""	  # GROUP SEPARATOR
0x1E    0x001E ""	  # RECORD SEPARATOR
0x1F    0x001F ""	  # UNIT SEPARATOR
0x20    0x0020 " "	  # SPACE
0x21    0x0021 "!"	  # EXCLAMATION MARK
0x22    0x0022 """	  # QUOTATION MARK
0x23    0x0023 "#"	  # NUMBER SIGN
0x24    0x0024 "$"	  # DOLLAR SIGN
0x25    0x0025 "%"	  # PERCENT SIGN
0x26    0x0026 "&"	  # AMPERSAND
0x27    0x0027 "'"	  # APOSTROPHE
0x28    0x0028 "("	  # LEFT PARENTHESIS
0x29    0x0029 ")"	  # RIGHT PARENTHESIS
0x2A    0x002A "*"	  # ASTERISK
0x2B    0x002B "+"	  # PLUS SIGN
0x2C    0x002C ","	  # COMMA
0x2D    0x002D "-"	  # HYPHEN-MINUS
0x2E    0x002E "."	  # FULL STOP
0x2F    0x002F "/"	  # SOLIDUS
0x30    0x0030 "0"	  # DIGIT ZERO
0x31    0x0031 "1"	  # DIGIT ONE
0x32    0x0032 "2"	  # DIGIT TWO
0x33    0x0033 "3"	  # DIGIT THREE
0x34    0x0034 "4"	  # DIGIT FOUR
0x35    0x0035 "5"	  # DIGIT FIVE
0x36    0x0036 "6"	  # DIGIT SIX
0x37    0x0037 "7"	  # DIGIT SEVEN
0x38    0x0038 "8"	  # DIGIT EIGHT
0x39    0x0039 "9"	  # DIGIT NINE
0x3A    0x003A ":"	  # COLON
0x3B    0x003B ";"	  # SEMICOLON
0x3C    0x003C "<"	  # LESS-THAN SIGN
0x3D    0x003D "="	  # EQUALS SIGN
0x3E    0x003E ">"	  # GREATER-THAN SIGN
0x3F    0x003F "?"	  # QUESTION MARK
0x40    0x0040 "@"	  # COMMERCIAL AT
0x41    0x0041 "A"	  # LATIN CAPITAL LETTER A
0x42    0x0042 "B"	  # LATIN CAPITAL LETTER B
0x43    0x0043 "C"	  # LATIN CAPITAL LETTER C
0x44    0x0044 "D"	  # LATIN CAPITAL LETTER D
0x45    0x0045 "E"	  # LATIN CAPITAL LETTER E
0x46    0x0046 "F"	  # LATIN CAPITAL LETTER F
0x47    0x0047 "G"	  # LATIN CAPITAL LETTER G
0x48    0x0048 "H"	  # LATIN CAPITAL LETTER H
0x49    0x0049 "I"	  # LATIN CAPITAL LETTER I
0x4A    0x004A "J"	  # LATIN CAPITAL LETTER J
0x4B    0x004B "K"	  # LATIN CAPITAL LETTER K
0x4C    0x004C "L"	  # LATIN CAPITAL LETTER L
0x4D    0x004D "M"	  # LATIN CAPITAL LETTER M
0x4E    0x004E "N"	  # LATIN CAPITAL LETTER N
0x4F    0x004F "O"	  # LATIN CAPITAL LETTER O
0x50    0x0050 "P"	  # LATIN CAPITAL LETTER P
0x51    0x0051 "Q"	  # LATIN CAPITAL LETTER Q
0x52    0x0052 "R"	  # LATIN CAPITAL LETTER R
0x53    0x0053 "S"	  # LATIN CAPITAL LETTER S
0x54    0x0054 "T"	  # LATIN CAPITAL LETTER T
0x55    0x0055 "U"	  # LATIN CAPITAL LETTER U
0x56    0x0056 "V"	  # LATIN CAPITAL LETTER V
0x57    0x0057 "W"	  # LATIN CAPITAL LETTER W
0x58    0x0058 "X"	  # LATIN CAPITAL LETTER X
0x59    0x0059 "Y"	  # LATIN CAPITAL LETTER Y
0x5A    0x005A "Z"	  # LATIN CAPITAL LETTER Z
0x5B    0x005B "["	  # LEFT SQUARE BRACKET
0x5C    0x005C "\"	  # REVERSE SOLIDUS
0x5D    0x005D "]"	  # RIGHT SQUARE BRACKET
0x5E    0x005E "^"	  # CIRCUMFLEX ACCENT
0x5F    0x005F "_"	  # LOW LINE
0x60    0x0060 "`"	  # GRAVE ACCENT
0x61    0x0061 "a"	  # LATIN SMALL LETTER A
0x62    0x0062 "b"	  # LATIN SMALL LETTER B
0x63    0x0063 "c"	  # LATIN SMALL LETTER C
0x64    0x0064 "d"	  # LATIN SMALL LETTER D
0x65    0x0065 "e"	  # LATIN SMALL LETTER E
0x66    0x0066 "f"	  # LATIN SMALL LETTER F
0x67    0x0067 "g"	  # LATIN SMALL LETTER G
0x68    0x0068 "h"	  # LATIN SMALL LETTER H
0x69    0x0069 "i"	  # LATIN SMALL LETTER I
0x6A    0x006A "j"	  # LATIN SMALL LETTER J
0x6B    0x006B "k"	  # LATIN SMALL LETTER K
0x6C    0x006C "l"	  # LATIN SMALL LETTER L
0x6D    0x006D "m"	  # LATIN SMALL LETTER M
0x6E    0x006E "n"	  # LATIN SMALL LETTER N
0x6F    0x006F "o"	  # LATIN SMALL LETTER O
0x70    0x0070 "p"	  # LATIN SMALL LETTER P
0x71    0x0071 "q"	  # LATIN SMALL LETTER Q
0x72    0x0072 "r"	  # LATIN SMALL LETTER R
0x73    0x0073 "s"	  # LATIN SMALL LETTER S
0x74    0x0074 "t"	  # LATIN SMALL LETTER T
0x75    0x0075 "u"	  # LATIN SMALL LETTER U
0x76    0x0076 "v"	  # LATIN SMALL LETTER V
0x77    0x0077 "w"	  # LATIN SMALL LETTER W
0x78    0x0078 "x"	  # LATIN SMALL LETTER X
0x79    0x0079 "y"	  # LATIN SMALL LETTER Y
0x7A    0x007A "z"	  # LATIN SMALL LETTER Z
0x7B    0x007B "{"	  # LEFT CURLY BRACKET
0x7C    0x007C "|"	  # VERTICAL LINE
0x7D    0x007D "}"	  # RIGHT CURLY BRACKET
0x7E    0x007E "~"	  # TILDE
0x7F    0x007F ""	  # DELETE
0x80    0x2500 "─"	  # BOX DRAWINGS LIGHT HORIZONTAL
0x81    0x2502 "│"	  # BOX DRAWINGS LIGHT VERTICAL
0x82    0x250C "┌"	  # BOX DRAWINGS LIGHT DOWN AND RIGHT
0x83    0x2510 "┐"	  # BOX DRAWINGS LIGHT DOWN AND LEFT
0x84    0x2514 "└"	  # BOX DRAWINGS LIGHT UP AND RIGHT
0x85    0x2518 "┘"	  # BOX DRAWINGS LIGHT UP AND LEFT
0x86    0x251C "├"	  # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
0x87    0x2524 "┤"	  # BOX DRAWINGS LIGHT VERTICAL AND LEFT
0x88    0x252C "┬"	  # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
0x89    0x2534 "┴"	  # BOX DRAWINGS LIGHT UP AND HORIZONTAL
0x8A    0x253C "┼"	  # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
0x8B    0x2580 "▀"	  # UPPER HALF BLOCK
0x8C    0x2584 "▄"	  # LOWER HALF BLOCK
0x8D    0x2588 "█"	  # FULL BLOCK
0x8E    0x258C "▌"	  # LEFT HALF BLOCK
0x8F    0x2590 "▐"	  # RIGHT HALF BLOCK
0x90    0x2591 "░"	  # LIGHT SHADE
0x91    0x2592 "▒"	  # MEDIUM SHADE
0x92    0x2593 "▓"	  # DARK SHADE
0x93    0x2320 "⌠"	  # TOP HALF INTEGRAL
0x94    0x25A0 "■"	  # BLACK SQUARE
0x95    0x2219 "∙"	  # BULLET OPERATOR
0x96    0x221A "√"	  # SQUARE ROOT
0x97    0x2248 "≈"	  # ALMOST EQUAL TO
0x98    0x2264 "≤"	  # LESS-THAN OR EQUAL TO
0x99    0x2265 "≥"	  # GREATER-THAN OR EQUAL TO
0x9A    0x00A0 " "	  # NO-BREAK SPACE
0x9B    0x2321 "⌡"	  # BOTTOM HALF INTEGRAL
0x9C    0x00B0 "°"	  # DEGREE SIGN
0x9D    0x00B2 "²"	  # SUPERSCRIPT TWO
0x9E    0x00B7 "·"	  # MIDDLE DOT
0x9F    0x00F7 "÷"	  # DIVISION SIGN
0xA0    0x2550 "═"	  # BOX DRAWINGS DOUBLE HORIZONTAL
0xA1    0x2551 "║"	  # BOX DRAWINGS DOUBLE VERTICAL
0xA2    0x2552 "╒"	  # BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
0xA3    0x0451 "ё"	  # CYRILLIC SMALL LETTER IO
0xA4    0x2553 "╓"	  # BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
0xA5    0x2554 "╔"	  # BOX DRAWINGS DOUBLE DOWN AND RIGHT
0xA6    0x2555 "╕"	  # BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
0xA7    0x2556 "╖"	  # BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
0xA8    0x2557 "╗"	  # BOX DRAWINGS DOUBLE DOWN AND LEFT
0xA9    0x2558 "╘"	  # BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
0xAA    0x2559 "╙"	  # BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
0xAB    0x255A "╚"	  # BOX DRAWINGS DOUBLE UP AND RIGHT
0xAC    0x255B "╛"	  # BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
0xAD    0x255C "╜"	  # BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
0xAE    0x255D "╝"	  # BOX DRAWINGS DOUBLE UP AND LEFT
0xAF    0x255E "╞"	  # BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
0xB0    0x255F "╟"	  # BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
0xB1    0x2560 "╠"	  # BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
0xB2    0x2561 "╡"	  # BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
0xB3    0x0401 "Ё"	  # CYRILLIC CAPITAL LETTER IO
0xB4    0x2562 "╢"	  # BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
0xB5    0x2563 "╣"	  # BOX DRAWINGS DOUBLE VERTICAL AND LEFT
0xB6    0x2564 "╤"	  # BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
0xB7    0x2565 "╥"	  # BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
0xB8    0x2566 "╦"	  # BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
0xB9    0x2567 "╧"	  # BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
0xBA    0x2568 "╨"	  # BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
0xBB    0x2569 "╩"	  # BOX DRAWINGS DOUBLE UP AND HORIZONTAL
0xBC    0x256A "╪"	  # BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
0xBD    0x256B "╫"	  # BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
0xBE    0x256C "╬"	  # BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
0xBF    0x00A9 "©"	  # COPYRIGHT SIGN
0xC0    0x044E "ю"	  # CYRILLIC SMALL LETTER YU
0xC1    0x0430 "а"	  # CYRILLIC SMALL LETTER A
0xC2    0x0431 "б"	  # CYRILLIC SMALL LETTER BE
0xC3    0x0446 "ц"	  # CYRILLIC SMALL LETTER TSE
0xC4    0x0434 "д"	  # CYRILLIC SMALL LETTER DE
0xC5    0x0435 "е"	  # CYRILLIC SMALL LETTER IE
0xC6    0x0444 "ф"	  # CYRILLIC SMALL LETTER EF
0xC7    0x0433 "г"	  # CYRILLIC SMALL LETTER GHE
0xC8    0x0445 "х"	  # CYRILLIC SMALL LETTER HA
0xC9    0x0438 "и"	  # CYRILLIC SMALL LETTER I
0xCA    0x0439 "й"	  # CYRILLIC SMALL LETTER SHORT I
0xCB    0x043A "к"	  # CYRILLIC SMALL LETTER KA
0xCC    0x043B "л"	  # CYRILLIC SMALL LETTER EL
0xCD    0x043C "м"	  # CYRILLIC SMALL LETTER EM
0xCE    0x043D "н"	  # CYRILLIC SMALL LETTER EN
0xCF    0x043E "о"	  # CYRILLIC SMALL LETTER O
0xD0    0x043F "п"	  # CYRILLIC SMALL LETTER PE
0xD1    0x044F "я"	  # CYRILLIC SMALL LETTER YA
0xD2    0x0440 "р"	  # CYRILLIC SMALL LETTER ER
0xD3    0x0441 "с"	  # CYRILLIC SMALL LETTER ES
0xD4    0x0442 "т"	  # CYRILLIC SMALL LETTER TE
0xD5    0x0443 "у"	  # CYRILLIC SMALL LETTER U
0xD6    0x0436 "ж"	  # CYRILLIC SMALL LETTER ZHE
0xD7    0x0432 "в"	  # CYRILLIC SMALL LETTER VE
0xD8    0x044C "ь"	  # CYRILLIC SMALL LETTER SOFT SIGN
0xD9    0x044B "ы"	  # CYRILLIC SMALL LETTER YERU
0xDA    0x0437 "з"	  # CYRILLIC SMALL LETTER ZE
0xDB    0x0448 "ш"	  # CYRILLIC SMALL LETTER SHA
0xDC    0x044D "э"	  # CYRILLIC SMALL LETTER E
0xDD    0x0449 "щ"	  # CYRILLIC SMALL LETTER SHCHA
0xDE    0x0447 "ч"	  # CYRILLIC SMALL LETTER CHE
0xDF    0x044A "ъ"	  # CYRILLIC SMALL LETTER HARD SIGN
0xE0    0x042E "Ю"	  # CYRILLIC CAPITAL LETTER YU
0xE1    0x0410 "А"	  # CYRILLIC CAPITAL LETTER A
0xE2    0x0411 "Б"	  # CYRILLIC CAPITAL LETTER BE
0xE3    0x0426 "Ц"	  # CYRILLIC CAPITAL LETTER TSE
0xE4    0x0414 "Д"	  # CYRILLIC CAPITAL LETTER DE
0xE5    0x0415 "Е"	  # CYRILLIC CAPITAL LETTER IE
0xE6    0x0424 "Ф"	  # CYRILLIC CAPITAL LETTER EF
0xE7    0x0413 "Г"	  # CYRILLIC CAPITAL LETTER GHE
0xE8    0x0425 "Х"	  # CYRILLIC CAPITAL LETTER HA
0xE9    0x0418 "И"	  # CYRILLIC CAPITAL LETTER I
0xEA    0x0419 "Й"	  # CYRILLIC CAPITAL LETTER SHORT I
0xEB    0x041A "К"	  # CYRILLIC CAPITAL LETTER KA
0xEC    0x041B "Л"	  # CYRILLIC CAPITAL LETTER EL
0xED    0x041C "М"	  # CYRILLIC CAPITAL LETTER EM
0xEE    0x041D "Н"	  # CYRILLIC CAPITAL LETTER EN
0xEF    0x041E "О"	  # CYRILLIC CAPITAL LETTER O
0xF0    0x041F "П"	  # CYRILLIC CAPITAL LETTER PE
0xF1    0x042F "Я"	  # CYRILLIC CAPITAL LETTER YA
0xF2    0x0420 "Р"	  # CYRILLIC CAPITAL LETTER ER
0xF3    0x0421 "С"	  # CYRILLIC CAPITAL LETTER ES
0xF4    0x0422 "Т"	  # CYRILLIC CAPITAL LETTER TE
0xF5    0x0423 "У"	  # CYRILLIC CAPITAL LETTER U
0xF6    0x0416 "Ж"	  # CYRILLIC CAPITAL LETTER ZHE
0xF7    0x0412 "В"	  # CYRILLIC CAPITAL LETTER VE
0xF8    0x042C "Ь"	  # CYRILLIC CAPITAL LETTER SOFT SIGN
0xF9    0x042B "Ы"	  # CYRILLIC CAPITAL LETTER YERU
0xFA    0x0417 "З"	  # CYRILLIC CAPITAL LETTER ZE
0xFB    0x0428 "Ш"	  # CYRILLIC CAPITAL LETTER SHA
0xFC    0x042D "Э"	  # CYRILLIC CAPITAL LETTER E
0xFD    0x0429 "Щ"	  # CYRILLIC CAPITAL LETTER SHCHA
0xFE    0x0427 "Ч"	  # CYRILLIC CAPITAL LETTER CHE
0xFF    0x042A "Ъ"	  # CYRILLIC CAPITAL LETTER HARD SIGN
lynx2-8-8/test/quickbrown.html000644 023711 023712 00000007420 07550154260 017013 0ustar00dickeylynx000000 000000 Markus Kuhn's quick-brown-fox UTF-8 demo
Sentences that contain all letters commonly used in a language
--------------------------------------------------------------

Markus Kuhn <mkuhn@acm.org> -- 1998-11-30

This file was UTF-8 encoded.


German (de)
-----------

  Falsches Üben von Xylophonmusik quält jeden größeren Zwerg
  (= Wrongful practicing of xylophone music tortures every larger dwarf)

  Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich
  (= Twelve boxing fighters hunted Eva across the dike of Sylt)

  Heizölrückstoßabdämpfung
  (= fuel oil recoil absorber) (jqvwxy missing, but all non-ASCII letters in one word)

English (en)
------------

  The quick brown fox jumps over the lazy dog

French (fr)
-----------

  Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à
  côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce qui lui
  permet de penser à la cænogenèse de l'être dont il est question dans la
  cause ambiguë entendue à Moÿ, dans un capharnaüm qui, pense-t-il, diminue
  çà et là la qualité de son œuvre. 

  l'île exiguë
  Où l'obèse jury mûr
  Fête l'haï volapük,
  Âne ex aéquo au whist,
  Ôtez ce vœu déçu.

  Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en
  canoë au delà des îles, près du mälström où brûlent les novæ.

Irish Gaelic (ga)
-----------------

  D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh

Icelandic (is)
--------------

  Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa

  Sævör grét áðan því úlpan var ónýt
  (some ASCII letters missing)

Hebrew (iw)
-----------

  דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה?

Polish (pl)
-----------

  Pchnąć w tę łódź jeża lub ośm skrzyń fig

Russian (ru)
------------

  В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!
  (= Would a citrus live in the bushes of south? Yes, but a only a fake!)


Please let me know if you find others! Special thanks to the people
from all over the world who contributed these sentences.

See also: lynx2-8-8/test/raw8bit.html000644 023711 023712 00000002254 10164770217 016210 0ustar00dickeylynx000000 000000 Test of raw 8-bit symbols
This is a test of translation 8-bit letters for different pairs of
document's charset (assumed charset) and display charset,
both can be reached from 'O'ptions menu.

This page (obviously) corresponds to text/html mode
but you may test text/plain just by pressing '\'
Try also: '@' for ``raw mode'' and '=' for Information Page.


    0 1 2 3 4 5 6 7 8 9 A B C D E F
20    ! " # $ % & ' ( ) * + , - . /
30  0 1 2 3 4 5 6 7 8 9 : ; < = > ?
40  @ A B C D E F G H I J K L M N O
50  P Q R S T U V W X Y Z [ \ ] ^ _
60  ` a b c d e f g h i j k l m n o
70  p q r s t u v w x y z { | } ~ 
80                 
90                 
A0                 
B0                 
C0                 
D0                 
E0                 
F0                 

lynx2-8-8/test/sgml.html000644 023711 023712 00000171407 07550154260 015600 0ustar00dickeylynx000000 000000 Test of some Unicode symbols enclosed as SGML entity names

    This table prepared from SGML.TXT available at ftp.unicode.org

         ftp://ftp.unicode.org/MAPPINGS/VENDORS/MISC/SGML.TXT
         (if doing ftp, try cd Public/MAPPINGS/VENDORS/MISC)


original comment:

# Author: John Cowan <cowan@ccil.org>
# Date: 25 July 1997
#
# The following table maps SGML character entities from various
# public sets (namely, ISOamsa, ISOamsb, ISOamsc, ISOamsn, ISOamso,
# ISOamsr, ISObox, ISOcyr1, ISOcyr2, ISOdia, ISOgrk1, ISOgrk2,
# ISOgrk3, ISOgrk4, ISOlat1, ISOlat2, ISOnum, ISOpub, ISOtech,
# HTMLspecial, HTMLsymbol) to corresponding Unicode characters.
#
# The table has four tab-separated columns:
#	Column 1: SGML character entity name
#	Column 2: SGML public entity set
#	Column 3: Unicode 2.0 character code
#	Column 4: Unicode 2.0 character name (UPPER CASE)
# Entries which don't have Unicode equivalents have "0x????"
# in Column 3 and a lower case description (from the public entity
# set DTD) in Column 4.  The mapping is not reversible, because many
# distinctions are unified away in Unicode, particularly between
# mathematical symbols.
#
# The table is sorted case-blind by SGML character entity name.
#
# The contents of this table are drawn from various sources, and
# are in the public domain.
#


This test illuminates SGML character entities implementation in your browser.
We sort the entities according to unicode numbers.
You should see visible character if your display character set supports it
or some substitution string picked up from  src/chrtrans/def7_uni.tbl.
If you see &somename; - this name is not implemented yet,
you may search for &. (Sorry, ISOgrk4 which holds a dot in its name
seems to be nonvisible for most browsers.  Keep in mind that
this table is much wider than in the HTML 4.0 draft).
							Leonid Pauzner.


0x0021    !	      ISOnum	# EXCLAMATION MARK
0x0022    "	      ISOnum	# QUOTATION MARK
0x0023    #	      ISOnum	# NUMBER SIGN
0x0024    $	      ISOnum	# DOLLAR SIGN
0x0025    %	      ISOnum	# PERCENT SIGN
0x0026    &	      ISOnum	# AMPERSAND
0x0028    (	      ISOnum	# LEFT PARENTHESIS
0x0029    )	      ISOnum	# RIGHT PARENTHESIS
0x002A    *	      ISOnum	# ASTERISK
0x002B    +	      ISOnum	# PLUS SIGN
0x002C    ,	      ISOnum	# COMMA
0x002D    ‐	      ISOnum	# HYPHEN-MINUS
0x002E    .	      ISOnum	# FULL STOP
0x002F    /	      ISOnum	# SOLIDUS
0x003A    :	      ISOnum	# COLON
0x003B    ;	      ISOnum	# SEMICOLON
0x003C    <	      ISOnum	# LESS-THAN SIGN
0x003D    =	      ISOnum	# EQUALS SIGN
0x003E    >	      ISOnum	# GREATER-THAN SIGN
0x003F    ?	      ISOnum	# QUESTION MARK
0x0040    @	      ISOnum	# COMMERCIAL AT
0x005B    [	      ISOnum	# LEFT SQUARE BRACKET
0x005C    \	      ISOnum	# REVERSE SOLIDUS
0x005C    &sbsol;	      ISOamso	# REVERSE SOLIDUS
0x005D    ]	      ISOnum	# RIGHT SQUARE BRACKET
0x005F    _	      ISOnum	# LOW LINE
0x0060    `	      ISOdia	# GRAVE ACCENT
0x007B    {	      ISOnum	# LEFT CURLY BRACKET
0x007C    |	      ISOnum	# VERTICAL LINE
0x007D    }	      ISOnum	# RIGHT CURLY BRACKET
0x00A0     	      ISOnum	# NO-BREAK SPACE
0x00A1    ¡	      ISOnum	# INVERTED EXCLAMATION MARK
0x00A2    ¢	      ISOnum	# CENT SIGN
0x00A3    £	      ISOnum	# POUND SIGN
0x00A4    ¤	      ISOnum	# CURRENCY SIGN
0x00A5    ¥	      ISOnum	# YEN SIGN
0x00A6    ¦	      ISOnum	# BROKEN BAR
0x00A7    §	      ISOnum	# SECTION SIGN
0x00A8    ¨	      ISOtech	# DIAERESIS
0x00A8    ¨	      ISOdia	# DIAERESIS
0x00A8    ¨	      ISOdia	# DIAERESIS
0x00A9    ©	      ISOnum	# COPYRIGHT SIGN
0x00AA    ª	      ISOnum	# FEMININE ORDINAL INDICATOR
0x00AB    «	      ISOnum	# LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC    ¬	      ISOnum	# NOT SIGN
0x00AD    ­	      ISOnum	# SOFT HYPHEN
0x00AE    ®	      ISOnum	# REGISTERED SIGN
0x00AF    ¯	      ISOdia	# MACRON
0x00B0    °	      ISOnum	# DEGREE SIGN
0x00B1    ±	      ISOnum	# PLUS-MINUS SIGN
0x00B2    ²	      ISOnum	# SUPERSCRIPT TWO
0x00B3    ³	      ISOnum	# SUPERSCRIPT THREE
0x00B4    ´	      ISOdia	# ACUTE ACCENT
0x00B5    µ	      ISOnum	# MICRO SIGN
0x00B6    ¶	      ISOnum	# PILCROW SIGN
0x00B7    ·	      ISOnum	# MIDDLE DOT
0x00B8    ¸	      ISOdia	# CEDILLA
0x00B9    ¹	      ISOnum	# SUPERSCRIPT ONE
0x00BA    º	      ISOnum	# MASCULINE ORDINAL INDICATOR
0x00BB    »	      ISOnum	# RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC    ¼	      ISOnum	# VULGAR FRACTION ONE QUARTER
0x00BD    ½	      ISOnum	# VULGAR FRACTION ONE HALF
0x00BD    ½	      ISOnum	# VULGAR FRACTION ONE HALF
0x00BE    ¾	      ISOnum	# VULGAR FRACTION THREE QUARTERS
0x00BF    ¿	      ISOnum	# INVERTED QUESTION MARK
0x00C0    À	      ISOlat1	# LATIN CAPITAL LETTER A WITH GRAVE
0x00C1    Á	      ISOlat1	# LATIN CAPITAL LETTER A WITH ACUTE
0x00C2    Â	      ISOlat1	# LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x00C3    Ã	      ISOlat1	# LATIN CAPITAL LETTER A WITH TILDE
0x00C4    Ä	      ISOlat1	# LATIN CAPITAL LETTER A WITH DIAERESIS
0x00C5    Å	      ISOlat1	# LATIN CAPITAL LETTER A WITH RING ABOVE
0x00C6    Æ	      ISOlat1	# LATIN CAPITAL LETTER AE
0x00C7    Ç	      ISOlat1	# LATIN CAPITAL LETTER C WITH CEDILLA
0x00C8    È	      ISOlat1	# LATIN CAPITAL LETTER E WITH GRAVE
0x00C9    É	      ISOlat1	# LATIN CAPITAL LETTER E WITH ACUTE
0x00CA    Ê	      ISOlat1	# LATIN CAPITAL LETTER E WITH CIRCUMFLEX
0x00CB    Ë	      ISOlat1	# LATIN CAPITAL LETTER E WITH DIAERESIS
0x00CC    Ì	      ISOlat1	# LATIN CAPITAL LETTER I WITH GRAVE
0x00CD    Í	      ISOlat1	# LATIN CAPITAL LETTER I WITH ACUTE
0x00CE    Î	      ISOlat1	# LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x00CF    Ï	      ISOlat1	# LATIN CAPITAL LETTER I WITH DIAERESIS
0x00D0    Ð	      ISOlat1	# LATIN CAPITAL LETTER ETH
0x00D1    Ñ	      ISOlat1	# LATIN CAPITAL LETTER N WITH TILDE
0x00D2    Ò	      ISOlat1	# LATIN CAPITAL LETTER O WITH GRAVE
0x00D3    Ó	      ISOlat1	# LATIN CAPITAL LETTER O WITH ACUTE
0x00D4    Ô	      ISOlat1	# LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x00D5    Õ	      ISOlat1	# LATIN CAPITAL LETTER O WITH TILDE
0x00D6    Ö	      ISOlat1	# LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7    ×	      ISOnum	# MULTIPLICATION SIGN
0x00D8    Ø	      ISOlat1	# LATIN CAPITAL LETTER O WITH STROKE
0x00D9    Ù	      ISOlat1	# LATIN CAPITAL LETTER U WITH GRAVE
0x00DA    Ú	      ISOlat1	# LATIN CAPITAL LETTER U WITH ACUTE
0x00DB    Û	      ISOlat1	# LATIN CAPITAL LETTER U WITH CIRCUMFLEX
0x00DC    Ü	      ISOlat1	# LATIN CAPITAL LETTER U WITH DIAERESIS
0x00DD    Ý	      ISOlat1	# LATIN CAPITAL LETTER Y WITH ACUTE
0x00DE    Þ	      ISOlat1	# LATIN CAPITAL LETTER THORN
0x00DF    ß	      ISOlat1	# LATIN SMALL LETTER SHARP S
0x00E0    à	      ISOlat1	# LATIN SMALL LETTER A WITH GRAVE
0x00E1    á	      ISOlat1	# LATIN SMALL LETTER A WITH ACUTE
0x00E2    â	      ISOlat1	# LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00E3    ã	      ISOlat1	# LATIN SMALL LETTER A WITH TILDE
0x00E4    ä	      ISOlat1	# LATIN SMALL LETTER A WITH DIAERESIS
0x00E5    å	      ISOlat1	# LATIN SMALL LETTER A WITH RING ABOVE
0x00E6    æ	      ISOlat1	# LATIN SMALL LETTER AE
0x00E7    ç	      ISOlat1	# LATIN SMALL LETTER C WITH CEDILLA
0x00E8    è	      ISOlat1	# LATIN SMALL LETTER E WITH GRAVE
0x00E9    é	      ISOlat1	# LATIN SMALL LETTER E WITH ACUTE
0x00EA    ê	      ISOlat1	# LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00EB    ë	      ISOlat1	# LATIN SMALL LETTER E WITH DIAERESIS
0x00EC    ì	      ISOlat1	# LATIN SMALL LETTER I WITH GRAVE
0x00ED    í	      ISOlat1	# LATIN SMALL LETTER I WITH ACUTE
0x00EE    î	      ISOlat1	# LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00EF    ï	      ISOlat1	# LATIN SMALL LETTER I WITH DIAERESIS
0x00F0    ð	      ISOlat1	# LATIN SMALL LETTER ETH
0x00F1    ñ	      ISOlat1	# LATIN SMALL LETTER N WITH TILDE
0x00F2    ò	      ISOlat1	# LATIN SMALL LETTER O WITH GRAVE
0x00F3    ó	      ISOlat1	# LATIN SMALL LETTER O WITH ACUTE
0x00F4    ô	      ISOlat1	# LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00F5    õ	      ISOlat1	# LATIN SMALL LETTER O WITH TILDE
0x00F6    ö	      ISOlat1	# LATIN SMALL LETTER O WITH DIAERESIS
0x00F7    ÷	      ISOnum	# DIVISION SIGN
0x00F8    ø	      ISOlat1	# LATIN SMALL LETTER O WITH STROKE
0x00F9    ù	      ISOlat1	# LATIN SMALL LETTER U WITH GRAVE
0x00FA    ú	      ISOlat1	# LATIN SMALL LETTER U WITH ACUTE
0x00FB    û	      ISOlat1	# LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00FC    ü	      ISOlat1	# LATIN SMALL LETTER U WITH DIAERESIS
0x00FD    ý	      ISOlat1	# LATIN SMALL LETTER Y WITH ACUTE
0x00FE    þ	      ISOlat1	# LATIN SMALL LETTER THORN
0x00FF    ÿ	      ISOlat1	# LATIN SMALL LETTER Y WITH DIAERESIS
0x0100    Ā	      ISOlat2	# LATIN CAPITAL LETTER A WITH MACRON
0x0101    ā	      ISOlat2	# LATIN SMALL LETTER A WITH MACRON
0x0102    Ă	      ISOlat2	# LATIN CAPITAL LETTER A WITH BREVE
0x0103    ă	      ISOlat2	# LATIN SMALL LETTER A WITH BREVE
0x0104    Ą	      ISOlat2	# LATIN CAPITAL LETTER A WITH OGONEK
0x0105    ą	      ISOlat2	# LATIN SMALL LETTER A WITH OGONEK
0x0106    Ć	      ISOlat2	# LATIN CAPITAL LETTER C WITH ACUTE
0x0107    ć	      ISOlat2	# LATIN SMALL LETTER C WITH ACUTE
0x0108    Ĉ	      ISOlat2	# LATIN CAPITAL LETTER C WITH CIRCUMFLEX
0x0109    ĉ	      ISOlat2	# LATIN SMALL LETTER C WITH CIRCUMFLEX
0x010A    Ċ	      ISOlat2	# LATIN CAPITAL LETTER C WITH DOT ABOVE
0x010B    ċ	      ISOlat2	# LATIN SMALL LETTER C WITH DOT ABOVE
0x010C    Č	      ISOlat2	# LATIN CAPITAL LETTER C WITH CARON
0x010D    č	      ISOlat2	# LATIN SMALL LETTER C WITH CARON
0x010E    Ď	      ISOlat2	# LATIN CAPITAL LETTER D WITH CARON
0x010F    ď	      ISOlat2	# LATIN SMALL LETTER D WITH CARON
0x0110    Đ	      ISOlat2	# LATIN CAPITAL LETTER D WITH STROKE
0x0111    đ	      ISOlat2	# LATIN SMALL LETTER D WITH STROKE
0x0112    Ē	      ISOlat2	# LATIN CAPITAL LETTER E WITH MACRON
0x0113    ē	      ISOlat2	# LATIN SMALL LETTER E WITH MACRON
0x0116    Ė	      ISOlat2	# LATIN CAPITAL LETTER E WITH DOT ABOVE
0x0117    ė	      ISOlat2	# LATIN SMALL LETTER E WITH DOT ABOVE
0x0118    Ę	      ISOlat2	# LATIN CAPITAL LETTER E WITH OGONEK
0x0119    ę	      ISOlat2	# LATIN SMALL LETTER E WITH OGONEK
0x011A    Ě	      ISOlat2	# LATIN CAPITAL LETTER E WITH CARON
0x011B    ě	      ISOlat2	# LATIN SMALL LETTER E WITH CARON
0x011C    Ĝ	      ISOlat2	# LATIN CAPITAL LETTER G WITH CIRCUMFLEX
0x011D    ĝ	      ISOlat2	# LATIN SMALL LETTER G WITH CIRCUMFLEX
0x011E    Ğ	      ISOlat2	# LATIN CAPITAL LETTER G WITH BREVE
0x011F    ğ	      ISOlat2	# LATIN SMALL LETTER G WITH BREVE
0x0120    Ġ	      ISOlat2	# LATIN CAPITAL LETTER G WITH DOT ABOVE
0x0121    ġ	      ISOlat2	# LATIN SMALL LETTER G WITH DOT ABOVE
0x0122    Ģ	      ISOlat2	# LATIN CAPITAL LETTER G WITH CEDILLA
0x0123    &gcedil;	      ISOlat2	# LATIN SMALL LETTER G WITH CEDILLA
0x0124    Ĥ	      ISOlat2	# LATIN CAPITAL LETTER H WITH CIRCUMFLEX
0x0125    ĥ	      ISOlat2	# LATIN SMALL LETTER H WITH CIRCUMFLEX
0x0126    Ħ	      ISOlat2	# LATIN CAPITAL LETTER H WITH STROKE
0x0127    ħ	      ISOlat2	# LATIN SMALL LETTER H WITH STROKE
0x0128    Ĩ	      ISOlat2	# LATIN CAPITAL LETTER I WITH TILDE
0x0129    ĩ	      ISOlat2	# LATIN SMALL LETTER I WITH TILDE
0x012A    Ī	      ISOlat2	# LATIN CAPITAL LETTER I WITH MACRON
0x012B    ī	      ISOlat2	# LATIN SMALL LETTER I WITH MACRON
0x012E    Į	      ISOlat2	# LATIN CAPITAL LETTER I WITH OGONEK
0x012F    į	      ISOlat2	# LATIN SMALL LETTER I WITH OGONEK
0x0130    İ	      ISOlat2	# LATIN CAPITAL LETTER I WITH DOT ABOVE
0x0131    ı	      ISOamso	# LATIN SMALL LETTER DOTLESS I
0x0131    ı	      ISOlat2	# LATIN SMALL LETTER DOTLESS I
0x0132    IJ	      ISOlat2	# LATIN CAPITAL LIGATURE IJ
0x0133    ij	      ISOlat2	# LATIN SMALL LIGATURE IJ
0x0134    Ĵ	      ISOlat2	# LATIN CAPITAL LETTER J WITH CIRCUMFLEX
0x0135    ĵ	      ISOlat2	# LATIN SMALL LETTER J WITH CIRCUMFLEX
0x0136    Ķ	      ISOlat2	# LATIN CAPITAL LETTER K WITH CEDILLA
0x0137    ķ	      ISOlat2	# LATIN SMALL LETTER K WITH CEDILLA
0x0138    ĸ	      ISOlat2	# LATIN SMALL LETTER KRA
0x0139    Ĺ	      ISOlat2	# LATIN CAPITAL LETTER L WITH ACUTE
0x013A    ĺ	      ISOlat2	# LATIN SMALL LETTER L WITH ACUTE
0x013B    Ļ	      ISOlat2	# LATIN CAPITAL LETTER L WITH CEDILLA
0x013C    ļ	      ISOlat2	# LATIN SMALL LETTER L WITH CEDILLA
0x013D    Ľ	      ISOlat2	# LATIN CAPITAL LETTER L WITH CARON
0x013E    ľ	      ISOlat2	# LATIN SMALL LETTER L WITH CARON
0x013F    Ŀ	      ISOlat2	# LATIN CAPITAL LETTER L WITH MIDDLE DOT
0x0140    ŀ	      ISOlat2	# LATIN SMALL LETTER L WITH MIDDLE DOT
0x0141    Ł	      ISOlat2	# LATIN CAPITAL LETTER L WITH STROKE
0x0142    ł	      ISOlat2	# LATIN SMALL LETTER L WITH STROKE
0x0143    Ń	      ISOlat2	# LATIN CAPITAL LETTER N WITH ACUTE
0x0144    ń	      ISOlat2	# LATIN SMALL LETTER N WITH ACUTE
0x0145    Ņ	      ISOlat2	# LATIN CAPITAL LETTER N WITH CEDILLA
0x0146    ņ	      ISOlat2	# LATIN SMALL LETTER N WITH CEDILLA
0x0147    Ň	      ISOlat2	# LATIN CAPITAL LETTER N WITH CARON
0x0148    ň	      ISOlat2	# LATIN SMALL LETTER N WITH CARON
0x0149    ʼn	      ISOlat2	# LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
0x014A    Ŋ	      ISOlat2	# LATIN CAPITAL LETTER ENG
0x014B    ŋ	      ISOlat2	# LATIN SMALL LETTER ENG
0x014C    Ō	      ISOlat2	# LATIN CAPITAL LETTER O WITH MACRON
0x014D    ō	      ISOlat2	# LATIN SMALL LETTER O WITH MACRON
0x0150    Ő	      ISOlat2	# LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
0x0151    ő	      ISOlat2	# LATIN SMALL LETTER O WITH DOUBLE ACUTE
0x0152    Œ	      ISOlat2	# LATIN CAPITAL LIGATURE OE
0x0153    œ	      ISOlat2	# LATIN SMALL LIGATURE OE
0x0154    Ŕ	      ISOlat2	# LATIN CAPITAL LETTER R WITH ACUTE
0x0155    ŕ	      ISOlat2	# LATIN SMALL LETTER R WITH ACUTE
0x0156    Ŗ	      ISOlat2	# LATIN CAPITAL LETTER R WITH CEDILLA
0x0157    ŗ	      ISOlat2	# LATIN SMALL LETTER R WITH CEDILLA
0x0158    Ř	      ISOlat2	# LATIN CAPITAL LETTER R WITH CARON
0x0159    ř	      ISOlat2	# LATIN SMALL LETTER R WITH CARON
0x015A    Ś	      ISOlat2	# LATIN CAPITAL LETTER S WITH ACUTE
0x015B    ś	      ISOlat2	# LATIN SMALL LETTER S WITH ACUTE
0x015C    Ŝ	      ISOlat2	# LATIN CAPITAL LETTER S WITH CIRCUMFLEX
0x015D    ŝ	      ISOlat2	# LATIN SMALL LETTER S WITH CIRCUMFLEX
0x015E    Ş	      ISOlat2	# LATIN CAPITAL LETTER S WITH CEDILLA
0x015F    ş	      ISOlat2	# LATIN SMALL LETTER S WITH CEDILLA
0x0160    Š	      ISOlat2	# LATIN CAPITAL LETTER S WITH CARON
0x0161    š	      ISOlat2	# LATIN SMALL LETTER S WITH CARON
0x0162    Ţ	      ISOlat2	# LATIN CAPITAL LETTER T WITH CEDILLA
0x0163    ţ	      ISOlat2	# LATIN SMALL LETTER T WITH CEDILLA
0x0164    Ť	      ISOlat2	# LATIN CAPITAL LETTER T WITH CARON
0x0165    ť	      ISOlat2	# LATIN SMALL LETTER T WITH CARON
0x0166    Ŧ	      ISOlat2	# LATIN CAPITAL LETTER T WITH STROKE
0x0167    ŧ	      ISOlat2	# LATIN SMALL LETTER T WITH STROKE
0x0168    Ũ	      ISOlat2	# LATIN CAPITAL LETTER U WITH TILDE
0x0169    ũ	      ISOlat2	# LATIN SMALL LETTER U WITH TILDE
0x016A    Ū	      ISOlat2	# LATIN CAPITAL LETTER U WITH MACRON
0x016B    ū	      ISOlat2	# LATIN SMALL LETTER U WITH MACRON
0x016C    Ŭ	      ISOlat2	# LATIN CAPITAL LETTER U WITH BREVE
0x016D    ŭ	      ISOlat2	# LATIN SMALL LETTER U WITH BREVE
0x016E    Ů	      ISOlat2	# LATIN CAPITAL LETTER U WITH RING ABOVE
0x016F    ů	      ISOlat2	# LATIN SMALL LETTER U WITH RING ABOVE
0x0170    Ű	      ISOlat2	# LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
0x0171    ű	      ISOlat2	# LATIN SMALL LETTER U WITH DOUBLE ACUTE
0x0172    Ų	      ISOlat2	# LATIN CAPITAL LETTER U WITH OGONEK
0x0173    ų	      ISOlat2	# LATIN SMALL LETTER U WITH OGONEK
0x0174    Ŵ	      ISOlat2	# LATIN CAPITAL LETTER W WITH CIRCUMFLEX
0x0175    ŵ	      ISOlat2	# LATIN SMALL LETTER W WITH CIRCUMFLEX
0x0176    Ŷ	      ISOlat2	# LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
0x0177    ŷ	      ISOlat2	# LATIN SMALL LETTER Y WITH CIRCUMFLEX
0x0178    Ÿ	      ISOlat2	# LATIN CAPITAL LETTER Y WITH DIAERESIS
0x0179    Ź	      ISOlat2	# LATIN CAPITAL LETTER Z WITH ACUTE
0x017A    ź	      ISOlat2	# LATIN SMALL LETTER Z WITH ACUTE
0x017B    Ż	      ISOlat2	# LATIN CAPITAL LETTER Z WITH DOT ABOVE
0x017C    ż	      ISOlat2	# LATIN SMALL LETTER Z WITH DOT ABOVE
0x017D    Ž	      ISOlat2	# LATIN CAPITAL LETTER Z WITH CARON
0x017E    ž	      ISOlat2	# LATIN SMALL LETTER Z WITH CARON
0x0192    ƒ	      ISOtech	# LATIN SMALL LETTER F WITH HOOK
0x01F5    ǵ	      ISOlat2	# LATIN SMALL LETTER G WITH ACUTE
0x02BC    '	      ISOnum	# MODIFIER LETTER APOSTROPHE
0x02C6    ˆ	      ISOdia	# MODIFIER LETTER CIRCUMFLEX ACCENT
0x02C7    ˇ	      ISOdia	# CARON
0x02D8    ˘	      ISOdia	# BREVE
0x02D9    ˙	      ISOdia	# DOT ABOVE
0x02DA    ˚	      ISOdia	# RING ABOVE
0x02DB    ˛	      ISOdia	# OGONEK
0x02DC    ˜	      ISOdia	# SMALL TILDE
0x02DD    ˝	      ISOdia	# DOUBLE ACUTE ACCENT
0x0386    &Aacgr;	      ISOgrk2	# GREEK CAPITAL LETTER ALPHA WITH TONOS
0x0388    &Eacgr;	      ISOgrk2	# GREEK CAPITAL LETTER EPSILON WITH TONOS
0x0389    &EEacgr;	      ISOgrk2	# GREEK CAPITAL LETTER ETA WITH TONOS
0x038A    &Iacgr;	      ISOgrk2	# GREEK CAPITAL LETTER IOTA WITH TONOS
0x038C    &Oacgr;	      ISOgrk2	# GREEK CAPITAL LETTER OMICRON WITH TONOS
0x038E    &Uacgr;	      ISOgrk2	# GREEK CAPITAL LETTER UPSILON WITH TONOS
0x038F    &OHacgr;	      ISOgrk2	# GREEK CAPITAL LETTER OMEGA WITH TONOS
0x0390    &idiagr;	      ISOgrk2	# GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
0x0391    &Agr;	      ISOgrk1	# GREEK CAPITAL LETTER ALPHA
0x0391    Α	      HTMLsymbol	# GREEK CAPITAL LETTER ALPHA
0x0392    Β	      HTMLsymbol	# GREEK CAPITAL LETTER BETA
0x0392    &Bgr;	      ISOgrk1	# GREEK CAPITAL LETTER BETA
0x0393    Γ	      ISOgrk3	# GREEK CAPITAL LETTER GAMMA
0x0393    &Ggr;	      ISOgrk1	# GREEK CAPITAL LETTER GAMMA
0x0393    &b.Gamma;	      ISOgrk4	# GREEK CAPITAL LETTER GAMMA
0x0394    Δ	      ISOgrk3	# GREEK CAPITAL LETTER DELTA
0x0394    &Dgr;	      ISOgrk1	# GREEK CAPITAL LETTER DELTA
0x0394    &b.Delta;	      ISOgrk4	# GREEK CAPITAL LETTER DELTA
0x0395    &Egr;	      ISOgrk1	# GREEK CAPITAL LETTER EPSILON
0x0395    Ε	      HTMLsymbol	# GREEK CAPITAL LETTER EPSILON
0x0396    Ζ	      HTMLsymbol	# GREEK CAPITAL LETTER ZETA
0x0396    &Zgr;	      ISOgrk1	# GREEK CAPITAL LETTER ZETA
0x0397    &EEgr;	      ISOgrk1	# GREEK CAPITAL LETTER ETA
0x0397    Η	      HTMLsymbol	# GREEK CAPITAL LETTER ETA
0x0398    &THgr;	      ISOgrk1	# GREEK CAPITAL LETTER THETA
0x0398    Θ	      ISOgrk3	# GREEK CAPITAL LETTER THETA
0x0398    &b.Theta;	      ISOgrk4	# GREEK CAPITAL LETTER THETA
0x0399    &Igr;	      ISOgrk1	# GREEK CAPITAL LETTER IOTA
0x0399    Ι	      HTMLsymbol	# GREEK CAPITAL LETTER IOTA
0x039A    Κ	      HTMLsymbol	# GREEK CAPITAL LETTER KAPPA
0x039A    &Kgr;	      ISOgrk1	# GREEK CAPITAL LETTER KAPPA
0x039B    Λ	      ISOgrk3	# GREEK CAPITAL LETTER LAMDA
0x039B    &Lgr;	      ISOgrk1	# GREEK CAPITAL LETTER LAMDA
0x039B    &b.Lambda;	      ISOgrk4	# GREEK CAPITAL LETTER LAMDA
0x039C    &Mgr;	      ISOgrk1	# GREEK CAPITAL LETTER MU
0x039C    Μ	      HTMLsymbol	# GREEK CAPITAL LETTER MU
0x039D    &Ngr;	      ISOgrk1	# GREEK CAPITAL LETTER NU
0x039D    Ν	      HTMLsymbol	# GREEK CAPITAL LETTER NU
0x039E    &Xgr;	      ISOgrk1	# GREEK CAPITAL LETTER XI
0x039E    Ξ	      ISOgrk3	# GREEK CAPITAL LETTER XI
0x039E    &b.Xi;	      ISOgrk4	# GREEK CAPITAL LETTER XI
0x039F    &Ogr;	      ISOgrk1	# GREEK CAPITAL LETTER OMICRON
0x039F    Ο	      HTMLsymbol	# GREEK CAPITAL LETTER OMICRON
0x03A0    &Pgr;	      ISOgrk1	# GREEK CAPITAL LETTER PI
0x03A0    Π	      ISOgrk3	# GREEK CAPITAL LETTER PI
0x03A0    &b.Pi;	      ISOgrk4	# GREEK CAPITAL LETTER PI
0x03A1    &Rgr;	      ISOgrk1	# GREEK CAPITAL LETTER RHO
0x03A1    Ρ	      HTMLsymbol	# GREEK CAPITAL LETTER RHO
0x03A3    &Sgr;	      ISOgrk1	# GREEK CAPITAL LETTER SIGMA
0x03A3    Σ	      ISOgrk3	# GREEK CAPITAL LETTER SIGMA
0x03A3    &b.Sigma;	      ISOgrk4	# GREEK CAPITAL LETTER SIGMA
0x03A4    Τ	      HTMLsymbol	# GREEK CAPITAL LETTER TAU
0x03A4    &Tgr;	      ISOgrk1	# GREEK CAPITAL LETTER TAU
0x03A5    &Ugr;	      ISOgrk1	# GREEK CAPITAL LETTER UPSILON
0x03A5    ϒ	      ISOgrk3	# GREEK CAPITAL LETTER UPSILON
0x03A5    Υ	      HTMLsymbol	# GREEK CAPITAL LETTER UPSILON
0x03A5    &b.Upsi;	      ISOgrk4	# GREEK CAPITAL LETTER UPSILON
0x03A6    &PHgr;	      ISOgrk1	# GREEK CAPITAL LETTER PHI
0x03A6    Φ	      ISOgrk3	# GREEK CAPITAL LETTER PHI
0x03A6    &b.Phi;	      ISOgrk4	# GREEK CAPITAL LETTER PHI
0x03A7    Χ	      HTMLsymbol	# GREEK CAPITAL LETTER CHI
0x03A7    &KHgr;	      ISOgrk1	# GREEK CAPITAL LETTER CHI
0x03A8    &PSgr;	      ISOgrk1	# GREEK CAPITAL LETTER PSI
0x03A8    Ψ	      ISOgrk3	# GREEK CAPITAL LETTER PSI
0x03A8    &b.Psi;	      ISOgrk4	# GREEK CAPITAL LETTER PSI
0x03A9    &OHgr;	      ISOgrk1	# GREEK CAPITAL LETTER OMEGA
0x03A9    Ω	      ISOgrk3	# GREEK CAPITAL LETTER OMEGA
0x03A9    &b.Omega;	      ISOgrk4	# GREEK CAPITAL LETTER OMEGA
0x03AA    &Idigr;	      ISOgrk2	# GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
0x03AB    &Udigr;	      ISOgrk2	# GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
0x03AC    &aacgr;	      ISOgrk2	# GREEK SMALL LETTER ALPHA WITH TONOS
0x03AD    &eacgr;	      ISOgrk2	# GREEK SMALL LETTER EPSILON WITH TONOS
0x03AE    &eeacgr;	      ISOgrk2	# GREEK SMALL LETTER ETA WITH TONOS
0x03AF    &iacgr;	      ISOgrk2	# GREEK SMALL LETTER IOTA WITH TONOS
0x03B0    &udiagr;	      ISOgrk2	# GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND
0x03B1    &agr;	      ISOgrk1	# GREEK SMALL LETTER ALPHA
0x03B1    α	      ISOgrk3	# GREEK SMALL LETTER ALPHA
0x03B1    &b.alpha;	      ISOgrk4	# GREEK SMALL LETTER ALPHA
0x03B2    &b.beta;	      ISOgrk4	# GREEK SMALL LETTER BETA
0x03B2    β	      ISOgrk3	# GREEK SMALL LETTER BETA
0x03B2    &bgr;	      ISOgrk1	# GREEK SMALL LETTER BETA
0x03B3    &b.gamma;	      ISOgrk4	# GREEK SMALL LETTER GAMMA
0x03B3    γ	      ISOgrk3	# GREEK SMALL LETTER GAMMA
0x03B3    &ggr;	      ISOgrk1	# GREEK SMALL LETTER GAMMA
0x03B4    &b.delta;	      ISOgrk4   # GREEK SMALL LETTER DELTA
0x03B4    δ	      ISOgrk3	# GREEK SMALL LETTER DELTA
0x03B4    &dgr;	      ISOgrk1	# GREEK SMALL LETTER DELTA
0x03B5    &b.epsi;	      ISOgrk4	# GREEK SMALL LETTER EPSILON
0x03B5    &b.epsis;	      ISOgrk4	# GREEK SMALL LETTER EPSILON
0x03B5    &b.epsiv;	      ISOgrk4	# GREEK SMALL LETTER EPSILON
0x03B5    &egr;	      ISOgrk1	# GREEK SMALL LETTER EPSILON
0x03B5    ε	      ISOgrk3	# GREEK SMALL LETTER EPSILON
0x03B5    ε	      HTMLsymbol	# GREEK SMALL LETTER EPSILON
0x03B6    &b.zeta;	      ISOgrk4	# GREEK SMALL LETTER ZETA
0x03B6    ζ	      ISOgrk3	# GREEK SMALL LETTER ZETA
0x03B6    &zgr;	      ISOgrk1	# GREEK SMALL LETTER ZETA
0x03B7    &b.eta;	      ISOgrk4	# GREEK SMALL LETTER ETA
0x03B7    &eegr;	      ISOgrk1	# GREEK SMALL LETTER ETA
0x03B7    η	      ISOgrk3	# GREEK SMALL LETTER ETA
0x03B8    &b.thetas;	      ISOgrk4	# GREEK SMALL LETTER THETA
0x03B8    θ	      HTMLsymbol	# GREEK SMALL LETTER THETA
0x03B8    &thetas;	      ISOgrk3	# GREEK SMALL LETTER THETA
0x03B8    &thgr;	      ISOgrk1	# GREEK SMALL LETTER THETA
0x03B9    &b.iota;	      ISOgrk4	# GREEK SMALL LETTER IOTA
0x03B9    &igr;	      ISOgrk1	# GREEK SMALL LETTER IOTA
0x03B9    ι	      ISOgrk3	# GREEK SMALL LETTER IOTA
0x03BA    &b.kappa;	      ISOgrk4	# GREEK SMALL LETTER KAPPA
0x03BA    κ	      ISOgrk3	# GREEK SMALL LETTER KAPPA
0x03BA    &kgr;	      ISOgrk1	# GREEK SMALL LETTER KAPPA
0x03BB    &b.lambda;	      ISOgrk4	# GREEK SMALL LETTER LAMDA
0x03BB    λ	      ISOgrk3	# GREEK SMALL LETTER LAMDA
0x03BB    &lgr;	      ISOgrk1	# GREEK SMALL LETTER LAMDA
0x03BC    &b.mu;	      ISOgrk4	# GREEK SMALL LETTER MU
0x03BC    &mgr;	      ISOgrk1	# GREEK SMALL LETTER MU
0x03BC    μ	      ISOgrk3	# GREEK SMALL LETTER MU
0x03BD    &b.nu;	      ISOgrk4	# GREEK SMALL LETTER NU
0x03BD    &ngr;	      ISOgrk1	# GREEK SMALL LETTER NU
0x03BD    ν	      ISOgrk3	# GREEK SMALL LETTER NU
0x03BE    &b.xi;	      ISOgrk4	# GREEK SMALL LETTER XI
0x03BE    &xgr;	      ISOgrk1	# GREEK SMALL LETTER XI
0x03BE    ξ	      ISOgrk3	# GREEK SMALL LETTER XI
0x03BF    &ogr;	      ISOgrk1	# GREEK SMALL LETTER OMICRON
0x03BF    ο	      HTMLsymbol	# GREEK SMALL LETTER OMICRON
0x03C0    &b.pi;	      ISOgrk4	# GREEK SMALL LETTER PI
0x03C0    &pgr;	      ISOgrk1	# GREEK SMALL LETTER PI
0x03C0    π	      ISOgrk3	# GREEK SMALL LETTER PI
0x03C1    &b.rho;	      ISOgrk4	# GREEK SMALL LETTER RHO
0x03C1    &rgr;	      ISOgrk1	# GREEK SMALL LETTER RHO
0x03C1    ρ	      ISOgrk3	# GREEK SMALL LETTER RHO
0x03C2    &b.sigmav;	      ISOgrk4	# GREEK SMALL LETTER FINAL SIGMA
0x03C2    &sfgr;	      ISOgrk1	# GREEK SMALL LETTER FINAL SIGMA
0x03C2    ς	      HTMLsymbol	# GREEK SMALL LETTER FINAL SIGMA
0x03C2    ς	      ISOgrk3	# GREEK SMALL LETTER FINAL SIGMA
0x03C3    &b.sigma;	      ISOgrk4	# GREEK SMALL LETTER SIGMA
0x03C3    &sgr;	      ISOgrk1	# GREEK SMALL LETTER SIGMA
0x03C3    σ	      ISOgrk3	# GREEK SMALL LETTER SIGMA
0x03C4    &b.tau;	      ISOgrk4	# GREEK SMALL LETTER TAU
0x03C4    τ	      ISOgrk3	# GREEK SMALL LETTER TAU
0x03C4    &tgr;	      ISOgrk1	# GREEK SMALL LETTER TAU
0x03C5    &b.upsi;	      ISOgrk4	# GREEK SMALL LETTER UPSILON
0x03C5    &ugr;	      ISOgrk1	# GREEK SMALL LETTER UPSILON
0x03C5    υ	      ISOgrk3	# GREEK SMALL LETTER UPSILON
0x03C5    υ	      HTMLsymbol	# GREEK SMALL LETTER UPSILON
0x03C6    &b.phis;	      ISOgrk4	# GREEK SMALL LETTER PHI
0x03C6    &phgr;	      ISOgrk1	# GREEK SMALL LETTER PHI
0x03C6    φ	      HTMLsymbol	# GREEK SMALL LETTER PHI
0x03C6    &phis;	      ISOgrk3	# GREEK SMALL LETTER PHI
0x03C7    &b.chi;	      ISOgrk4	# GREEK SMALL LETTER CHI
0x03C7    χ	      ISOgrk3	# GREEK SMALL LETTER CHI
0x03C7    &khgr;	      ISOgrk1	# GREEK SMALL LETTER CHI
0x03C8    &b.psi;	      ISOgrk4	# GREEK SMALL LETTER PSI
0x03C8    &psgr;	      ISOgrk1	# GREEK SMALL LETTER PSI
0x03C8    ψ	      ISOgrk3	# GREEK SMALL LETTER PSI
0x03C9    &ohgr;	      ISOgrk1	# GREEK SMALL LETTER OMEGA
0x03C9    ω	      ISOgrk3	# GREEK SMALL LETTER OMEGA
0x03CA    &idigr;	      ISOgrk2	# GREEK SMALL LETTER IOTA WITH DIALYTIKA
0x03CB    &udigr;	      ISOgrk2	# GREEK SMALL LETTER UPSILON WITH DIALYTIKA
0x03CC    &oacgr;	      ISOgrk2	# GREEK SMALL LETTER OMICRON WITH TONOS
0x03CD    &uacgr;	      ISOgrk2	# GREEK SMALL LETTER UPSILON WITH TONOS
0x03CE    &b.omega;	      ISOgrk4	# GREEK SMALL LETTER OMEGA WITH TONOS
0x03CE    &ohacgr;	      ISOgrk2	# GREEK SMALL LETTER OMEGA WITH TONOS
0x03D1    &b.thetav;	      ISOgrk4	# GREEK THETA SYMBOL
0x03D1    ϑ	      HTMLsymbol	# GREEK THETA SYMBOL
0x03D1    ϑ	      ISOgrk3	# GREEK THETA SYMBOL
0x03D2    ϒ	      HTMLsymbol	# GREEK UPSILON WITH HOOK SYMBOL
0x03D5    &b.phiv;	      ISOgrk4	# GREEK PHI SYMBOL
0x03D5    ϕ	      ISOgrk3	# GREEK PHI SYMBOL
0x03D6    &b.piv;	      ISOgrk4	# GREEK PI SYMBOL
0x03D6    ϖ	      ISOgrk3	# GREEK PI SYMBOL
0x03DC    &b.gammad;	      ISOgrk4	# GREEK LETTER DIGAMMA
0x03DC    ϝ	      ISOgrk3	# GREEK LETTER DIGAMMA
0x03F0    &b.kappav;	      ISOgrk4	# GREEK KAPPA SYMBOL
0x03F0    ϰ	      ISOgrk3	# GREEK KAPPA SYMBOL
0x03F1    &b.rhov;	      ISOgrk4	# GREEK RHO SYMBOL
0x03F1    ϱ	      ISOgrk3	# GREEK RHO SYMBOL
0x0401    Ё	      ISOcyr1	# CYRILLIC CAPITAL LETTER IO
0x0402    Ђ	      ISOcyr2	# CYRILLIC CAPITAL LETTER DJE
0x0403    Ѓ	      ISOcyr2	# CYRILLIC CAPITAL LETTER GJE
0x0404    Є	      ISOcyr2	# CYRILLIC CAPITAL LETTER UKRAINIAN IE
0x0405    Ѕ	      ISOcyr2	# CYRILLIC CAPITAL LETTER DZE
0x0406    І	      ISOcyr2	# CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
0x0407    Ї	      ISOcyr2	# CYRILLIC CAPITAL LETTER YI
0x0408    Ј	      ISOcyr2	# CYRILLIC CAPITAL LETTER JE
0x0409    Љ	      ISOcyr2	# CYRILLIC CAPITAL LETTER LJE
0x040A    Њ	      ISOcyr2	# CYRILLIC CAPITAL LETTER NJE
0x040B    Ћ	      ISOcyr2	# CYRILLIC CAPITAL LETTER TSHE
0x040C    Ќ	      ISOcyr2	# CYRILLIC CAPITAL LETTER KJE
0x040E    Ў	      ISOcyr2	# CYRILLIC CAPITAL LETTER SHORT U
0x040F    Џ	      ISOcyr2	# CYRILLIC CAPITAL LETTER DZHE
0x0410    А	      ISOcyr1	# CYRILLIC CAPITAL LETTER A
0x0411    Б	      ISOcyr1	# CYRILLIC CAPITAL LETTER BE
0x0412    В	      ISOcyr1	# CYRILLIC CAPITAL LETTER VE
0x0413    Г	      ISOcyr1	# CYRILLIC CAPITAL LETTER GHE
0x0414    Д	      ISOcyr1	# CYRILLIC CAPITAL LETTER DE
0x0415    Е	      ISOcyr1	# CYRILLIC CAPITAL LETTER IE
0x0416    Ж	      ISOcyr1	# CYRILLIC CAPITAL LETTER ZHE
0x0417    З	      ISOcyr1	# CYRILLIC CAPITAL LETTER ZE
0x0418    И	      ISOcyr1	# CYRILLIC CAPITAL LETTER I
0x0419    Й	      ISOcyr1	# CYRILLIC CAPITAL LETTER SHORT I
0x041A    К	      ISOcyr1	# CYRILLIC CAPITAL LETTER KA
0x041B    Л	      ISOcyr1	# CYRILLIC CAPITAL LETTER EL
0x041C    М	      ISOcyr1	# CYRILLIC CAPITAL LETTER EM
0x041D    Н	      ISOcyr1	# CYRILLIC CAPITAL LETTER EN
0x041E    О	      ISOcyr1	# CYRILLIC CAPITAL LETTER O
0x041F    П	      ISOcyr1	# CYRILLIC CAPITAL LETTER PE
0x0420    Р	      ISOcyr1	# CYRILLIC CAPITAL LETTER ER
0x0421    С	      ISOcyr1	# CYRILLIC CAPITAL LETTER ES
0x0422    Т	      ISOcyr1	# CYRILLIC CAPITAL LETTER TE
0x0423    У	      ISOcyr1	# CYRILLIC CAPITAL LETTER U
0x0424    Ф	      ISOcyr1	# CYRILLIC CAPITAL LETTER EF
0x0425    Х	      ISOcyr1	# CYRILLIC CAPITAL LETTER HA
0x0426    Ц	      ISOcyr1	# CYRILLIC CAPITAL LETTER TSE
0x0427    Ч	      ISOcyr1	# CYRILLIC CAPITAL LETTER CHE
0x0428    Ш	      ISOcyr1	# CYRILLIC CAPITAL LETTER SHA
0x0429    Щ	      ISOcyr1	# CYRILLIC CAPITAL LETTER SHCHA
0x042A    Ъ	      ISOcyr1	# CYRILLIC CAPITAL LETTER HARD SIGN
0x042B    Ы	      ISOcyr1	# CYRILLIC CAPITAL LETTER YERU
0x042C    Ь	      ISOcyr1	# CYRILLIC CAPITAL LETTER SOFT SIGN
0x042D    Э	      ISOcyr1	# CYRILLIC CAPITAL LETTER E
0x042E    Ю	      ISOcyr1	# CYRILLIC CAPITAL LETTER YU
0x042F    Я	      ISOcyr1	# CYRILLIC CAPITAL LETTER YA
0x0430    а	      ISOcyr1	# CYRILLIC SMALL LETTER A
0x0431    б	      ISOcyr1	# CYRILLIC SMALL LETTER BE
0x0432    в	      ISOcyr1	# CYRILLIC SMALL LETTER VE
0x0433    г	      ISOcyr1	# CYRILLIC SMALL LETTER GHE
0x0434    д	      ISOcyr1	# CYRILLIC SMALL LETTER DE
0x0435    е	      ISOcyr1	# CYRILLIC SMALL LETTER IE
0x0436    ж	      ISOcyr1	# CYRILLIC SMALL LETTER ZHE
0x0437    з	      ISOcyr1	# CYRILLIC SMALL LETTER ZE
0x0438    и	      ISOcyr1	# CYRILLIC SMALL LETTER I
0x0439    й	      ISOcyr1	# CYRILLIC SMALL LETTER SHORT I
0x043A    к	      ISOcyr1	# CYRILLIC SMALL LETTER KA
0x043B    л	      ISOcyr1	# CYRILLIC SMALL LETTER EL
0x043C    м	      ISOcyr1	# CYRILLIC SMALL LETTER EM
0x043D    н	      ISOcyr1	# CYRILLIC SMALL LETTER EN
0x043E    о	      ISOcyr1	# CYRILLIC SMALL LETTER O
0x043F    п	      ISOcyr1	# CYRILLIC SMALL LETTER PE
0x0440    р	      ISOcyr1	# CYRILLIC SMALL LETTER ER
0x0441    с	      ISOcyr1	# CYRILLIC SMALL LETTER ES
0x0442    т	      ISOcyr1	# CYRILLIC SMALL LETTER TE
0x0443    у	      ISOcyr1	# CYRILLIC SMALL LETTER U
0x0444    ф	      ISOcyr1	# CYRILLIC SMALL LETTER EF
0x0445    х	      ISOcyr1	# CYRILLIC SMALL LETTER HA
0x0446    ц	      ISOcyr1	# CYRILLIC SMALL LETTER TSE
0x0447    ч	      ISOcyr1	# CYRILLIC SMALL LETTER CHE
0x0448    ш	      ISOcyr1	# CYRILLIC SMALL LETTER SHA
0x0449    щ	      ISOcyr1	# CYRILLIC SMALL LETTER SHCHA
0x044A    ъ	      ISOcyr1	# CYRILLIC SMALL LETTER HARD SIGN
0x044B    ы	      ISOcyr1	# CYRILLIC SMALL LETTER YERU
0x044C    ь	      ISOcyr1	# CYRILLIC SMALL LETTER SOFT SIGN
0x044D    э	      ISOcyr1	# CYRILLIC SMALL LETTER E
0x044E    ю	      ISOcyr1	# CYRILLIC SMALL LETTER YU
0x044F    я	      ISOcyr1	# CYRILLIC SMALL LETTER YA
0x0451    ё	      ISOcyr1	# CYRILLIC SMALL LETTER IO
0x0452    ђ	      ISOcyr2	# CYRILLIC SMALL LETTER DJE
0x0453    ѓ	      ISOcyr2	# CYRILLIC SMALL LETTER GJE
0x0454    є	      ISOcyr2	# CYRILLIC SMALL LETTER UKRAINIAN IE
0x0455    ѕ	      ISOcyr2	# CYRILLIC SMALL LETTER DZE
0x0456    і	      ISOcyr2	# CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
0x0457    ї	      ISOcyr2	# CYRILLIC SMALL LETTER YI
0x0458    ј	      ISOcyr2	# CYRILLIC SMALL LETTER JE
0x0459    љ	      ISOcyr2	# CYRILLIC SMALL LETTER LJE
0x045A    њ	      ISOcyr2	# CYRILLIC SMALL LETTER NJE
0x045B    ћ	      ISOcyr2	# CYRILLIC SMALL LETTER TSHE
0x045C    ќ	      ISOcyr2	# CYRILLIC SMALL LETTER KJE
0x045E    ў	      ISOcyr2	# CYRILLIC SMALL LETTER SHORT U
0x045F    џ	      ISOcyr2	# CYRILLIC SMALL LETTER DZHE
0x2002     	      ISOpub	# EN SPACE
0x2003     	      ISOpub	# EM SPACE
0x2004     	      ISOpub	# THREE-PER-EM SPACE
0x2005     	      ISOpub	# FOUR-PER-EM SPACE
0x2007     	      ISOpub	# FIGURE SPACE
0x2008     	      ISOpub	# PUNCTUATION SPACE
0x2009     	      ISOpub	# THIN SPACE
0x200A     	      ISOpub	# HAIR SPACE
0x200C    ‌	      HTMLspecial	# ZERO WIDTH NON-JOINER
0x200D    ‍	      HTMLspecial	# ZERO WIDTH JOINER
0x200E    ‎	      HTMLspecial	# LEFT-TO-RIGHT MARK
0x200F    ‏	      HTMLspecial	# RIGHT-TO-LEFT MARK
0x2010    ‐	      ISOpub	# HYPHEN
0x2013    –	      ISOpub	# EN DASH
0x2014    —	      ISOpub	# EM DASH
0x2015    ―	      ISOnum	# HORIZONTAL BAR
0x2016    ‖	      ISOtech	# DOUBLE VERTICAL LINE
0x2018    ‘	      ISOnum	# LEFT SINGLE QUOTATION MARK
0x2018    ’	      ISOpub	# LEFT SINGLE QUOTATION MARK
0x2019    ’	      ISOnum	# RIGHT SINGLE QUOTATION MARK
0x201A    ‚	      ISOpub	# SINGLE LOW-9 QUOTATION MARK
0x201A    ‚	      HTMLspecial	# SINGLE LOW-9 QUOTATION MARK
0x201C    “	      ISOnum	# LEFT DOUBLE QUOTATION MARK
0x201C    ”	      ISOpub	# LEFT DOUBLE QUOTATION MARK
0x201D    ”	      ISOnum	# RIGHT DOUBLE QUOTATION MARK
0x201E    „	      HTMLspecial	# DOUBLE LOW-9 QUOTATION MARK
0x201E    „	      ISOpub	# DOUBLE LOW-9 QUOTATION MARK
0x2020    †	      ISOpub	# DAGGER
0x2021    ‡	      ISOpub	# DOUBLE DAGGER
0x2022    •	      ISOpub	# BULLET
0x2025    ‥	      ISOpub	# TWO DOT LEADER
0x2026    …	      ISOpub	# HORIZONTAL ELLIPSIS
0x2026    …	      ISOpub	# HORIZONTAL ELLIPSIS
0x2030    ‰	      ISOtech	# PER MILLE SIGN
0x2032    ′	      ISOtech	# PRIME
0x2032    &vprime;	      ISOamso	# PRIME
0x2033    ″	      ISOtech	# DOUBLE PRIME
0x2034    ‴	      ISOtech	# TRIPLE PRIME
0x2035    ‵	      ISOamso	# REVERSED PRIME
0x2039    ‹	      HTMLspecial	# SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x203A    ›	      HTMLspecial	# SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x203E    ‾	      HTMLsymbol	# OVERLINE
0x2041    ⁁	      ISOpub	# CARET INSERTION POINT
0x2043    ⁃	      ISOpub	# HYPHEN BULLET
0x2044    ⁄	      HTMLsymbol	# FRACTION SLASH
0x20AC    €	      new       # EURO SIGN
0x20DB    ⃛	      ISOtech	# COMBINING THREE DOTS ABOVE
0x20DC    ⃜	      ISOtech	# COMBINING FOUR DOTS ABOVE
0x2105    ℅	      ISOpub	# CARE OF
0x210B    ℋ	      ISOtech	# SCRIPT CAPITAL H
0x210F    ℏ	      ISOamso	# PLANCK CONSTANT OVER TWO PI
0x2111    ℑ	      ISOamso	# BLACK-LETTER CAPITAL I
0x2112    ℒ	      ISOtech	# SCRIPT CAPITAL L
0x2113    ℓ	      ISOamso	# SCRIPT SMALL L
0x2116    №	      ISOcyr1	# NUMERO SIGN
0x2117    ℗	      ISOpub	# SOUND RECORDING COPYRIGHT
0x2118    ℘	      ISOamso	# SCRIPT CAPITAL P
0x211C    ℜ	      ISOamso	# BLACK-LETTER CAPITAL R
0x211E    ℞	      ISOpub	# PRESCRIPTION TAKE
0x2122    ™	      ISOnum	# TRADE MARK SIGN
0x2126    Ω	      ISOnum	# OHM SIGN
0x212B    Å	      ISOtech	# ANGSTROM SIGN
0x212C    ℬ	      ISOtech	# SCRIPT CAPITAL B
0x2133    ℳ	      ISOtech	# SCRIPT CAPITAL M
0x2134    ℴ	      ISOtech	# SCRIPT SMALL O
0x2135    ℵ	      HTMLsymbol	# ALEF SYMBOL
0x2135    ℵ	      ISOtech	# ALEF SYMBOL
0x2136    ℶ	      ISOamso	# BET SYMBOL
0x2137    ℷ	      ISOamso	# GIMEL SYMBOL
0x2138    ℸ	      ISOamso	# DALET SYMBOL
0x2153    ⅓	      ISOpub	# VULGAR FRACTION ONE THIRD
0x2154    ⅔	      ISOpub	# VULGAR FRACTION TWO THIRDS
0x2155    ⅕	      ISOpub	# VULGAR FRACTION ONE FIFTH
0x2156    ⅖	      ISOpub	# VULGAR FRACTION TWO FIFTHS
0x2157    ⅗	      ISOpub	# VULGAR FRACTION THREE FIFTHS
0x2158    ⅘	      ISOpub	# VULGAR FRACTION FOUR FIFTHS
0x2159    ⅙	      ISOpub	# VULGAR FRACTION ONE SIXTH
0x215A    ⅚	      ISOpub	# VULGAR FRACTION FIVE SIXTHS
0x215B    ⅛	      ISOnum	# VULGAR FRACTION ONE EIGHTH
0x215C    ⅜	      ISOnum	# VULGAR FRACTION THREE EIGHTHS
0x215D    ⅝	      ISOnum	# VULGAR FRACTION FIVE EIGHTHS
0x215E    ⅞	      ISOnum	# VULGAR FRACTION SEVEN EIGHTHS
0x2190    ←	      ISOnum	# LEFTWARDS ARROW
0x2191    ↑	      ISOnum	# UPWARDS ARROW
0x2192    →	      ISOnum	# RIGHTWARDS ARROW
0x2193    ↓	      ISOnum	# DOWNWARDS ARROW
0x2194    ↔	      ISOamsa	# LEFT RIGHT ARROW
0x2194    ⟺	      ISOamsa	# LEFT RIGHT ARROW
0x2194    ⟷	      ISOamsa	# LEFT RIGHT ARROW
0x2195    ↕	      ISOamsa	# UP DOWN ARROW
0x2196    ↖	      ISOamsa	# NORTH WEST ARROW
0x2197    ↗	      ISOamsa	# NORTH EAST ARROW
0x2198    &drarr;	      ISOamsa	# SOUTH EAST ARROW
0x2199    &dlarr;	      ISOamsa	# SOUTH WEST ARROW
0x219A    ↚	      ISOamsa	# LEFTWARDS ARROW WITH STROKE
0x219B    ↛	      ISOamsa	# RIGHTWARDS ARROW WITH STROKE
0x219D    ↝	      ISOamsa	# RIGHTWARDS WAVE ARROW
0x219E    ↞	      ISOamsa	# LEFTWARDS TWO HEADED ARROW
0x21A0    ↠	      ISOamsa	# RIGHTWARDS TWO HEADED ARROW
0x21A2    ↢	      ISOamsa	# LEFTWARDS ARROW WITH TAIL
0x21A3    ↣	      ISOamsa	# RIGHTWARDS ARROW WITH TAIL
0x21A6    ↦	      ISOamsa	# RIGHTWARDS ARROW FROM BAR
0x21A9    ↩	      ISOamsa	# LEFTWARDS ARROW WITH HOOK
0x21AA    ↪	      ISOamsa	# RIGHTWARDS ARROW WITH HOOK
0x21AB    ↫	      ISOamsa	# LEFTWARDS ARROW WITH LOOP
0x21AC    ↬	      ISOamsa	# RIGHTWARDS ARROW WITH LOOP
0x21AD    ↭	      ISOamsa	# LEFT RIGHT WAVE ARROW
0x21AE    ↮	      ISOamsa	# LEFT RIGHT ARROW WITH STROKE
0x21B0    ↰	      ISOamsa	# UPWARDS ARROW WITH TIP LEFTWARDS
0x21B1    ↱	      ISOamsa	# UPWARDS ARROW WITH TIP RIGHTWARDS
0x21B5    ↵	      HTMLsymbol	# DOWNWARDS ARROW WITH CORNER LEFTWARDS
0x21B6    ↶	      ISOamsa	# ANTICLOCKWISE TOP SEMICIRCLE ARROW
0x21B7    ↷	      ISOamsa	# CLOCKWISE TOP SEMICIRCLE ARROW
0x21BA    ↺	      ISOamsa	# ANTICLOCKWISE OPEN CIRCLE ARROW
0x21BB    ↻	      ISOamsa	# CLOCKWISE OPEN CIRCLE ARROW
0x21BC    ↼	      ISOamsa	# LEFTWARDS HARPOON WITH BARB UPWARDS
0x21BD    ↽	      ISOamsa	# LEFTWARDS HARPOON WITH BARB DOWNWARDS
0x21BE    ↾	      ISOamsa	# UPWARDS HARPOON WITH BARB RIGHTWARDS
0x21BF    ↿	      ISOamsa	# UPWARDS HARPOON WITH BARB LEFTWARDS
0x21C0    ⇀	      ISOamsa	# RIGHTWARDS HARPOON WITH BARB UPWARDS
0x21C1    ⇁	      ISOamsa	# RIGHTWARDS HARPOON WITH BARB DOWNWARDS
0x21C2    ⇂	      ISOamsa	# DOWNWARDS HARPOON WITH BARB RIGHTWARDS
0x21C3    ⇃	      ISOamsa	# DOWNWARDS HARPOON WITH BARB LEFTWARDS
0x21C4    &rlarr2;	      ISOamsa	# RIGHTWARDS ARROW OVER LEFTWARDS ARROW
0x21C6    &lrarr2;	      ISOamsa	# LEFTWARDS ARROW OVER RIGHTWARDS ARROW
0x21C7    &larr2;	      ISOamsa	# LEFTWARDS PAIRED ARROWS
0x21C8    &uarr2;	      ISOamsa	# UPWARDS PAIRED ARROWS
0x21C9    &rarr2;	      ISOamsa	# RIGHTWARDS PAIRED ARROWS
0x21CA    &darr2;	      ISOamsa	# DOWNWARDS PAIRED ARROWS
0x21CB    &lrhar2;	      ISOamsa	# LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
0x21CC    &rlhar2;	      ISOamsa	# RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
0x21CD    ⇍	      ISOamsa	# LEFTWARDS DOUBLE ARROW WITH STROKE
0x21CE    ⇎	      ISOamsa	# LEFT RIGHT DOUBLE ARROW WITH STROKE
0x21CF    ⇏	      ISOamsa	# RIGHTWARDS DOUBLE ARROW WITH STROKE
0x21D0    ⇐	      ISOtech	# LEFTWARDS DOUBLE ARROW
0x21D0    ⟸	      ISOamsa	# LEFTWARDS DOUBLE ARROW
0x21D1    ⇑	      ISOamsa	# UPWARDS DOUBLE ARROW
0x21D2    ⇒	      ISOtech	# RIGHTWARDS DOUBLE ARROW
0x21D2    ⟹	      ISOamsa	# RIGHTWARDS DOUBLE ARROW
0x21D3    ⇓	      ISOamsa	# DOWNWARDS DOUBLE ARROW
0x21D4    ⇔	      ISOamsa	# LEFT RIGHT DOUBLE ARROW
0x21D4    ⇔	      ISOtech	# LEFT RIGHT DOUBLE ARROW
0x21D5    ⇕	      ISOamsa	# UP DOWN DOUBLE ARROW
0x21DA    ⇚	      ISOamsa	# LEFTWARDS TRIPLE ARROW
0x21DB    ⇛	      ISOamsa	# RIGHTWARDS TRIPLE ARROW
0x2200    ∀	      ISOtech	# FOR ALL
0x2201    ∁	      ISOamso	# COMPLEMENT
0x2202    ∂	      ISOtech	# PARTIAL DIFFERENTIAL
0x2203    ∃	      ISOtech	# THERE EXISTS
0x2204    ∄	      ISOamso	# THERE DOES NOT EXIST
0x2205    ∅	      ISOamso	# EMPTY SET
0x2207    ∇	      ISOtech	# NABLA
0x2208    ∈	      ISOtech	# ELEMENT OF
0x2209    ∉	      ISOtech	# NOT AN ELEMENT OF
0x220A    &epsis;	      ISOgrk3	# SMALL ELEMENT OF
0x220B    ∋	      ISOtech	# CONTAINS AS MEMBER
0x220D    ϶	      ISOamsr	# SMALL CONTAINS AS MEMBER
0x220F    ∏	      ISOamsb	# N-ARY PRODUCT
0x2210    ⨿	      ISOamsb	# N-ARY COPRODUCT
0x2210    ∐	      ISOamsb	# N-ARY COPRODUCT
0x2210    &samalg;	      ISOamsr	# N-ARY COPRODUCT
0x2211    ∑	      ISOamsb	# N-ARY SUMMATION
0x2212    −	      ISOtech	# MINUS SIGN
0x2213    ∓	      ISOtech	# MINUS-OR-PLUS SIGN
0x2214    ∔	      ISOamsb	# DOT PLUS
0x2216    ∖	      ISOamsb	# SET MINUS
0x2216    ∖	      ISOamsb	# SET MINUS
0x2217    ∗	      ISOtech	# ASTERISK OPERATOR
0x2218    ∘	      ISOtech	# RING OPERATOR
0x221A    √	      ISOtech	# SQUARE ROOT
0x221D    ∝	      ISOtech	# PROPORTIONAL TO
0x221D    ∝	      ISOamsr	# PROPORTIONAL TO
0x221E    ∞	      ISOtech	# INFINITY
0x221F    &ang90;	      ISOtech	# RIGHT ANGLE
0x2220    ∠	      ISOamso	# ANGLE
0x2221    ∡	      ISOamso	# MEASURED ANGLE
0x2222    ∢	      ISOtech	# SPHERICAL ANGLE
0x2223    ∣	      ISOamsr	# DIVIDES
0x2224    ∤	      ISOamsn	# DOES NOT DIVIDE
0x2225    ∥	      ISOtech	# PARALLEL TO
0x2225    ∥	      ISOamsr	# PARALLEL TO
0x2226    ∦	      ISOamsn	# NOT PARALLEL TO
0x2226    ∦	      ISOamsn	# NOT PARALLEL TO
0x2227    ∧	      ISOtech	# LOGICAL AND
0x2228    ∨	      ISOtech	# LOGICAL OR
0x2229    ∩	      ISOtech	# INTERSECTION
0x222A    ∪	      ISOtech	# UNION
0x222B    ∫	      ISOtech	# INTEGRAL
0x222E    ∮	      ISOtech	# CONTOUR INTEGRAL
0x2234    ∴	      ISOtech	# THEREFORE
0x2235    ∵	      ISOtech	# BECAUSE
0x223C    ∼	      ISOtech	# TILDE OPERATOR
0x223C    ∼	      ISOamsr	# TILDE OPERATOR
0x223D    ∽	      ISOamsr	# REVERSED TILDE
0x2240    ≀	      ISOamsb	# WREATH PRODUCT
0x2241    ≁	      ISOamsn	# NOT TILDE
0x2243    ≃	      ISOtech	# ASYMPTOTICALLY EQUAL TO
0x2244    ≄	      ISOamsn	# NOT ASYMPTOTICALLY EQUAL TO
0x2245    ≅	      ISOtech	# APPROXIMATELY EQUAL TO
0x2247    ≇	      ISOamsn	# NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
0x2248    ≈	      ISOtech	# ALMOST EQUAL TO
0x2248    ≈	      ISOamsr	# ALMOST EQUAL TO
0x2248    ≈	      ISOamsr	# ALMOST EQUAL TO
0x2249    ≉	      ISOamsn	# NOT ALMOST EQUAL TO
0x224A    ≊	      ISOamsr	# ALMOST EQUAL OR EQUAL TO
0x224C    ≌	      ISOamsr	# ALL EQUAL TO
0x224E    ≎	      ISOamsr	# GEOMETRICALLY EQUIVALENT TO
0x224F    ≏	      ISOamsr	# DIFFERENCE BETWEEN
0x2250    ≐	      ISOamsr	# APPROACHES THE LIMIT
0x2251    ≑	      ISOamsr	# GEOMETRICALLY EQUAL TO
0x2252    ≒	      ISOamsr	# APPROXIMATELY EQUAL TO OR THE IMAGE OF
0x2253    ≓	      ISOamsr	# IMAGE OF OR APPROXIMATELY EQUAL TO
0x2254    ≔	      ISOamsr	# COLON EQUALS
0x2255    ≕	      ISOamsr	# EQUALS COLON
0x2256    ≖	      ISOamsr	# RING IN EQUAL TO
0x2257    ≗	      ISOamsr	# RING EQUAL TO
0x2259    ≙	      ISOtech	# ESTIMATES
0x225C    ≜	      ISOamsr	# DELTA EQUAL TO
0x2260    ≠	      ISOtech	# NOT EQUAL TO
0x2261    ≡	      ISOtech	# IDENTICAL TO
0x2262    ≢	      ISOamsn	# NOT IDENTICAL TO
0x2264    ≤	      ISOtech	# LESS-THAN OR EQUAL TO
0x2264    ⩽	      ISOamsr	# LESS-THAN OR EQUAL TO
0x2265    ≥	      ISOtech	# GREATER-THAN OR EQUAL TO
0x2265    ⩾	      ISOamsr	# GREATER-THAN OR EQUAL TO
0x2266    ≦	      ISOamsr	# LESS-THAN OVER EQUAL TO
0x2267    ≧	      ISOamsr	# GREATER-THAN OVER EQUAL TO
0x2268    ≨	      ISOamsn	# LESS-THAN BUT NOT EQUAL TO
0x2268    ⪇	      ISOamsn	# LESS-THAN BUT NOT EQUAL TO
0x2268    ≨︀	      ISOamsn	# LESS-THAN BUT NOT EQUAL TO
0x2269    ≩	      ISOamsn	# GREATER-THAN BUT NOT EQUAL TO
0x2269    ⪈	      ISOamsn	# GREATER-THAN BUT NOT EQUAL TO
0x2269    ≩︀	      ISOamsn	# GREATER-THAN BUT NOT EQUAL TO
0x226A    ≪	      ISOamsr	# MUCH LESS-THAN
0x226B    ≫	      ISOamsr	# MUCH GREATER-THAN
0x226C    ≬	      ISOamsr	# BETWEEN
0x226E    ≮	      ISOamsn	# NOT LESS-THAN
0x226F    ≯	      ISOamsn	# NOT GREATER-THAN
0x2270    ≰	      ISOamsn	# NEITHER LESS-THAN NOR EQUAL TO
0x2270    ⩽̸	      ISOamsn	# NEITHER LESS-THAN NOR EQUAL TO
0x2271    ≱	      ISOamsn	# NEITHER GREATER-THAN NOR EQUAL TO
0x2271    ⩾̸	      ISOamsn	# NEITHER GREATER-THAN NOR EQUAL TO
0x2272    ≲	      ISOamsr	# LESS-THAN OR EQUIVALENT TO
0x2273    ≳	      ISOamsr	# GREATER-THAN OR EQUIVALENT TO
0x2276    ≶	      ISOamsr	# LESS-THAN OR GREATER-THAN
0x2277    ≷	      ISOamsr	# GREATER-THAN OR LESS-THAN
0x227A    ≺	      ISOamsr	# PRECEDES
0x227B    ≻	      ISOamsr	# SUCCEEDS
0x227C    &cupre;	      ISOamsr	# PRECEDES OR EQUAL TO
0x227C    ⪯	      ISOamsr	# PRECEDES OR EQUAL TO
0x227D    ≽	      ISOamsr	# SUCCEEDS OR EQUAL TO
0x227D    ⪰	      ISOamsr	# SUCCEEDS OR EQUAL TO
0x227E    ≾	      ISOamsr	# PRECEDES OR EQUIVALENT TO
0x227F    ≿	      ISOamsr	# SUCCEEDS OR EQUIVALENT TO
0x2280    ⊀	      ISOamsn	# DOES NOT PRECEDE
0x2281    ⊁	      ISOamsn	# DOES NOT SUCCEED
0x2282    ⊂	      ISOtech	# SUBSET OF
0x2283    ⊃	      ISOtech	# SUPERSET OF
0x2284    ⊄	      ISOamsn	# NOT A SUBSET OF
0x2285    ⊅	      ISOamsn	# NOT A SUPERSET OF
0x2286    ⫅	      ISOamsr	# SUBSET OF OR EQUAL TO
0x2286    ⊆	      ISOtech	# SUBSET OF OR EQUAL TO
0x2287    ⫆	      ISOamsr	# SUPERSET OF OR EQUAL TO
0x2287    ⊇	      ISOtech	# SUPERSET OF OR EQUAL TO
0x2288    ⫅̸	      ISOamsn	# NEITHER A SUBSET OF NOR EQUAL TO
0x2288    ⊈	      ISOamsn	# NEITHER A SUBSET OF NOR EQUAL TO
0x2289    ⫆̸	      ISOamsn	# NEITHER A SUPERSET OF NOR EQUAL TO
0x2289    ⊉	      ISOamsn	# NEITHER A SUPERSET OF NOR EQUAL TO
0x228A    ⫋	      ISOamsn	# SUBSET OF WITH NOT EQUAL TO
0x228A    ⊊	      ISOamsn	# SUBSET OF WITH NOT EQUAL TO
0x228A    ⫋︀	      ISOamsn	# SUBSET OF WITH NOT EQUAL TO
0x228A    ⊊︀	      ISOamsn	# SUBSET OF WITH NOT EQUAL TO
0x228B    ⫌	      ISOamsn	# SUPERSET OF WITH NOT EQUAL TO
0x228B    ⊋	      ISOamsn	# SUPERSET OF WITH NOT EQUAL TO
0x228B    ⫌︀	      ISOamsn	# SUPERSET OF WITH NOT EQUAL TO
0x228B    ⊋︀	      ISOamsn	# SUPERSET OF WITH NOT EQUAL TO
0x228E    ⊎	      ISOamsb	# MULTISET UNION
0x228F    ⊏	      ISOamsr	# SQUARE IMAGE OF
0x2290    ⊐	      ISOamsr	# SQUARE ORIGINAL OF
0x2291    ⊑	      ISOamsr	# SQUARE IMAGE OF OR EQUAL TO
0x2292    ⊒	      ISOamsr	# SQUARE ORIGINAL OF OR EQUAL TO
0x2293    ⊓	      ISOamsb	# SQUARE CAP
0x2294    ⊔	      ISOamsb	# SQUARE CUP
0x2295    ⊕	      ISOamsb	# CIRCLED PLUS
0x2296    ⊖	      ISOamsb	# CIRCLED MINUS
0x2297    ⊗	      ISOamsb	# CIRCLED TIMES
0x2298    ⊘	      ISOamsb	# CIRCLED DIVISION SLASH
0x2299    ⊙	      ISOamsb	# CIRCLED DOT OPERATOR
0x229A    ⊚	      ISOamsb	# CIRCLED RING OPERATOR
0x229B    ⊛	      ISOamsb	# CIRCLED ASTERISK OPERATOR
0x229D    ⊝	      ISOamsb	# CIRCLED DASH
0x229E    ⊞	      ISOamsb	# SQUARED PLUS
0x229F    ⊟	      ISOamsb	# SQUARED MINUS
0x22A0    ⊠	      ISOamsb	# SQUARED TIMES
0x22A1    ⊡	      ISOamsb	# SQUARED DOT OPERATOR
0x22A2    ⊢	      ISOamsr	# RIGHT TACK
0x22A3    ⊣	      ISOamsr	# LEFT TACK
0x22A4    ⊤	      ISOamsb	# DOWN TACK
0x22A5    ⊥	      ISOtech	# UP TACK
0x22A5    ⊥	      ISOtech	# UP TACK
0x22A7    ⊧	      ISOamsr	# MODELS
0x22A8    ⊨	      ISOamsr	# TRUE
0x22A9    ⊩	      ISOamsr	# FORCES
0x22AA    ⊪	      ISOamsr	# TRIPLE VERTICAL BAR RIGHT TURNSTILE
0x22AC    ⊬	      ISOamsn	# DOES NOT PROVE
0x22AD    ⊭	      ISOamsn	# NOT TRUE
0x22AE    ⊮	      ISOamsn	# DOES NOT FORCE
0x22AF    ⊯	      ISOamsn	# NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT
0x22B2    ⊲	      ISOamsr	# NORMAL SUBGROUP OF
0x22B3    ⊳	      ISOamsr	# CONTAINS AS NORMAL SUBGROUP
0x22B4    ⊴	      ISOamsr	# NORMAL SUBGROUP OF OR EQUAL TO
0x22B5    ⊵	      ISOamsr	# CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
0x22B8    ⊸	      ISOamsa	# MULTIMAP
0x22BA    ⊺	      ISOamsb	# INTERCALATE
0x22BB    ⊻	      ISOamsr	# XOR
0x22BC    ⌅	      ISOamsb	# NAND
0x22C4    ⋄	      ISOamsb	# DIAMOND OPERATOR
0x22C5    ⋅	      ISOamsb	# DOT OPERATOR
0x22C6    ⋆	      ISOamsb	# STAR OPERATOR
0x22C7    ⋇	      ISOamsb	# DIVISION TIMES
0x22C8    ⋈	      ISOamsr	# BOWTIE
0x22C9    ⋉	      ISOamsb	# LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
0x22CA    ⋊	      ISOamsb	# RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
0x22CB    ⋋	      ISOamsb	# LEFT SEMIDIRECT PRODUCT
0x22CC    ⋌	      ISOamsb	# RIGHT SEMIDIRECT PRODUCT
0x22CD    ⋍	      ISOamsr	# REVERSED TILDE EQUALS
0x22CE    ⋎	      ISOamsb	# CURLY LOGICAL OR
0x22CF    ⋏	      ISOamsb	# CURLY LOGICAL AND
0x22D0    ⋐	      ISOamsr	# DOUBLE SUBSET
0x22D1    ⋑	      ISOamsr	# DOUBLE SUPERSET
0x22D2    ⋒	      ISOamsb	# DOUBLE INTERSECTION
0x22D3    ⋓	      ISOamsb	# DOUBLE UNION
0x22D4    ⋔	      ISOamsr	# PITCHFORK
0x22D6    &ldot;	      ISOamsr	# LESS-THAN WITH DOT
0x22D7    &gsdot;	      ISOamsr	# GREATER-THAN WITH DOT
0x22D8    ⋘	      ISOamsr	# VERY MUCH LESS-THAN
0x22D9    ⋙	      ISOamsr	# VERY MUCH GREATER-THAN
0x22DA    ⋚	      ISOamsr	# LESS-THAN EQUAL TO OR GREATER-THAN
0x22DB    ⋛	      ISOamsr	# GREATER-THAN EQUAL TO OR LESS-THAN
0x22DC    ⪕	      ISOamsr	# EQUAL TO OR LESS-THAN
0x22DD    ⪖	      ISOamsr	# EQUAL TO OR GREATER-THAN
0x22DE    ⋞	      ISOamsr	# EQUAL TO OR PRECEDES
0x22DF    ⋟	      ISOamsr	# EQUAL TO OR SUCCEEDS
0x22E0    ⪯̸	      ISOamsn	# DOES NOT PRECEDE OR EQUAL
0x22E1    ⪰̸	      ISOamsn	# DOES NOT SUCCEED OR EQUAL
0x22E6    ⋦	      ISOamsn	# LESS-THAN BUT NOT EQUIVALENT TO
0x22E7    ⋧	      ISOamsn	# GREATER-THAN BUT NOT EQUIVALENT TO
0x22E8    ⋨	      ISOamsn	# PRECEDES BUT NOT EQUIVALENT TO
0x22E9    ⋩	      ISOamsn	# SUCCEEDS BUT NOT EQUIVALENT TO
0x22EA    ⋪	      ISOamsn	# NOT NORMAL SUBGROUP OF
0x22EB    ⋫	      ISOamsn	# DOES NOT CONTAIN AS NORMAL SUBGROUP
0x22EC    ⋬	      ISOamsn	# NOT NORMAL SUBGROUP OF OR EQUAL TO
0x22ED    ⋭	      ISOamsn	# DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
0x22EE    ⋮	      ISOpub	# VERTICAL ELLIPSIS
0x2306    ⌆	      ISOamsb	# PERSPECTIVE
0x2308    ⌈	      ISOamsc	# LEFT CEILING
0x2309    ⌉	      ISOamsc	# RIGHT CEILING
0x230A    ⌊	      ISOamsc	# LEFT FLOOR
0x230B    ⌋	      ISOamsc	# RIGHT FLOOR
0x230C    ⌌	      ISOpub	# BOTTOM RIGHT CROP
0x230D    ⌍	      ISOpub	# BOTTOM LEFT CROP
0x230E    ⌎	      ISOpub	# TOP RIGHT CROP
0x230F    ⌏	      ISOpub	# TOP LEFT CROP
0x2315    ⌕	      ISOpub	# TELEPHONE RECORDER
0x2316    ⌖	      ISOpub	# POSITION INDICATOR
0x231C    ⌜	      ISOamsc	# TOP LEFT CORNER
0x231D    ⌝	      ISOamsc	# TOP RIGHT CORNER
0x231E    ⌞	      ISOamsc	# BOTTOM LEFT CORNER
0x231F    ⌟	      ISOamsc	# BOTTOM RIGHT CORNER
0x2322    ⌢	      ISOamsr	# FROWN
0x2322    ⌢	      ISOamsr	# FROWN
0x2323    ⌣	      ISOamsr	# SMILE
0x2323    ⌣	      ISOamsr	# SMILE
0x2329    ⟨	      ISOtech	# LEFT-POINTING ANGLE BRACKET
0x232A    ⟩	      ISOtech	# RIGHT-POINTING ANGLE BRACKET
0x2423    ␣	      ISOpub	# OPEN BOX
0x24C8    Ⓢ	      ISOamso	# CIRCLED LATIN CAPITAL LETTER S
0x2500    ─	      ISObox	# BOX DRAWINGS LIGHT HORIZONTAL
0x2502    │	      ISObox	# BOX DRAWINGS LIGHT VERTICAL
0x250C    ┌	      ISObox	# BOX DRAWINGS LIGHT DOWN AND RIGHT
0x2510    ┐	      ISObox	# BOX DRAWINGS LIGHT DOWN AND LEFT
0x2514    └	      ISObox	# BOX DRAWINGS LIGHT UP AND RIGHT
0x2518    ┘	      ISObox	# BOX DRAWINGS LIGHT UP AND LEFT
0x251C    ├	      ISObox	# BOX DRAWINGS LIGHT VERTICAL AND RIGHT
0x2524    ┤	      ISObox	# BOX DRAWINGS LIGHT VERTICAL AND LEFT
0x252C    ┬	      ISObox	# BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
0x2534    ┴	      ISObox	# BOX DRAWINGS LIGHT UP AND HORIZONTAL
0x253C    ┼	      ISObox	# BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
0x2550    ═	      ISObox	# BOX DRAWINGS DOUBLE HORIZONTAL
0x2551    ║	      ISObox	# BOX DRAWINGS DOUBLE VERTICAL
0x2552    ╒	      ISObox	# BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
0x2553    ╓	      ISObox	# BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
0x2554    ╔	      ISObox	# BOX DRAWINGS DOUBLE DOWN AND RIGHT
0x2555    ╕	      ISObox	# BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
0x2556    ╖	      ISObox	# BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
0x2557    ╗	      ISObox	# BOX DRAWINGS DOUBLE DOWN AND LEFT
0x2558    ╘	      ISObox	# BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
0x2559    ╙	      ISObox	# BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
0x255A    ╚	      ISObox	# BOX DRAWINGS DOUBLE UP AND RIGHT
0x255B    ╛	      ISObox	# BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
0x255C    ╜	      ISObox	# BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
0x255D    ╝	      ISObox	# BOX DRAWINGS DOUBLE UP AND LEFT
0x255E    ╞	      ISObox	# BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
0x255F    ╟	      ISObox	# BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
0x2560    ╠	      ISObox	# BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
0x2561    ╡	      ISObox	# BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
0x2562    ╢	      ISObox	# BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
0x2563    ╣	      ISObox	# BOX DRAWINGS DOUBLE VERTICAL AND LEFT
0x2564    ╤	      ISObox	# BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
0x2565    ╥	      ISObox	# BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
0x2566    ╦	      ISObox	# BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
0x2567    ╧	      ISObox	# BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
0x2568    ╨	      ISObox	# BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
0x2569    ╩	      ISObox	# BOX DRAWINGS DOUBLE UP AND HORIZONTAL
0x256A    ╪	      ISObox	# BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
0x256B    ╫	      ISObox	# BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
0x256C    ╬	      ISObox	# BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
0x2580    ▀	      ISOpub	# UPPER HALF BLOCK
0x2584    ▄	      ISOpub	# LOWER HALF BLOCK
0x2588    █	      ISOpub	# FULL BLOCK
0x2591    ░	      ISOpub	# LIGHT SHADE
0x2592    ▒	      ISOpub	# MEDIUM SHADE
0x2593    ▓	      ISOpub	# DARK SHADE
0x25A1    □	      ISOpub	# WHITE SQUARE
0x25A1    □	      ISOtech	# WHITE SQUARE
0x25AA    ▪	      ISOpub	# BLACK SMALL SQUARE
0x25AD    ▭	      ISOpub	# WHITE RECTANGLE
0x25AE    ▮	      ISOpub	# BLACK VERTICAL RECTANGLE
0x25B3    △	      ISOamsb	# WHITE UP-POINTING TRIANGLE
0x25B4    ▴	      ISOpub	# BLACK UP-POINTING SMALL TRIANGLE
0x25B5    ▵	      ISOpub	# WHITE UP-POINTING SMALL TRIANGLE
0x25B8    ▸	      ISOpub	# BLACK RIGHT-POINTING SMALL TRIANGLE
0x25B9    ▹	      ISOpub	# WHITE RIGHT-POINTING SMALL TRIANGLE
0x25BD    ▽	      ISOamsb	# WHITE DOWN-POINTING TRIANGLE
0x25BE    ▾	      ISOpub	# BLACK DOWN-POINTING SMALL TRIANGLE
0x25BF    ▿	      ISOpub	# WHITE DOWN-POINTING SMALL TRIANGLE
0x25C2    ◂	      ISOpub	# BLACK LEFT-POINTING SMALL TRIANGLE
0x25C3    ◃	      ISOpub	# WHITE LEFT-POINTING SMALL TRIANGLE
0x25CA    ◊	      ISOpub	# LOZENGE
0x25CB    ○	      ISOpub	# WHITE CIRCLE
0x25CB    ◯	      ISOamsb	# WHITE CIRCLE
0x2605    ★	      ISOpub	# BLACK STAR
0x2606    ☆	      ISOpub	# WHITE STAR
0x260E    ☎	      ISOpub	# BLACK TELEPHONE
0x2640    ♀	      ISOpub	# FEMALE SIGN
0x2642    ♂	      ISOpub	# MALE SIGN
0x2660    ♠	      ISOpub	# BLACK SPADE SUIT
0x2663    ♣	      ISOpub	# BLACK CLUB SUIT
0x2665    ♥	      ISOpub	# BLACK HEART SUIT
0x2666    ♦	      ISOpub	# BLACK DIAMOND SUIT
0x266A    ♪	      ISOnum	# EIGHTH NOTE
0x266D    ♭	      ISOpub	# MUSIC FLAT SIGN
0x266E    ♮	      ISOpub	# MUSIC NATURAL SIGN
0x266F    ♯	      ISOpub	# MUSIC SHARP SIGN
0x2713    ✓	      ISOpub	# CHECK MARK
0x2717    ✗	      ISOpub	# BALLOT X
0x2720    ✠	      ISOpub	# MALTESE CROSS
0x2726    ⧫	      ISOpub	# BLACK FOUR POINTED STAR

0x2736    ✶	      ISOpub	# SIX POINTED BLACK STAR
0x????    ϵ	      ISOgrk3	# variant epsilon
0x????    fj	      ISOpub	# fj ligature
0x????    ⪌	      ISOamsr	# greater-than, double equals, less-than
0x????    ⪆	      ISOamsr	# greater-than, approximately equal to
0x????    ⪊	      ISOamsn	# greater-than, not approximately equal to
0x????    &jnodot;	      ISOamso	# latin small letter dotless j
0x????    ⪋	      ISOamsr	# less-than, double equals, greater-than
0x????    ⪅	      ISOamsr	# less-than, approximately equal to
0x????    ⪉	      ISOamsn	# less-than, not approximately equal to
0x????    &lpargt;	      ISOamsc	# left parenthesis, greater-than
0x????    ≧̸	      ISOamsn	# not greater-than, double equals
0x????    ≦̸	      ISOamsn	# not less-than, double equals
0x????    ∤	      ISOamsn	# nshortmid
0x????    ⪷	      ISOamsr	# precedes, approximately equal to
0x????    ⪵	      ISOamsn	# precedes, not double equal
0x????    ⪹	      ISOamsn	# precedes, not approximately equal to
0x????    ⦔	      ISOamsc	# right parenthesis, greater-than
0x????    ⪸	      ISOamsr	# succeeds, approximately equal to
0x????    ⪶	      ISOamsn	# succeeds, not double equals
0x????    ⪺	      ISOamsn	# succeeds, not approximately equal to
0x????    ∣	      ISOamsr	# shortmid
0xFB00    ff	      ISOpub	# LATIN SMALL LIGATURE FF
0xFB01    fi	      ISOpub	# LATIN SMALL LIGATURE FI
0xFB02    fl	      ISOpub	# LATIN SMALL LIGATURE FL
0xFB03    ffi	      ISOpub	# LATIN SMALL LIGATURE FFI
0xFB04    ffl	      ISOpub	# LATIN SMALL LIGATURE FFL

lynx2-8-8/test/spaces.html000644 023711 023712 00000005054 10164770217 016107 0ustar00dickeylynx000000 000000 Test of some symbols You may press '\' to view the source of this test
UNICODE NCR alt-NCR named alt-named

0x2000 [ ] [ ] # EN QUAD
0x2001 [ ] [ ] # EM QUAD
0x2002 [ ] [ ] [ ] [ ] # EN SPACE
0x2003 [ ] [ ] [ ] [ ] # EM SPACE
0x2004 [ ] [ ] [ ] [ ] # THREE-PER-EM SPACE
0x2005 [ ] [ ] [ ] [ ] # FOUR-PER-EM SPACE
0x2007 [ ] [ ] [ ] [ ] # FIGURE SPACE
0x2008 [ ] [ ] [ ] [ ] # PUNCTUATION SPACE
0x2009 [ ] [ ] [ ] [ ] # THIN SPACE
0x200A [ ] [ ] [ ] [ ] # HAIR SPACE
0x200C [‌] [‌] [‌] [‌] # ZERO WIDTH NON-JOINER
0x200D [‍] [‍] [‍] [‍] # ZERO WIDTH JOINER
0x200E [‎] [‎] [‎] [‎] # LEFT-TO-RIGHT MARK
0x200F [‏] [‏] [‏] [‏] # RIGHT-TO-LEFT MARK
0x2010 [‐] [‐] [‐] [‐] # HYPHEN
0x2013 [–] [–] [–] [–] # EN DASH
0x2014 [—] [—] [—] [—] # EM DASH
lynx2-8-8/test/special_urls.html000644 023711 023712 00000001553 10164770217 017316 0ustar00dickeylynx000000 000000 Lynx Special URLs

Lynx Special URLs

LYNXCFG:LYNXCFG (ok)
LYNXCOMPILEOPTS:LYNXCOMPILEOPTS (ok)
LYNXCOOKIE:LYNXCOOKIE is not allowed
LYNXDIRED:LYNXDIRED is not allowed
LYNXDOWNLOAD:LYNXDOWNLOAD is not allowed
LYNXHIST:LYNXHIST is not allowed
LYNXIMGMAP:LYNXIMGMAP is not allowed
LYNXKEYMAP:LYNXKEYMAP (ok)
LYNXMESSAGES:LYNXMESSAGES (ok)
LYNXOPTIONS:LYNXOPTIONS (ok)
LYNXPRINT:LYNXPRINT is not allowed
lynx2-8-8/test/tabtest.html000644 023711 023712 00000002607 10054226354 016274 0ustar00dickeylynx000000 000000 Tests of TAB element.

Tests of TAB element.

Normal Style:
OneTwoThree FourFive SixSeven Eight
1.2.3.4.5. 6.7.8.
i.ii.iii.iv.v. vi.vii.viii.

In PRE block:
OneTwoThreeFourFive
1.2.3.4.5.
i.ii.iii.iv.v.
In BQ block:
OneTwoThreeFour
1.2.3.4.
i.ii.iii.iv.

noctambulant - walking at night
(from Latin: nox noctis night + ambulare walk)

|||
080158
lynx2-8-8/test/tags.html000644 023711 023712 00000012173 12264313047 015564 0ustar00dickeylynx000000 000000 Tags to Test Color-Style

Content of an H1 Tag

Text after an H1 Tag.

Paragraph after an H1 Tag.

Content of an H2 Tag

Text after an H2 Tag.

Paragraph after an H2 Tag.

Content of an H3 Tag

Text after an H3 Tag.

Paragraph after an H3 Tag.

Content of an H4 Tag

Text after an H4 Tag.

Paragraph after an H4 Tag.

Content of an H5 Tag
Text after an H5 Tag.

Paragraph after an H5 Tag.

Content of an H6 Tag
Text after an H6 Tag.

Paragraph after an H6 Tag. This is an "a" tag.
This is an

"address"
tag.
This is a "b" tag.
This is a "big" tag.
Before quote,
this is a "blockquote"
, after quote.
This is a
"center"
tag.
This is a "cite" tag.
This is a "code" tag.
This is a
div
tag.
This is an "em" tag.
This is a "font" tag.
This is an
"hr"
tag.
This is an "i" tag.
This is an tag.
This is an img tag.
This is an tag.
map: normal: lightgray: blue
This is
pre-formatted
text (three lines, with pre's on preceding/following lines).

This is a "q"tag.
This is a "samp" tag.
This is a "small" tag.
This is a "strong" tag.
This is a "sub" tag.
This is a "sup" tag.
This is a "tt" tag.
This is a "var" tag.

Forms


First: Last: Description:

Another form



first
second
third
empty
done

Another form




Unquoted Table
First: the first row short last
Second: the second row very long string lower-right
Quoted Table
First: the first row very long string last
Second: the second row short lower-right

An image map

Square Circle Triangle

Definition List

This is an definition list:
the first dt
the first dd
the second dt
the second dd
the first dt
the first dd
the second dt
the second dd
the third dt
the third dd
the third dt
the third dd

Unordered List

This is an unordered list:
  • first item
  • second item
    • first item
    • second item
    • third item
  • third item

Ordered List

This is an ordered list:
  1. first item
  2. second item
    1. first item
    2. second item
    3. third item
  3. third item
lynx2-8-8/test/test-styles.html000644 023711 023712 00000005526 10475671321 017136 0ustar00dickeylynx000000 000000 Test Color-Styles

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

Heading 1 - Ordinary Text

Heading 2 - Fontlike Text

This is b (bold).
This is big.
This is blink.
This is i (italicized).
This is small.
This is strike.
This is tt (typewriter).
This is u (underlined).

Heading 2 - Emphasized Text

This is cite (citation).
This is code.
This is del.
This is dfn (definition).
This is emphasized.
This is ins.
This is kbd (keyboard).
This is q (quoted).
This is samp (sample).
This is span.
This is strong.
This is var.

Heading 1 - Ordinary Links

This is a link to fontlike text.
This is a link to emphasized text.

Heading 1 - Emphasized Links

Heading 2 - Fontlike Links


This is b (bold) link.
This is big link.
This is blink link.
This is i (italicized) link.
This is small link.
This is strike link.
This is tt (typewriter) link.
This is u (underlined) link.

Heading 2 - Emphasized Links

This is cite (citation) link.
This is code link.
This is del link.
This is dfn (definition) link.
This is emphasized link.
This is ins link.
This is kbd (keyboard) link.
This is q (quoted) link.
This is samp (sample) link.
This is span link.
This is strong link.
This is var link. lynx2-8-8/test/unicode.html000644 023711 023712 00000125750 07550154260 016264 0ustar00dickeylynx000000 000000 Test of some Unicode symbols in numeric character reference form

    This table prepared from SGML.TXT available at ftp.unicode.org

         ftp://ftp.unicode.org/MAPPINGS/VENDORS/MISC/SGML.TXT
         (if doing ftp, try cd Public/MAPPINGS/VENDORS/MISC)


original comment:

# Author: John Cowan <cowan@ccil.org>
# Date: 25 July 1997
#
# The following table maps SGML character entities from various
# public sets (namely, ISOamsa, ISOamsb, ISOamsc, ISOamsn, ISOamso,
# ISOamsr, ISObox, ISOcyr1, ISOcyr2, ISOdia, ISOgrk1, ISOgrk2,
# ISOgrk3, ISOgrk4, ISOlat1, ISOlat2, ISOnum, ISOpub, ISOtech,
# HTMLspecial, HTMLsymbol) to corresponding Unicode characters.
#
# The table has four tab-separated columns:
#	Column 1: SGML character entity name
#	Column 2: SGML public entity set
#	Column 3: Unicode 2.0 character code
#	Column 4: Unicode 2.0 character name (UPPER CASE)
# Entries which don't have Unicode equivalents have "0x????"
# in Column 3 and a lower case description (from the public entity
# set DTD) in Column 4.  The mapping is not reversible, because many
# distinctions are unified away in Unicode, particularly between
# mathematical symbols.
#
# The table is sorted case-blind by SGML character entity name.
#
# The contents of this table are drawn from various sources, and
# are in the public domain.
#


This test is illuminated Unicode numeric entities like &#x22AB;
We sort the entities according to unicode numbers.
You should see visible characters if your display character set support them
or some substitution string picked up from  src/chrtrans/def7_uni.tbl

If you see something like &#x34D2; - this number unknown to def7_uni.tbl
or the internal browser's implementation is broken.
							Leonid Pauzner.




0x0021    !  		# EXCLAMATION MARK
0x0022    "  		# QUOTATION MARK
0x0023    #  		# NUMBER SIGN
0x0024    $  		# DOLLAR SIGN
0x0025    %  		# PERCENT SIGN
0x0026    &  		# AMPERSAND
0x0028    (  		# LEFT PARENTHESIS
0x0029    )  		# RIGHT PARENTHESIS
0x002A    *  		# ASTERISK
0x002B    +  		# PLUS SIGN
0x002C    ,  		# COMMA
0x002D    -  		# HYPHEN-MINUS
0x002E    .  		# FULL STOP
0x002F    /  		# SOLIDUS
0x003A    :  		# COLON
0x003B    ;  		# SEMICOLON
0x003C    <  		# LESS-THAN SIGN
0x003D    =  		# EQUALS SIGN
0x003E    >  		# GREATER-THAN SIGN
0x003F    ?  		# QUESTION MARK
0x0040    @  		# COMMERCIAL AT
0x005B    [  		# LEFT SQUARE BRACKET
0x005C    \  		# REVERSE SOLIDUS
0x005C    \  		# REVERSE SOLIDUS
0x005D    ]  		# RIGHT SQUARE BRACKET
0x005F    _  		# LOW LINE
0x0060    `  		# GRAVE ACCENT
0x007B    {  		# LEFT CURLY BRACKET
0x007C    |  		# VERTICAL LINE
0x007D    }  		# RIGHT CURLY BRACKET
0x00A0       		# NO-BREAK SPACE
0x00A1    ¡  		# INVERTED EXCLAMATION MARK
0x00A2    ¢  		# CENT SIGN
0x00A3    £  		# POUND SIGN
0x00A4    ¤  		# CURRENCY SIGN
0x00A5    ¥  		# YEN SIGN
0x00A6    ¦  		# BROKEN BAR
0x00A7    §  		# SECTION SIGN
0x00A8    ¨  		# DIAERESIS
0x00A9    ©  		# COPYRIGHT SIGN
0x00AA    ª  		# FEMININE ORDINAL INDICATOR
0x00AB    «  		# LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC    ¬  		# NOT SIGN
0x00AD    ­  		# SOFT HYPHEN
0x00AE    ®  		# REGISTERED SIGN
0x00AF    ¯  		# MACRON
0x00B0    °  		# DEGREE SIGN
0x00B1    ±  		# PLUS-MINUS SIGN
0x00B2    ²  		# SUPERSCRIPT TWO
0x00B3    ³  		# SUPERSCRIPT THREE
0x00B4    ´  		# ACUTE ACCENT
0x00B5    µ  		# MICRO SIGN
0x00B6    ¶  		# PILCROW SIGN
0x00B7    ·  		# MIDDLE DOT
0x00B8    ¸  		# CEDILLA
0x00B9    ¹  		# SUPERSCRIPT ONE
0x00BA    º  		# MASCULINE ORDINAL INDICATOR
0x00BB    »  		# RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC    ¼  		# VULGAR FRACTION ONE QUARTER
0x00BD    ½  		# VULGAR FRACTION ONE HALF
0x00BE    ¾  		# VULGAR FRACTION THREE QUARTERS
0x00BF    ¿  		# INVERTED QUESTION MARK
0x00C0    À  		# LATIN CAPITAL LETTER A WITH GRAVE
0x00C1    Á  		# LATIN CAPITAL LETTER A WITH ACUTE
0x00C2    Â  		# LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x00C3    Ã  		# LATIN CAPITAL LETTER A WITH TILDE
0x00C4    Ä  		# LATIN CAPITAL LETTER A WITH DIAERESIS
0x00C5    Å  		# LATIN CAPITAL LETTER A WITH RING ABOVE
0x00C6    Æ  		# LATIN CAPITAL LETTER AE
0x00C7    Ç  		# LATIN CAPITAL LETTER C WITH CEDILLA
0x00C8    È  		# LATIN CAPITAL LETTER E WITH GRAVE
0x00C9    É  		# LATIN CAPITAL LETTER E WITH ACUTE
0x00CA    Ê  		# LATIN CAPITAL LETTER E WITH CIRCUMFLEX
0x00CB    Ë  		# LATIN CAPITAL LETTER E WITH DIAERESIS
0x00CC    Ì  		# LATIN CAPITAL LETTER I WITH GRAVE
0x00CD    Í  		# LATIN CAPITAL LETTER I WITH ACUTE
0x00CE    Î  		# LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x00CF    Ï  		# LATIN CAPITAL LETTER I WITH DIAERESIS
0x00D0    Ð  		# LATIN CAPITAL LETTER ETH
0x00D1    Ñ  		# LATIN CAPITAL LETTER N WITH TILDE
0x00D2    Ò  		# LATIN CAPITAL LETTER O WITH GRAVE
0x00D3    Ó  		# LATIN CAPITAL LETTER O WITH ACUTE
0x00D4    Ô  		# LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x00D5    Õ  		# LATIN CAPITAL LETTER O WITH TILDE
0x00D6    Ö  		# LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7    ×  		# MULTIPLICATION SIGN
0x00D8    Ø  		# LATIN CAPITAL LETTER O WITH STROKE
0x00D9    Ù  		# LATIN CAPITAL LETTER U WITH GRAVE
0x00DA    Ú  		# LATIN CAPITAL LETTER U WITH ACUTE
0x00DB    Û  		# LATIN CAPITAL LETTER U WITH CIRCUMFLEX
0x00DC    Ü  		# LATIN CAPITAL LETTER U WITH DIAERESIS
0x00DD    Ý  		# LATIN CAPITAL LETTER Y WITH ACUTE
0x00DE    Þ  		# LATIN CAPITAL LETTER THORN
0x00DF    ß  		# LATIN SMALL LETTER SHARP S
0x00E0    à  		# LATIN SMALL LETTER A WITH GRAVE
0x00E1    á  		# LATIN SMALL LETTER A WITH ACUTE
0x00E2    â  		# LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00E3    ã  		# LATIN SMALL LETTER A WITH TILDE
0x00E4    ä  		# LATIN SMALL LETTER A WITH DIAERESIS
0x00E5    å  		# LATIN SMALL LETTER A WITH RING ABOVE
0x00E6    æ  		# LATIN SMALL LETTER AE
0x00E7    ç  		# LATIN SMALL LETTER C WITH CEDILLA
0x00E8    è  		# LATIN SMALL LETTER E WITH GRAVE
0x00E9    é  		# LATIN SMALL LETTER E WITH ACUTE
0x00EA    ê  		# LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00EB    ë  		# LATIN SMALL LETTER E WITH DIAERESIS
0x00EC    ì  		# LATIN SMALL LETTER I WITH GRAVE
0x00ED    í  		# LATIN SMALL LETTER I WITH ACUTE
0x00EE    î  		# LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00EF    ï  		# LATIN SMALL LETTER I WITH DIAERESIS
0x00F0    ð  		# LATIN SMALL LETTER ETH
0x00F1    ñ  		# LATIN SMALL LETTER N WITH TILDE
0x00F2    ò  		# LATIN SMALL LETTER O WITH GRAVE
0x00F3    ó  		# LATIN SMALL LETTER O WITH ACUTE
0x00F4    ô  		# LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00F5    õ  		# LATIN SMALL LETTER O WITH TILDE
0x00F6    ö  		# LATIN SMALL LETTER O WITH DIAERESIS
0x00F7    ÷  		# DIVISION SIGN
0x00F8    ø  		# LATIN SMALL LETTER O WITH STROKE
0x00F9    ù  		# LATIN SMALL LETTER U WITH GRAVE
0x00FA    ú  		# LATIN SMALL LETTER U WITH ACUTE
0x00FB    û  		# LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00FC    ü  		# LATIN SMALL LETTER U WITH DIAERESIS
0x00FD    ý  		# LATIN SMALL LETTER Y WITH ACUTE
0x00FE    þ  		# LATIN SMALL LETTER THORN
0x00FF    ÿ  		# LATIN SMALL LETTER Y WITH DIAERESIS
0x0100    Ā  		# LATIN CAPITAL LETTER A WITH MACRON
0x0101    ā  		# LATIN SMALL LETTER A WITH MACRON
0x0102    Ă  		# LATIN CAPITAL LETTER A WITH BREVE
0x0103    ă  		# LATIN SMALL LETTER A WITH BREVE
0x0104    Ą  		# LATIN CAPITAL LETTER A WITH OGONEK
0x0105    ą  		# LATIN SMALL LETTER A WITH OGONEK
0x0106    Ć  		# LATIN CAPITAL LETTER C WITH ACUTE
0x0107    ć  		# LATIN SMALL LETTER C WITH ACUTE
0x0108    Ĉ  		# LATIN CAPITAL LETTER C WITH CIRCUMFLEX
0x0109    ĉ  		# LATIN SMALL LETTER C WITH CIRCUMFLEX
0x010A    Ċ  		# LATIN CAPITAL LETTER C WITH DOT ABOVE
0x010B    ċ  		# LATIN SMALL LETTER C WITH DOT ABOVE
0x010C    Č  		# LATIN CAPITAL LETTER C WITH CARON
0x010D    č  		# LATIN SMALL LETTER C WITH CARON
0x010E    Ď  		# LATIN CAPITAL LETTER D WITH CARON
0x010F    ď  		# LATIN SMALL LETTER D WITH CARON
0x0110    Đ  		# LATIN CAPITAL LETTER D WITH STROKE
0x0111    đ  		# LATIN SMALL LETTER D WITH STROKE
0x0112    Ē  		# LATIN CAPITAL LETTER E WITH MACRON
0x0113    ē  		# LATIN SMALL LETTER E WITH MACRON
0x0116    Ė  		# LATIN CAPITAL LETTER E WITH DOT ABOVE
0x0117    ė  		# LATIN SMALL LETTER E WITH DOT ABOVE
0x0118    Ę  		# LATIN CAPITAL LETTER E WITH OGONEK
0x0119    ę  		# LATIN SMALL LETTER E WITH OGONEK
0x011A    Ě  		# LATIN CAPITAL LETTER E WITH CARON
0x011B    ě  		# LATIN SMALL LETTER E WITH CARON
0x011C    Ĝ  		# LATIN CAPITAL LETTER G WITH CIRCUMFLEX
0x011D    ĝ  		# LATIN SMALL LETTER G WITH CIRCUMFLEX
0x011E    Ğ  		# LATIN CAPITAL LETTER G WITH BREVE
0x011F    ğ  		# LATIN SMALL LETTER G WITH BREVE
0x0120    Ġ  		# LATIN CAPITAL LETTER G WITH DOT ABOVE
0x0121    ġ  		# LATIN SMALL LETTER G WITH DOT ABOVE
0x0122    Ģ  		# LATIN CAPITAL LETTER G WITH CEDILLA
0x0123    ģ  		# LATIN SMALL LETTER G WITH CEDILLA
0x0124    Ĥ  		# LATIN CAPITAL LETTER H WITH CIRCUMFLEX
0x0125    ĥ  		# LATIN SMALL LETTER H WITH CIRCUMFLEX
0x0126    Ħ  		# LATIN CAPITAL LETTER H WITH STROKE
0x0127    ħ  		# LATIN SMALL LETTER H WITH STROKE
0x0128    Ĩ  		# LATIN CAPITAL LETTER I WITH TILDE
0x0129    ĩ  		# LATIN SMALL LETTER I WITH TILDE
0x012A    Ī  		# LATIN CAPITAL LETTER I WITH MACRON
0x012B    ī  		# LATIN SMALL LETTER I WITH MACRON
0x012E    Į  		# LATIN CAPITAL LETTER I WITH OGONEK
0x012F    į  		# LATIN SMALL LETTER I WITH OGONEK
0x0130    İ  		# LATIN CAPITAL LETTER I WITH DOT ABOVE
0x0131    ı  		# LATIN SMALL LETTER DOTLESS I
0x0131    ı  		# LATIN SMALL LETTER DOTLESS I
0x0132    IJ  		# LATIN CAPITAL LIGATURE IJ
0x0133    ij  		# LATIN SMALL LIGATURE IJ
0x0134    Ĵ  		# LATIN CAPITAL LETTER J WITH CIRCUMFLEX
0x0135    ĵ  		# LATIN SMALL LETTER J WITH CIRCUMFLEX
0x0136    Ķ  		# LATIN CAPITAL LETTER K WITH CEDILLA
0x0137    ķ  		# LATIN SMALL LETTER K WITH CEDILLA
0x0138    ĸ  		# LATIN SMALL LETTER KRA
0x0139    Ĺ  		# LATIN CAPITAL LETTER L WITH ACUTE
0x013A    ĺ  		# LATIN SMALL LETTER L WITH ACUTE
0x013B    Ļ  		# LATIN CAPITAL LETTER L WITH CEDILLA
0x013C    ļ  		# LATIN SMALL LETTER L WITH CEDILLA
0x013D    Ľ  		# LATIN CAPITAL LETTER L WITH CARON
0x013E    ľ  		# LATIN SMALL LETTER L WITH CARON
0x013F    Ŀ  		# LATIN CAPITAL LETTER L WITH MIDDLE DOT
0x0140    ŀ  		# LATIN SMALL LETTER L WITH MIDDLE DOT
0x0141    Ł  		# LATIN CAPITAL LETTER L WITH STROKE
0x0142    ł  		# LATIN SMALL LETTER L WITH STROKE
0x0143    Ń  		# LATIN CAPITAL LETTER N WITH ACUTE
0x0144    ń  		# LATIN SMALL LETTER N WITH ACUTE
0x0145    Ņ  		# LATIN CAPITAL LETTER N WITH CEDILLA
0x0146    ņ  		# LATIN SMALL LETTER N WITH CEDILLA
0x0147    Ň  		# LATIN CAPITAL LETTER N WITH CARON
0x0148    ň  		# LATIN SMALL LETTER N WITH CARON
0x0149    ʼn  		# LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
0x014A    Ŋ  		# LATIN CAPITAL LETTER ENG
0x014B    ŋ  		# LATIN SMALL LETTER ENG
0x014C    Ō  		# LATIN CAPITAL LETTER O WITH MACRON
0x014D    ō  		# LATIN SMALL LETTER O WITH MACRON
0x0150    Ő  		# LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
0x0151    ő  		# LATIN SMALL LETTER O WITH DOUBLE ACUTE
0x0152    Œ  		# LATIN CAPITAL LIGATURE OE
0x0153    œ  		# LATIN SMALL LIGATURE OE
0x0154    Ŕ  		# LATIN CAPITAL LETTER R WITH ACUTE
0x0155    ŕ  		# LATIN SMALL LETTER R WITH ACUTE
0x0156    Ŗ  		# LATIN CAPITAL LETTER R WITH CEDILLA
0x0157    ŗ  		# LATIN SMALL LETTER R WITH CEDILLA
0x0158    Ř  		# LATIN CAPITAL LETTER R WITH CARON
0x0159    ř  		# LATIN SMALL LETTER R WITH CARON
0x015A    Ś  		# LATIN CAPITAL LETTER S WITH ACUTE
0x015B    ś  		# LATIN SMALL LETTER S WITH ACUTE
0x015C    Ŝ  		# LATIN CAPITAL LETTER S WITH CIRCUMFLEX
0x015D    ŝ  		# LATIN SMALL LETTER S WITH CIRCUMFLEX
0x015E    Ş  		# LATIN CAPITAL LETTER S WITH CEDILLA
0x015F    ş  		# LATIN SMALL LETTER S WITH CEDILLA
0x0160    Š  		# LATIN CAPITAL LETTER S WITH CARON
0x0161    š  		# LATIN SMALL LETTER S WITH CARON
0x0162    Ţ  		# LATIN CAPITAL LETTER T WITH CEDILLA
0x0163    ţ  		# LATIN SMALL LETTER T WITH CEDILLA
0x0164    Ť  		# LATIN CAPITAL LETTER T WITH CARON
0x0165    ť  		# LATIN SMALL LETTER T WITH CARON
0x0166    Ŧ  		# LATIN CAPITAL LETTER T WITH STROKE
0x0167    ŧ  		# LATIN SMALL LETTER T WITH STROKE
0x0168    Ũ  		# LATIN CAPITAL LETTER U WITH TILDE
0x0169    ũ  		# LATIN SMALL LETTER U WITH TILDE
0x016A    Ū  		# LATIN CAPITAL LETTER U WITH MACRON
0x016B    ū  		# LATIN SMALL LETTER U WITH MACRON
0x016C    Ŭ  		# LATIN CAPITAL LETTER U WITH BREVE
0x016D    ŭ  		# LATIN SMALL LETTER U WITH BREVE
0x016E    Ů  		# LATIN CAPITAL LETTER U WITH RING ABOVE
0x016F    ů  		# LATIN SMALL LETTER U WITH RING ABOVE
0x0170    Ű  		# LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
0x0171    ű  		# LATIN SMALL LETTER U WITH DOUBLE ACUTE
0x0172    Ų  		# LATIN CAPITAL LETTER U WITH OGONEK
0x0173    ų  		# LATIN SMALL LETTER U WITH OGONEK
0x0174    Ŵ  		# LATIN CAPITAL LETTER W WITH CIRCUMFLEX
0x0175    ŵ  		# LATIN SMALL LETTER W WITH CIRCUMFLEX
0x0176    Ŷ  		# LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
0x0177    ŷ  		# LATIN SMALL LETTER Y WITH CIRCUMFLEX
0x0178    Ÿ  		# LATIN CAPITAL LETTER Y WITH DIAERESIS
0x0179    Ź  		# LATIN CAPITAL LETTER Z WITH ACUTE
0x017A    ź  		# LATIN SMALL LETTER Z WITH ACUTE
0x017B    Ż  		# LATIN CAPITAL LETTER Z WITH DOT ABOVE
0x017C    ż  		# LATIN SMALL LETTER Z WITH DOT ABOVE
0x017D    Ž  		# LATIN CAPITAL LETTER Z WITH CARON
0x017E    ž  		# LATIN SMALL LETTER Z WITH CARON
0x0192    ƒ  		# LATIN SMALL LETTER F WITH HOOK
0x01F5    ǵ  		# LATIN SMALL LETTER G WITH ACUTE
0x02BC    ʼ  		# MODIFIER LETTER APOSTROPHE
0x02C6    ˆ  		# MODIFIER LETTER CIRCUMFLEX ACCENT
0x02C7    ˇ  		# CARON
0x02D8    ˘  		# BREVE
0x02D9    ˙  		# DOT ABOVE
0x02DA    ˚  		# RING ABOVE
0x02DB    ˛  		# OGONEK
0x02DC    ˜  		# SMALL TILDE
0x02DD    ˝  		# DOUBLE ACUTE ACCENT
0x0386    Ά  		# GREEK CAPITAL LETTER ALPHA WITH TONOS
0x0388    Έ  		# GREEK CAPITAL LETTER EPSILON WITH TONOS
0x0389    Ή  		# GREEK CAPITAL LETTER ETA WITH TONOS
0x038A    Ί  		# GREEK CAPITAL LETTER IOTA WITH TONOS
0x038C    Ό  		# GREEK CAPITAL LETTER OMICRON WITH TONOS
0x038E    Ύ  		# GREEK CAPITAL LETTER UPSILON WITH TONOS
0x038F    Ώ  		# GREEK CAPITAL LETTER OMEGA WITH TONOS
0x0390    ΐ  		# GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
0x0391    Α  		# GREEK CAPITAL LETTER ALPHA
0x0392    Β  		# GREEK CAPITAL LETTER BETA
0x0393    Γ  		# GREEK CAPITAL LETTER GAMMA
0x0394    Δ  		# GREEK CAPITAL LETTER DELTA
0x0395    Ε  		# GREEK CAPITAL LETTER EPSILON
0x0396    Ζ  		# GREEK CAPITAL LETTER ZETA
0x0397    Η  		# GREEK CAPITAL LETTER ETA
0x0398    Θ  		# GREEK CAPITAL LETTER THETA
0x0399    Ι  		# GREEK CAPITAL LETTER IOTA
0x039A    Κ  		# GREEK CAPITAL LETTER KAPPA
0x039B    Λ  		# GREEK CAPITAL LETTER LAMDA
0x039C    Μ  		# GREEK CAPITAL LETTER MU
0x039D    Ν  		# GREEK CAPITAL LETTER NU
0x039E    Ξ  		# GREEK CAPITAL LETTER XI
0x039F    Ο  		# GREEK CAPITAL LETTER OMICRON
0x03A0    Π  		# GREEK CAPITAL LETTER PI
0x03A1    Ρ  		# GREEK CAPITAL LETTER RHO
0x03A3    Σ  		# GREEK CAPITAL LETTER SIGMA
0x03A4    Τ  		# GREEK CAPITAL LETTER TAU
0x03A5    Υ  		# GREEK CAPITAL LETTER UPSILON
0x03A6    Φ  		# GREEK CAPITAL LETTER PHI
0x03A7    Χ  		# GREEK CAPITAL LETTER CHI
0x03A8    Ψ  		# GREEK CAPITAL LETTER PSI
0x03A9    Ω  		# GREEK CAPITAL LETTER OMEGA
0x03AA    Ϊ  		# GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
0x03AB    Ϋ  		# GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
0x03AC    ά  		# GREEK SMALL LETTER ALPHA WITH TONOS
0x03AD    έ  		# GREEK SMALL LETTER EPSILON WITH TONOS
0x03AE    ή  		# GREEK SMALL LETTER ETA WITH TONOS
0x03AF    ί  		# GREEK SMALL LETTER IOTA WITH TONOS
0x03B0    ΰ  		# GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
0x03B1    α  		# GREEK SMALL LETTER ALPHA
0x03B2    β  		# GREEK SMALL LETTER BETA
0x03B3    γ  		# GREEK SMALL LETTER GAMMA
0x03B4    δ  		# GREEK SMALL LETTER DELTA
0x03B5    ε  		# GREEK SMALL LETTER EPSILON
0x03B6    ζ  		# GREEK SMALL LETTER ZETA
0x03B7    η  		# GREEK SMALL LETTER ETA
0x03B8    θ  		# GREEK SMALL LETTER THETA
0x03B9    ι  		# GREEK SMALL LETTER IOTA
0x03BA    κ  		# GREEK SMALL LETTER KAPPA
0x03BB    λ  		# GREEK SMALL LETTER LAMDA
0x03BC    μ  		# GREEK SMALL LETTER MU
0x03BD    ν  		# GREEK SMALL LETTER NU
0x03BE    ξ  		# GREEK SMALL LETTER XI
0x03BF    ο  		# GREEK SMALL LETTER OMICRON
0x03C0    π  		# GREEK SMALL LETTER PI
0x03C1    ρ  		# GREEK SMALL LETTER RHO
0x03C2    ς  		# GREEK SMALL LETTER FINAL SIGMA
0x03C3    σ  		# GREEK SMALL LETTER SIGMA
0x03C4    τ  		# GREEK SMALL LETTER TAU
0x03C5    υ  		# GREEK SMALL LETTER UPSILON
0x03C6    φ  		# GREEK SMALL LETTER PHI
0x03C7    χ  		# GREEK SMALL LETTER CHI
0x03C8    ψ  		# GREEK SMALL LETTER PSI
0x03C9    ω  		# GREEK SMALL LETTER OMEGA
0x03CA    ϊ  		# GREEK SMALL LETTER IOTA WITH DIALYTIKA
0x03CB    ϋ  		# GREEK SMALL LETTER UPSILON WITH DIALYTIKA
0x03CC    ό  		# GREEK SMALL LETTER OMICRON WITH TONOS
0x03CE    ώ  		# GREEK SMALL LETTER OMEGA WITH TONOS
0x03D1    ϑ  		# GREEK THETA SYMBOL
0x03D2    ϒ  		# GREEK UPSILON WITH HOOK SYMBOL
0x03D5    ϕ  		# GREEK PHI SYMBOL
0x03D6    ϖ  		# GREEK PI SYMBOL
0x03DC    Ϝ  		# GREEK LETTER DIGAMMA
0x03F0    ϰ  		# GREEK KAPPA SYMBOL
0x03F1    ϱ  		# GREEK RHO SYMBOL
0x0401    Ё  		# CYRILLIC CAPITAL LETTER IO
0x0402    Ђ  		# CYRILLIC CAPITAL LETTER DJE
0x0403    Ѓ  		# CYRILLIC CAPITAL LETTER GJE
0x0404    Є  		# CYRILLIC CAPITAL LETTER UKRAINIAN IE
0x0405    Ѕ  		# CYRILLIC CAPITAL LETTER DZE
0x0406    І  		# CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
0x0407    Ї  		# CYRILLIC CAPITAL LETTER YI
0x0408    Ј  		# CYRILLIC CAPITAL LETTER JE
0x0409    Љ  		# CYRILLIC CAPITAL LETTER LJE
0x040A    Њ  		# CYRILLIC CAPITAL LETTER NJE
0x040B    Ћ  		# CYRILLIC CAPITAL LETTER TSHE
0x040C    Ќ  		# CYRILLIC CAPITAL LETTER KJE
0x040E    Ў  		# CYRILLIC CAPITAL LETTER SHORT U
0x040F    Џ  		# CYRILLIC CAPITAL LETTER DZHE
0x0410    А  		# CYRILLIC CAPITAL LETTER A
0x0411    Б  		# CYRILLIC CAPITAL LETTER BE
0x0412    В  		# CYRILLIC CAPITAL LETTER VE
0x0413    Г  		# CYRILLIC CAPITAL LETTER GHE
0x0414    Д  		# CYRILLIC CAPITAL LETTER DE
0x0415    Е  		# CYRILLIC CAPITAL LETTER IE
0x0416    Ж  		# CYRILLIC CAPITAL LETTER ZHE
0x0417    З  		# CYRILLIC CAPITAL LETTER ZE
0x0418    И  		# CYRILLIC CAPITAL LETTER I
0x0419    Й  		# CYRILLIC CAPITAL LETTER SHORT I
0x041A    К  		# CYRILLIC CAPITAL LETTER KA
0x041B    Л  		# CYRILLIC CAPITAL LETTER EL
0x041C    М  		# CYRILLIC CAPITAL LETTER EM
0x041D    Н  		# CYRILLIC CAPITAL LETTER EN
0x041E    О  		# CYRILLIC CAPITAL LETTER O
0x041F    П  		# CYRILLIC CAPITAL LETTER PE
0x0420    Р  		# CYRILLIC CAPITAL LETTER ER
0x0421    С  		# CYRILLIC CAPITAL LETTER ES
0x0422    Т  		# CYRILLIC CAPITAL LETTER TE
0x0423    У  		# CYRILLIC CAPITAL LETTER U
0x0424    Ф  		# CYRILLIC CAPITAL LETTER EF
0x0425    Х  		# CYRILLIC CAPITAL LETTER HA
0x0426    Ц  		# CYRILLIC CAPITAL LETTER TSE
0x0427    Ч  		# CYRILLIC CAPITAL LETTER CHE
0x0428    Ш  		# CYRILLIC CAPITAL LETTER SHA
0x0429    Щ  		# CYRILLIC CAPITAL LETTER SHCHA
0x042A    Ъ  		# CYRILLIC CAPITAL LETTER HARD SIGN
0x042B    Ы  		# CYRILLIC CAPITAL LETTER YERU
0x042C    Ь  		# CYRILLIC CAPITAL LETTER SOFT SIGN
0x042D    Э  		# CYRILLIC CAPITAL LETTER E
0x042E    Ю  		# CYRILLIC CAPITAL LETTER YU
0x042F    Я  		# CYRILLIC CAPITAL LETTER YA
0x0430    а  		# CYRILLIC SMALL LETTER A
0x0431    б  		# CYRILLIC SMALL LETTER BE
0x0432    в  		# CYRILLIC SMALL LETTER VE
0x0433    г  		# CYRILLIC SMALL LETTER GHE
0x0434    д  		# CYRILLIC SMALL LETTER DE
0x0435    е  		# CYRILLIC SMALL LETTER IE
0x0436    ж  		# CYRILLIC SMALL LETTER ZHE
0x0437    з  		# CYRILLIC SMALL LETTER ZE
0x0438    и  		# CYRILLIC SMALL LETTER I
0x0439    й  		# CYRILLIC SMALL LETTER SHORT I
0x043A    к  		# CYRILLIC SMALL LETTER KA
0x043B    л  		# CYRILLIC SMALL LETTER EL
0x043C    м  		# CYRILLIC SMALL LETTER EM
0x043D    н  		# CYRILLIC SMALL LETTER EN
0x043E    о  		# CYRILLIC SMALL LETTER O
0x043F    п  		# CYRILLIC SMALL LETTER PE
0x0440    р  		# CYRILLIC SMALL LETTER ER
0x0441    с  		# CYRILLIC SMALL LETTER ES
0x0442    т  		# CYRILLIC SMALL LETTER TE
0x0443    у  		# CYRILLIC SMALL LETTER U
0x0444    ф  		# CYRILLIC SMALL LETTER EF
0x0445    х  		# CYRILLIC SMALL LETTER HA
0x0446    ц  		# CYRILLIC SMALL LETTER TSE
0x0447    ч  		# CYRILLIC SMALL LETTER CHE
0x0448    ш  		# CYRILLIC SMALL LETTER SHA
0x0449    щ  		# CYRILLIC SMALL LETTER SHCHA
0x044A    ъ  		# CYRILLIC SMALL LETTER HARD SIGN
0x044B    ы  		# CYRILLIC SMALL LETTER YERU
0x044C    ь  		# CYRILLIC SMALL LETTER SOFT SIGN
0x044D    э  		# CYRILLIC SMALL LETTER E
0x044E    ю  		# CYRILLIC SMALL LETTER YU
0x044F    я  		# CYRILLIC SMALL LETTER YA
0x0451    ё  		# CYRILLIC SMALL LETTER IO
0x0452    ђ  		# CYRILLIC SMALL LETTER DJE
0x0453    ѓ  		# CYRILLIC SMALL LETTER GJE
0x0454    є  		# CYRILLIC SMALL LETTER UKRAINIAN IE
0x0455    ѕ  		# CYRILLIC SMALL LETTER DZE
0x0456    і  		# CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
0x0457    ї  		# CYRILLIC SMALL LETTER YI
0x0458    ј  		# CYRILLIC SMALL LETTER JE
0x0459    љ  		# CYRILLIC SMALL LETTER LJE
0x045A    њ  		# CYRILLIC SMALL LETTER NJE
0x045B    ћ  		# CYRILLIC SMALL LETTER TSHE
0x045C    ќ  		# CYRILLIC SMALL LETTER KJE
0x045E    ў  		# CYRILLIC SMALL LETTER SHORT U
0x045F    џ  		# CYRILLIC SMALL LETTER DZHE
0x2002       		# EN SPACE
0x2003       		# EM SPACE
0x2004       		# THREE-PER-EM SPACE
0x2005       		# FOUR-PER-EM SPACE
0x2007       		# FIGURE SPACE
0x2008       		# PUNCTUATION SPACE
0x2009       		# THIN SPACE
0x200A       		# HAIR SPACE
0x200C    ‌  		# ZERO WIDTH NON-JOINER
0x200D    ‍  		# ZERO WIDTH JOINER
0x200E    ‎  		# LEFT-TO-RIGHT MARK
0x200F    ‏  		# RIGHT-TO-LEFT MARK
0x2010    ‐  		# HYPHEN
0x2013    –  		# EN DASH
0x2014    —  		# EM DASH
0x2015    ―  		# HORIZONTAL BAR
0x2016    ‖  		# DOUBLE VERTICAL LINE
0x2018    ‘  		# LEFT SINGLE QUOTATION MARK
0x2018    ‘  		# LEFT SINGLE QUOTATION MARK
0x2019    ’  		# RIGHT SINGLE QUOTATION MARK
0x201A    ‚  		# SINGLE LOW-9 QUOTATION MARK
0x201A    ‚  		# SINGLE LOW-9 QUOTATION MARK
0x201C    “  		# LEFT DOUBLE QUOTATION MARK
0x201C    “  		# LEFT DOUBLE QUOTATION MARK
0x201D    ”  		# RIGHT DOUBLE QUOTATION MARK
0x201E    „  		# DOUBLE LOW-9 QUOTATION MARK
0x201E    „  		# DOUBLE LOW-9 QUOTATION MARK
0x2020    †  		# DAGGER
0x2021    ‡  		# DOUBLE DAGGER
0x2022    •  		# BULLET
0x2025    ‥  		# TWO DOT LEADER
0x2026    …  		# HORIZONTAL ELLIPSIS
0x2026    …  		# HORIZONTAL ELLIPSIS
0x2030    ‰  		# PER MILLE SIGN
0x2032    ′  		# PRIME
0x2032    ′  		# PRIME
0x2033    ″  		# DOUBLE PRIME
0x2034    ‴  		# TRIPLE PRIME
0x2035    ‵  		# REVERSED PRIME
0x2039    ‹  		# SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x203A    ›  		# SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x203E    ‾  		# OVERLINE
0x2041    ⁁  		# CARET INSERTION POINT
0x2043    ⁃  		# HYPHEN BULLET
0x2044    ⁄  		# FRACTION SLASH
0x20AC    €  		# EURO SIGN
0x20DB    ⃛  		# COMBINING THREE DOTS ABOVE
0x20DC    ⃜  		# COMBINING FOUR DOTS ABOVE
0x2105    ℅  		# CARE OF
0x210B    ℋ  		# SCRIPT CAPITAL H
0x210F    ℏ  		# PLANCK CONSTANT OVER TWO PI
0x2111    ℑ  		# BLACK-LETTER CAPITAL I
0x2112    ℒ  		# SCRIPT CAPITAL L
0x2113    ℓ  		# SCRIPT SMALL L
0x2116    №  		# NUMERO SIGN
0x2117    ℗  		# SOUND RECORDING COPYRIGHT
0x2118    ℘  		# SCRIPT CAPITAL P
0x211C    ℜ  		# BLACK-LETTER CAPITAL R
0x211E    ℞  		# PRESCRIPTION TAKE
0x2122    ™  		# TRADE MARK SIGN
0x2126    Ω  		# OHM SIGN
0x212B    Å  		# ANGSTROM SIGN
0x212C    ℬ  		# SCRIPT CAPITAL B
0x2133    ℳ  		# SCRIPT CAPITAL M
0x2134    ℴ  		# SCRIPT SMALL O
0x2135    ℵ  		# ALEF SYMBOL
0x2135    ℵ  		# ALEF SYMBOL
0x2136    ℶ  		# BET SYMBOL
0x2137    ℷ  		# GIMEL SYMBOL
0x2138    ℸ  		# DALET SYMBOL
0x2153    ⅓  		# VULGAR FRACTION ONE THIRD
0x2154    ⅔  		# VULGAR FRACTION TWO THIRDS
0x2155    ⅕  		# VULGAR FRACTION ONE FIFTH
0x2156    ⅖  		# VULGAR FRACTION TWO FIFTHS
0x2157    ⅗  		# VULGAR FRACTION THREE FIFTHS
0x2158    ⅘  		# VULGAR FRACTION FOUR FIFTHS
0x2159    ⅙  		# VULGAR FRACTION ONE SIXTH
0x215A    ⅚  		# VULGAR FRACTION FIVE SIXTHS
0x215B    ⅛  		# VULGAR FRACTION ONE EIGHTH
0x215C    ⅜  		# VULGAR FRACTION THREE EIGHTHS
0x215D    ⅝  		# VULGAR FRACTION FIVE EIGHTHS
0x215E    ⅞  		# VULGAR FRACTION SEVEN EIGHTHS
0x2190    ←  		# LEFTWARDS ARROW
0x2191    ↑  		# UPWARDS ARROW
0x2192    →  		# RIGHTWARDS ARROW
0x2193    ↓  		# DOWNWARDS ARROW
0x2194    ↔  		# LEFT RIGHT ARROW
0x2195    ↕  		# UP DOWN ARROW
0x2196    ↖  		# NORTH WEST ARROW
0x2197    ↗  		# NORTH EAST ARROW
0x2198    ↘  		# SOUTH EAST ARROW
0x2199    ↙  		# SOUTH WEST ARROW
0x219A    ↚  		# LEFTWARDS ARROW WITH STROKE
0x219B    ↛  		# RIGHTWARDS ARROW WITH STROKE
0x219D    ↝  		# RIGHTWARDS WAVE ARROW
0x219E    ↞  		# LEFTWARDS TWO HEADED ARROW
0x21A0    ↠  		# RIGHTWARDS TWO HEADED ARROW
0x21A2    ↢  		# LEFTWARDS ARROW WITH TAIL
0x21A3    ↣  		# RIGHTWARDS ARROW WITH TAIL
0x21A6    ↦  		# RIGHTWARDS ARROW FROM BAR
0x21A9    ↩  		# LEFTWARDS ARROW WITH HOOK
0x21AA    ↪  		# RIGHTWARDS ARROW WITH HOOK
0x21AB    ↫  		# LEFTWARDS ARROW WITH LOOP
0x21AC    ↬  		# RIGHTWARDS ARROW WITH LOOP
0x21AD    ↭  		# LEFT RIGHT WAVE ARROW
0x21AE    ↮  		# LEFT RIGHT ARROW WITH STROKE
0x21B0    ↰  		# UPWARDS ARROW WITH TIP LEFTWARDS
0x21B1    ↱  		# UPWARDS ARROW WITH TIP RIGHTWARDS
0x21B5    ↵  		# DOWNWARDS ARROW WITH CORNER LEFTWARDS
0x21B6    ↶  		# ANTICLOCKWISE TOP SEMICIRCLE ARROW
0x21B7    ↷  		# CLOCKWISE TOP SEMICIRCLE ARROW
0x21BA    ↺  		# ANTICLOCKWISE OPEN CIRCLE ARROW
0x21BB    ↻  		# CLOCKWISE OPEN CIRCLE ARROW
0x21BC    ↼  		# LEFTWARDS HARPOON WITH BARB UPWARDS
0x21BD    ↽  		# LEFTWARDS HARPOON WITH BARB DOWNWARDS
0x21BE    ↾  		# UPWARDS HARPOON WITH BARB RIGHTWARDS
0x21BF    ↿  		# UPWARDS HARPOON WITH BARB LEFTWARDS
0x21C0    ⇀  		# RIGHTWARDS HARPOON WITH BARB UPWARDS
0x21C1    ⇁  		# RIGHTWARDS HARPOON WITH BARB DOWNWARDS
0x21C2    ⇂  		# DOWNWARDS HARPOON WITH BARB RIGHTWARDS
0x21C3    ⇃  		# DOWNWARDS HARPOON WITH BARB LEFTWARDS
0x21C4    ⇄  		# RIGHTWARDS ARROW OVER LEFTWARDS ARROW
0x21C6    ⇆  		# LEFTWARDS ARROW OVER RIGHTWARDS ARROW
0x21C7    ⇇  		# LEFTWARDS PAIRED ARROWS
0x21C8    ⇈  		# UPWARDS PAIRED ARROWS
0x21C9    ⇉  		# RIGHTWARDS PAIRED ARROWS
0x21CA    ⇊  		# DOWNWARDS PAIRED ARROWS
0x21CB    ⇋  		# LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
0x21CC    ⇌  		# RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
0x21CD    ⇍  		# LEFTWARDS DOUBLE ARROW WITH STROKE
0x21CE    ⇎  		# LEFT RIGHT DOUBLE ARROW WITH STROKE
0x21CF    ⇏  		# RIGHTWARDS DOUBLE ARROW WITH STROKE
0x21D0    ⇐  		# LEFTWARDS DOUBLE ARROW
0x21D1    ⇑  		# UPWARDS DOUBLE ARROW
0x21D2    ⇒  		# RIGHTWARDS DOUBLE ARROW
0x21D3    ⇓  		# DOWNWARDS DOUBLE ARROW
0x21D4    ⇔  		# LEFT RIGHT DOUBLE ARROW
0x21D5    ⇕  		# UP DOWN DOUBLE ARROW
0x21DA    ⇚  		# LEFTWARDS TRIPLE ARROW
0x21DB    ⇛  		# RIGHTWARDS TRIPLE ARROW
0x2200    ∀  		# FOR ALL
0x2201    ∁  		# COMPLEMENT
0x2202    ∂  		# PARTIAL DIFFERENTIAL
0x2203    ∃  		# THERE EXISTS
0x2204    ∄  		# THERE DOES NOT EXIST
0x2205    ∅  		# EMPTY SET
0x2207    ∇  		# NABLA
0x2208    ∈  		# ELEMENT OF
0x2209    ∉  		# NOT AN ELEMENT OF
0x220A    ∊  		# SMALL ELEMENT OF
0x220B    ∋  		# CONTAINS AS MEMBER
0x220D    ∍  		# SMALL CONTAINS AS MEMBER
0x220F    ∏  		# N-ARY PRODUCT
0x2210    ∐  		# N-ARY COPRODUCT
0x2211    ∑  		# N-ARY SUMMATION
0x2212    −  		# MINUS SIGN
0x2213    ∓  		# MINUS-OR-PLUS SIGN
0x2214    ∔  		# DOT PLUS
0x2216    ∖  		# SET MINUS
0x2217    ∗  		# ASTERISK OPERATOR
0x2218    ∘  		# RING OPERATOR
0x221A    √  		# SQUARE ROOT
0x221D    ∝  		# PROPORTIONAL TO
0x221E    ∞  		# INFINITY
0x221F    ∟  		# RIGHT ANGLE
0x2220    ∠  		# ANGLE
0x2221    ∡  		# MEASURED ANGLE
0x2222    ∢  		# SPHERICAL ANGLE
0x2223    ∣  		# DIVIDES
0x2224    ∤  		# DOES NOT DIVIDE
0x2225    ∥  		# PARALLEL TO
0x2226    ∦  		# NOT PARALLEL TO
0x2227    ∧  		# LOGICAL AND
0x2228    ∨  		# LOGICAL OR
0x2229    ∩  		# INTERSECTION
0x222A    ∪  		# UNION
0x222B    ∫  		# INTEGRAL
0x222E    ∮  		# CONTOUR INTEGRAL
0x2234    ∴  		# THEREFORE
0x2235    ∵  		# BECAUSE
0x223C    ∼  		# TILDE OPERATOR
0x223D    ∽  		# REVERSED TILDE
0x2240    ≀  		# WREATH PRODUCT
0x2241    ≁  		# NOT TILDE
0x2243    ≃  		# ASYMPTOTICALLY EQUAL TO
0x2244    ≄  		# NOT ASYMPTOTICALLY EQUAL TO
0x2245    ≅  		# APPROXIMATELY EQUAL TO
0x2247    ≇  		# NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
0x2248    ≈  		# ALMOST EQUAL TO
0x2249    ≉  		# NOT ALMOST EQUAL TO
0x224A    ≊  		# ALMOST EQUAL OR EQUAL TO
0x224C    ≌  		# ALL EQUAL TO
0x224E    ≎  		# GEOMETRICALLY EQUIVALENT TO
0x224F    ≏  		# DIFFERENCE BETWEEN
0x2250    ≐  		# APPROACHES THE LIMIT
0x2251    ≑  		# GEOMETRICALLY EQUAL TO
0x2252    ≒  		# APPROXIMATELY EQUAL TO OR THE IMAGE OF
0x2253    ≓  		# IMAGE OF OR APPROXIMATELY EQUAL TO
0x2254    ≔  		# COLON EQUALS
0x2255    ≕  		# EQUALS COLON
0x2256    ≖  		# RING IN EQUAL TO
0x2257    ≗  		# RING EQUAL TO
0x2259    ≙  		# ESTIMATES
0x225C    ≜  		# DELTA EQUAL TO
0x2260    ≠  		# NOT EQUAL TO
0x2261    ≡  		# IDENTICAL TO
0x2262    ≢  		# NOT IDENTICAL TO
0x2264    ≤  		# LESS-THAN OR EQUAL TO
0x2265    ≥  		# GREATER-THAN OR EQUAL TO
0x2266    ≦  		# LESS-THAN OVER EQUAL TO
0x2267    ≧  		# GREATER-THAN OVER EQUAL TO
0x2268    ≨  		# LESS-THAN BUT NOT EQUAL TO
0x2269    ≩  		# GREATER-THAN BUT NOT EQUAL TO
0x226A    ≪  		# MUCH LESS-THAN
0x226B    ≫  		# MUCH GREATER-THAN
0x226C    ≬  		# BETWEEN
0x226E    ≮  		# NOT LESS-THAN
0x226F    ≯  		# NOT GREATER-THAN
0x2270    ≰  		# NEITHER LESS-THAN NOR EQUAL TO
0x2271    ≱  		# NEITHER GREATER-THAN NOR EQUAL TO
0x2272    ≲  		# LESS-THAN OR EQUIVALENT TO
0x2273    ≳  		# GREATER-THAN OR EQUIVALENT TO
0x2276    ≶  		# LESS-THAN OR GREATER-THAN
0x2277    ≷  		# GREATER-THAN OR LESS-THAN
0x227A    ≺  		# PRECEDES
0x227B    ≻  		# SUCCEEDS
0x227C    ≼  		# PRECEDES OR EQUAL TO
0x227D    ≽  		# SUCCEEDS OR EQUAL TO
0x227E    ≾  		# PRECEDES OR EQUIVALENT TO
0x227F    ≿  		# SUCCEEDS OR EQUIVALENT TO
0x2280    ⊀  		# DOES NOT PRECEDE
0x2281    ⊁  		# DOES NOT SUCCEED
0x2282    ⊂  		# SUBSET OF
0x2283    ⊃  		# SUPERSET OF
0x2284    ⊄  		# NOT A SUBSET OF
0x2285    ⊅  		# NOT A SUPERSET OF
0x2286    ⊆  		# SUBSET OF OR EQUAL TO
0x2287    ⊇  		# SUPERSET OF OR EQUAL TO
0x2288    ⊈  		# NEITHER A SUBSET OF NOR EQUAL TO
0x2289    ⊉  		# NEITHER A SUPERSET OF NOR EQUAL TO
0x228A    ⊊  		# SUBSET OF WITH NOT EQUAL TO
0x228B    ⊋  		# SUPERSET OF WITH NOT EQUAL TO
0x228E    ⊎  		# MULTISET UNION
0x228F    ⊏  		# SQUARE IMAGE OF
0x2290    ⊐  		# SQUARE ORIGINAL OF
0x2291    ⊑  		# SQUARE IMAGE OF OR EQUAL TO
0x2292    ⊒  		# SQUARE ORIGINAL OF OR EQUAL TO
0x2293    ⊓  		# SQUARE CAP
0x2294    ⊔  		# SQUARE CUP
0x2295    ⊕  		# CIRCLED PLUS
0x2296    ⊖  		# CIRCLED MINUS
0x2297    ⊗  		# CIRCLED TIMES
0x2298    ⊘  		# CIRCLED DIVISION SLASH
0x2299    ⊙  		# CIRCLED DOT OPERATOR
0x229A    ⊚  		# CIRCLED RING OPERATOR
0x229B    ⊛  		# CIRCLED ASTERISK OPERATOR
0x229D    ⊝  		# CIRCLED DASH
0x229E    ⊞  		# SQUARED PLUS
0x229F    ⊟  		# SQUARED MINUS
0x22A0    ⊠  		# SQUARED TIMES
0x22A1    ⊡  		# SQUARED DOT OPERATOR
0x22A2    ⊢  		# RIGHT TACK
0x22A3    ⊣  		# LEFT TACK
0x22A4    ⊤  		# DOWN TACK
0x22A5    ⊥  		# UP TACK
0x22A7    ⊧  		# MODELS
0x22A8    ⊨  		# TRUE
0x22A9    ⊩  		# FORCES
0x22AA    ⊪  		# TRIPLE VERTICAL BAR RIGHT TURNSTILE
0x22AC    ⊬  		# DOES NOT PROVE
0x22AD    ⊭  		# NOT TRUE
0x22AE    ⊮  		# DOES NOT FORCE
0x22AF    ⊯  		# NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
0x22B2    ⊲  		# NORMAL SUBGROUP OF
0x22B3    ⊳  		# CONTAINS AS NORMAL SUBGROUP
0x22B4    ⊴  		# NORMAL SUBGROUP OF OR EQUAL TO
0x22B5    ⊵  		# CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
0x22B8    ⊸  		# MULTIMAP
0x22BA    ⊺  		# INTERCALATE
0x22BB    ⊻  		# XOR
0x22BC    ⊼  		# NAND
0x22C4    ⋄  		# DIAMOND OPERATOR
0x22C5    ⋅  		# DOT OPERATOR
0x22C6    ⋆  		# STAR OPERATOR
0x22C7    ⋇  		# DIVISION TIMES
0x22C8    ⋈  		# BOWTIE
0x22C9    ⋉  		# LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
0x22CA    ⋊  		# RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
0x22CB    ⋋  		# LEFT SEMIDIRECT PRODUCT
0x22CC    ⋌  		# RIGHT SEMIDIRECT PRODUCT
0x22CD    ⋍  		# REVERSED TILDE EQUALS
0x22CE    ⋎  		# CURLY LOGICAL OR
0x22CF    ⋏  		# CURLY LOGICAL AND
0x22D0    ⋐  		# DOUBLE SUBSET
0x22D1    ⋑  		# DOUBLE SUPERSET
0x22D2    ⋒  		# DOUBLE INTERSECTION
0x22D3    ⋓  		# DOUBLE UNION
0x22D4    ⋔  		# PITCHFORK
0x22D6    ⋖  		# LESS-THAN WITH DOT
0x22D7    ⋗  		# GREATER-THAN WITH DOT
0x22D8    ⋘  		# VERY MUCH LESS-THAN
0x22D9    ⋙  		# VERY MUCH GREATER-THAN
0x22DA    ⋚  		# LESS-THAN EQUAL TO OR GREATER-THAN
0x22DB    ⋛  		# GREATER-THAN EQUAL TO OR LESS-THAN
0x22DC    ⋜  		# EQUAL TO OR LESS-THAN
0x22DD    ⋝  		# EQUAL TO OR GREATER-THAN
0x22DE    ⋞  		# EQUAL TO OR PRECEDES
0x22DF    ⋟  		# EQUAL TO OR SUCCEEDS
0x22E0    ⋠  		# DOES NOT PRECEDE OR EQUAL
0x22E1    ⋡  		# DOES NOT SUCCEED OR EQUAL
0x22E6    ⋦  		# LESS-THAN BUT NOT EQUIVALENT TO
0x22E7    ⋧  		# GREATER-THAN BUT NOT EQUIVALENT TO
0x22E8    ⋨  		# PRECEDES BUT NOT EQUIVALENT TO
0x22E9    ⋩  		# SUCCEEDS BUT NOT EQUIVALENT TO
0x22EA    ⋪  		# NOT NORMAL SUBGROUP OF
0x22EB    ⋫  		# DOES NOT CONTAIN AS NORMAL SUBGROUP
0x22EC    ⋬  		# NOT NORMAL SUBGROUP OF OR EQUAL TO
0x22ED    ⋭  		# DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
0x22EE    ⋮  		# VERTICAL ELLIPSIS
0x2306    ⌆  		# PERSPECTIVE
0x2308    ⌈  		# LEFT CEILING
0x2309    ⌉  		# RIGHT CEILING
0x230A    ⌊  		# LEFT FLOOR
0x230B    ⌋  		# RIGHT FLOOR
0x230C    ⌌  		# BOTTOM RIGHT CROP
0x230D    ⌍  		# BOTTOM LEFT CROP
0x230E    ⌎  		# TOP RIGHT CROP
0x230F    ⌏  		# TOP LEFT CROP
0x2315    ⌕  		# TELEPHONE RECORDER
0x2316    ⌖  		# POSITION INDICATOR
0x231C    ⌜  		# TOP LEFT CORNER
0x231D    ⌝  		# TOP RIGHT CORNER
0x231E    ⌞  		# BOTTOM LEFT CORNER
0x231F    ⌟  		# BOTTOM RIGHT CORNER
0x2322    ⌢  		# FROWN
0x2323    ⌣  		# SMILE
0x2329    〈  		# LEFT-POINTING ANGLE BRACKET
0x232A    〉  		# RIGHT-POINTING ANGLE BRACKET
0x2423    ␣  		# OPEN BOX
0x24C8    Ⓢ  		# CIRCLED LATIN CAPITAL LETTER S
0x2500    ─  		# BOX DRAWINGS LIGHT HORIZONTAL
0x2502    │  		# BOX DRAWINGS LIGHT VERTICAL
0x250C    ┌  		# BOX DRAWINGS LIGHT DOWN AND RIGHT
0x2510    ┐  		# BOX DRAWINGS LIGHT DOWN AND LEFT
0x2514    └  		# BOX DRAWINGS LIGHT UP AND RIGHT
0x2518    ┘  		# BOX DRAWINGS LIGHT UP AND LEFT
0x251C    ├  		# BOX DRAWINGS LIGHT VERTICAL AND RIGHT
0x2524    ┤  		# BOX DRAWINGS LIGHT VERTICAL AND LEFT
0x252C    ┬  		# BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
0x2534    ┴  		# BOX DRAWINGS LIGHT UP AND HORIZONTAL
0x253C    ┼  		# BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
0x2550    ═  		# BOX DRAWINGS DOUBLE HORIZONTAL
0x2551    ║  		# BOX DRAWINGS DOUBLE VERTICAL
0x2552    ╒  		# BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
0x2553    ╓  		# BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
0x2554    ╔  		# BOX DRAWINGS DOUBLE DOWN AND RIGHT
0x2555    ╕  		# BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
0x2556    ╖  		# BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
0x2557    ╗  		# BOX DRAWINGS DOUBLE DOWN AND LEFT
0x2558    ╘  		# BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
0x2559    ╙  		# BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
0x255A    ╚  		# BOX DRAWINGS DOUBLE UP AND RIGHT
0x255B    ╛  		# BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
0x255C    ╜  		# BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
0x255D    ╝  		# BOX DRAWINGS DOUBLE UP AND LEFT
0x255E    ╞  		# BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
0x255F    ╟  		# BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
0x2560    ╠  		# BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
0x2561    ╡  		# BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
0x2562    ╢  		# BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
0x2563    ╣  		# BOX DRAWINGS DOUBLE VERTICAL AND LEFT
0x2564    ╤  		# BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
0x2565    ╥  		# BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
0x2566    ╦  		# BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
0x2567    ╧  		# BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
0x2568    ╨  		# BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
0x2569    ╩  		# BOX DRAWINGS DOUBLE UP AND HORIZONTAL
0x256A    ╪  		# BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
0x256B    ╫  		# BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
0x256C    ╬  		# BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
0x2580    ▀  		# UPPER HALF BLOCK
0x2584    ▄  		# LOWER HALF BLOCK
0x2588    █  		# FULL BLOCK
0x2591    ░  		# LIGHT SHADE
0x2592    ▒  		# MEDIUM SHADE
0x2593    ▓  		# DARK SHADE
0x25A1    □  		# WHITE SQUARE
0x25AA    ▪  		# BLACK SMALL SQUARE
0x25AD    ▭  		# WHITE RECTANGLE
0x25AE    ▮  		# BLACK VERTICAL RECTANGLE
0x25B3    △  		# WHITE UP-POINTING TRIANGLE
0x25B4    ▴  		# BLACK UP-POINTING SMALL TRIANGLE
0x25B5    ▵  		# WHITE UP-POINTING SMALL TRIANGLE
0x25B8    ▸  		# BLACK RIGHT-POINTING SMALL TRIANGLE
0x25B9    ▹  		# WHITE RIGHT-POINTING SMALL TRIANGLE
0x25BD    ▽  		# WHITE DOWN-POINTING TRIANGLE
0x25BE    ▾  		# BLACK DOWN-POINTING SMALL TRIANGLE
0x25BF    ▿  		# WHITE DOWN-POINTING SMALL TRIANGLE
0x25C2    ◂  		# BLACK LEFT-POINTING SMALL TRIANGLE
0x25C3    ◃  		# WHITE LEFT-POINTING SMALL TRIANGLE
0x25CA    ◊  		# LOZENGE
0x25CB    ○  		# WHITE CIRCLE
0x2605    ★  		# BLACK STAR
0x2606    ☆  		# WHITE STAR
0x260E    ☎  		# BLACK TELEPHONE
0x2640    ♀  		# FEMALE SIGN
0x2642    ♂  		# MALE SIGN
0x2660    ♠  		# BLACK SPADE SUIT
0x2663    ♣  		# BLACK CLUB SUIT
0x2665    ♥  		# BLACK HEART SUIT
0x2666    ♦  		# BLACK DIAMOND SUIT
0x266A    ♪  		# EIGHTH NOTE
0x266D    ♭  		# MUSIC FLAT SIGN
0x266E    ♮  		# MUSIC NATURAL SIGN
0x266F    ♯  		# MUSIC SHARP SIGN
0x2713    ✓  		# CHECK MARK
0x2717    ✗  		# BALLOT X
0x2720    ✠  		# MALTESE CROSS
0x2726    ✦  		# BLACK FOUR POINTED STAR
0x2727    ✧  		# WHITE FOUR POINTED STAR
0x2736    ✶  		# SIX POINTED BLACK STAR
0xFB00    ff  		# LATIN SMALL LIGATURE FF
0xFB01    fi  		# LATIN SMALL LIGATURE FI
0xFB02    fl  		# LATIN SMALL LIGATURE FL
0xFB03    ffi  		# LATIN SMALL LIGATURE FFI
0xFB04    ffl  		# LATIN SMALL LIGATURE FFL


lynx2-8-8/test/utf-8-demo.html000644 023711 023712 00000075532 07550154260 016525 0ustar00dickeylynx000000 000000 Markus Kuhn's UTF-8 demo
UTF-8 encoded sample plain-text file
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾

Markus Kuhn [ˈmaʳkʊs kuːn] <mkuhn@acm.org> — 1999-08-20


The ASCII compatible UTF-8 encoding of ISO 10646 and Unicode
plain-text files is defined in RFC 2279 and in ISO 10646-1 Annex R.


Using Unicode/UTF-8, you can write in emails and source code things such as

Mathematics and Sciences:

  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),

  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),

  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm

Linguistics and dictionaries:

  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]

APL:

  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈

Nicer typography in plain text files:

  ╔══════════════════════════════════════════╗
  ║                                          ║
  ║   • ‘single’ and “double” quotes         ║
  ║                                          ║
  ║   • Curly apostrophes: “We’ve been here” ║
  ║                                          ║
  ║   • Latin-1 apostrophe and accents: '´`  ║
  ║                                          ║
  ║   • ‚deutsche‘ „Anführungszeichen“       ║
  ║                                          ║
  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║
  ║                                          ║
  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║
  ║                      ╭─────────╮         ║
  ║   • the euro symbol: │ € 14.95 │         ║
  ║                      ╰─────────╯         ║
  ╚══════════════════════════════════════════╝

Greek (in Polytonic):

  The Greek anthem:

  Σὲ γνωρίζω ἀπὸ τὴν κόψη
  τοῦ σπαθιοῦ τὴν τρομερή,
  σὲ γνωρίζω ἀπὸ τὴν ὄψη
  ποὺ μὲ βία μετράει τὴ γῆ.

  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
  τῶν ῾Ελλήνων τὰ ἱερά
  καὶ σὰν πρῶτα ἀνδρειωμένη
  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!

  From a speech of Demosthenes in the 4th century BC:

  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ 
  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.

  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς

Georgian:

  From a Unicode conference invitation:

  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.

Russian:

  From a Unicode conference invitation:

  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
  Конференция соберет широкий круг экспертов по  вопросам глобального
  Интернета и Unicode, локализации и интернационализации, воплощению и
  применению Unicode в различных операционных системах и программных
  приложениях, шрифтах, верстке и многоязычных компьютерных системах.

Thai (UCS Level 2):

  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
  classic 'San Gua'):

  [----------------------------|------------------------]
    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่
  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา
    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา
  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ
    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ
  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้
  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ

  (The above is a two-column text. If combining characters are handled
  correctly, the lines of the second column should be aligned with the
  | character above.)

Ethiopian:

  Proverbs in the Amharic language:

  ሰማይ አይታረስ ንጉሥ አይከሰስ።
  ብላ ካለኝ እንደአባቴ በቆመጠኝ።
  ጌጥ ያለቤቱ ቁምጥና ነው።
  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
  የአፍ ወለምታ በቅቤ አይታሽም።
  አይጥ በበላ ዳዋ ተመታ።
  ሲተረጉሙ ይደረግሙ።
  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
  ድር ቢያብር አንበሳ ያስር።
  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
  ሥራ ከመፍታት ልጄን ላፋታት።
  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
  ተንጋሎ ቢተፉ ተመልሶ ባፉ።
  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
  እግርህን በፍራሽህ ልክ ዘርጋ።

Runes:

  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ

  (Old English, which transcribed into Latin reads 'He cwaeth that he
  bude thaem lande northweardum with tha Westsae.' and means 'He said
  that he lived in the northern land near the Western Sea.')

Braille:

  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌

  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ 
  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲

  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲

  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ 
  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ 
  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲

  (The first couple of paragraphs of "A Christmas Carol" by Dickens)

Compact font selection example text:

  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა

Greetings in various languages:

  Hello world, Καλημέρα κόσμε, コンニチハ

Box drawing alignment tests:                                          █
                                                                      ▉
  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳
  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳
  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳
  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎
  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏
  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛           └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█

lynx2-8-8/test/triangle.html000644 023711 023712 00000000377 12264313047 016436 0ustar00dickeylynx000000 000000 Test ImageMap - triangle

TRIANGLE

lynx2-8-8/test/circle.html000644 023711 023712 00000000373 12264313047 016066 0ustar00dickeylynx000000 000000 Test ImageMap - circle

CIRCLE

lynx2-8-8/src/AttrList.h000644 023711 023712 00000002557 12175560333 015477 0ustar00dickeylynx000000 000000 /* * $LynxId: AttrList.h,v 1.17 2013/05/03 20:54:09 tom Exp $ */ #if !defined(__ATTRLIST_H) #define __ATTRLIST_H #include #include #ifdef __cplusplus extern "C" { #endif enum { ABS_OFF = 0, STACK_OFF = 0, STACK_ON, ABS_ON }; #define STARTAT 8 enum { DSTYLE_LINK = HTML_A + STARTAT, DSTYLE_STATUS = HTML_ELEMENTS + STARTAT, DSTYLE_ALINK, /* active link */ DSTYLE_NORMAL, /* default attributes */ DSTYLE_OPTION, /* option on the option screen */ DSTYLE_VALUE, /* value on the option screen */ DSTYLE_CANDY, /* possibly going to vanish */ DSTYLE_WHEREIS, /* whereis search target */ DSTYLE_ELEMENTS }; typedef struct { int color; /* color highlighting to be done */ int mono; /* mono highlighting to be done */ int cattr; /* attributes to go with the color */ } HTCharStyle; #if 0 #define HText_characterStyle CTRACE((tfp,"HTC called from %s/%d\n",__FILE__,__LINE__));_internal_HTC #else #define HText_characterStyle _internal_HTC #endif #if defined(USE_COLOR_STYLE) extern void _internal_HTC(HText *text, int style, int dir); #define TEMPSTRINGSIZE 256 extern char class_string[TEMPSTRINGSIZE + 1]; /* stack of attributes during page rendering */ #define MAX_LAST_STYLES 128 extern int last_styles[MAX_LAST_STYLES + 1]; extern int last_colorattr_ptr; #endif #ifdef __cplusplus } #endif #endif lynx2-8-8/src/DefaultStyle.c000644 023711 023712 00000030233 11365151736 016324 0ustar00dickeylynx000000 000000 /* * $LynxId: DefaultStyle.c,v 1.20 2009/11/27 13:04:27 tom Exp $ * * A real style sheet for the Character Grid browser * * The dimensions are all in characters! */ #include #include #include #include #include /* Tab arrays: */ static const HTTabStop tabs_8[] = { {0, 8}, {0, 16}, {0, 24}, {0, 32}, {0, 40}, {0, 48}, {0, 56}, {0, 64}, {0, 72}, {0, 80}, {0, 88}, {0, 96}, {0, 104}, {0, 112}, {0, 120}, {0, 128}, {0, 136}, {0, 144}, {0, 152}, {0, 160}, {0, 168}, {0, 176}, {0, 0} /* Terminate */ }; /* Template: * link to next, name, name id (enum), tag, * font, size, colour, superscript, anchor id, * indents: 1st, left, right, alignment lineheight, descent, tabs, * word wrap, free format, space: before, after, flags. */ static HTStyle HTStyleNormal = HTStyleInit( 0, Normal, "P", HT_FONT, 1, HT_BLACK, 0, 0, 3, 3, 6, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleDivCenter = HTStyleInit( &HTStyleNormal, DivCenter, "DCENTER", HT_FONT, 1, HT_BLACK, 0, 0, 3, 3, 6, HT_CENTER, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleDivLeft = HTStyleInit( &HTStyleDivCenter, DivLeft, "DLEFT", HT_FONT, 1, HT_BLACK, 0, 0, 3, 3, 6, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleDivRight = HTStyleInit( &HTStyleDivLeft, DivRight, "DRIGHT", HT_FONT, 1, HT_BLACK, 0, 0, 3, 3, 6, HT_RIGHT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleBanner = HTStyleInit( &HTStyleDivRight, Banner, "BANNER", HT_FONT, 1, HT_BLACK, 0, 0, 3, 3, 6, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleBlockquote = HTStyleInit( &HTStyleBanner, Blockquote, "BLOCKQUOTE", HT_FONT, 1, HT_BLACK, 0, 0, 5, 5, 7, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleBq = HTStyleInit( /* HTML 3.0 BLOCKQUOTE - FM */ &HTStyleBlockquote, Bq, "BQ", HT_FONT, 1, HT_BLACK, 0, 0, 5, 5, 7, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleFootnote = HTStyleInit( /* HTML 3.0 FN - FM */ &HTStyleBq, Footnote, "FN", HT_FONT, 1, HT_BLACK, 0, 0, 5, 5, 7, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleList = HTStyleInit( &HTStyleFootnote, List, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 3, 7, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleList1 = HTStyleInit( &HTStyleList, List1, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 8, 12, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleList2 = HTStyleInit( &HTStyleList1, List2, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 13, 17, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleList3 = HTStyleInit( &HTStyleList2, List3, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 18, 22, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleList4 = HTStyleInit( &HTStyleList3, List4, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 23, 27, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleList5 = HTStyleInit( &HTStyleList4, List5, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 28, 32, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleList6 = HTStyleInit( &HTStyleList5, List6, "UL", HT_FONT, 1, HT_BLACK, 0, 0, 33, 37, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0); static HTStyle HTStyleMenu = HTStyleInit( &HTStyleList6, Menu, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 3, 7, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleMenu1 = HTStyleInit( &HTStyleMenu, Menu1, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 8, 12, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleMenu2 = HTStyleInit( &HTStyleMenu1, Menu2, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 13, 17, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleMenu3 = HTStyleInit( &HTStyleMenu2, Menu3, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 18, 22, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleMenu4 = HTStyleInit( &HTStyleMenu3, Menu4, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 23, 27, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleMenu5 = HTStyleInit( &HTStyleMenu4, Menu5, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 28, 33, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleMenu6 = HTStyleInit( &HTStyleMenu5, Menu6, "MENU", HT_FONT, 1, HT_BLACK, 0, 0, 33, 38, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossary = HTStyleInit( &HTStyleMenu6, Glossary, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 3, 10, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossary1 = HTStyleInit( &HTStyleGlossary, Glossary1, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 8, 16, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossary2 = HTStyleInit( &HTStyleGlossary1, Glossary2, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 14, 22, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossary3 = HTStyleInit( &HTStyleGlossary2, Glossary3, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 20, 28, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossary4 = HTStyleInit( &HTStyleGlossary3, Glossary4, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 26, 34, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossary5 = HTStyleInit( &HTStyleGlossary4, Glossary5, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 32, 40, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossary6 = HTStyleInit( &HTStyleGlossary5, Glossary6, "DL", HT_FONT, 1, HT_BLACK, 0, 0, 38, 46, 6, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0 ); static HTStyle HTStyleGlossaryCompact = HTStyleInit( &HTStyleGlossary6, GlossaryCompact, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 3, 10, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossaryCompact1 = HTStyleInit( &HTStyleGlossaryCompact, GlossaryCompact1, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 8, 15, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossaryCompact2 = HTStyleInit( &HTStyleGlossaryCompact1, GlossaryCompact2, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 13, 20, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossaryCompact3 = HTStyleInit( &HTStyleGlossaryCompact2, GlossaryCompact3, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 18, 25, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossaryCompact4 = HTStyleInit( &HTStyleGlossaryCompact3, GlossaryCompact4, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 23, 30, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossaryCompact5 = HTStyleInit( &HTStyleGlossaryCompact4, GlossaryCompact5, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 28, 35, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleGlossaryCompact6 = HTStyleInit( &HTStyleGlossaryCompact5, GlossaryCompact6, "DLC", HT_FONT, 1, HT_BLACK, 0, 0, 33, 40, 6, HT_LEFT, 1, 0, 0, YES, YES, 0, 0, 0 ); static HTStyle HTStyleExample = HTStyleInit( &HTStyleGlossaryCompact6, Example, "XMP", HT_FONT, 1, HT_BLACK, 0, 0, 0, 0, 0, HT_LEFT, 1, 0, tabs_8, NO, NO, 0, 0, 0 ); static HTStyle HTStylePreformatted = HTStyleInit( &HTStyleExample, Preformatted, "PRE", HT_FONT, 1, HT_BLACK, 0, 0, 0, 0, 0, HT_LEFT, 1, 0, tabs_8, NO, NO, 0, 0, 0 ); static HTStyle HTStyleListing = HTStyleInit( &HTStylePreformatted, Listing, "LISTING", HT_FONT, 1, HT_BLACK, 0, 0, 0, 0, 0, HT_LEFT, 1, 0, tabs_8, NO, NO, 0, 0, 0); static HTStyle HTStyleAddress = HTStyleInit( &HTStyleListing, Address, "ADDRESS", HT_FONT, 1, HT_BLACK, 0, 0, 4, 4, 7, HT_LEFT, 1, 0, tabs_8, YES, YES, 2, 0, 0); static HTStyle HTStyleNote = HTStyleInit( /* HTML 3.0 NOTE - FM */ &HTStyleAddress, Note, "NOTE", HT_FONT, 1, HT_BLACK, 0, 0, 5, 5, 7, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleHeading1 = HTStyleInit( &HTStyleNote, Heading1, "H1", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 0, 0, 0, HT_CENTER, 1, 0, 0, YES, YES, 1, 1, 0); static HTStyle HTStyleHeading2 = HTStyleInit( &HTStyleHeading1, Heading2, "H2", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 0, 0, 0, HT_LEFT, 1, 0, 0, YES, YES, 1, 1, 0); static HTStyle HTStyleHeading3 = HTStyleInit( &HTStyleHeading2, Heading3, "H3", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 2, 2, 0, HT_LEFT, 1, 0, 0, YES, YES, 1, 0, 0); static HTStyle HTStyleHeading4 = HTStyleInit( &HTStyleHeading3, Heading4, "H4", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 4, 4, 0, HT_LEFT, 1, 0, 0, YES, YES, 1, 0, 0); static HTStyle HTStyleHeading5 = HTStyleInit( &HTStyleHeading4, Heading5, "H5", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 6, 6, 0, HT_LEFT, 1, 0, 0, YES, YES, 1, 0, 0); static HTStyle HTStyleHeading6 = HTStyleInit( &HTStyleHeading5, Heading6, "H6", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 8, 8, 0, HT_LEFT, 1, 0, 0, YES, YES, 1, 0, 0); static HTStyle HTStyleHeadingCenter = HTStyleInit( &HTStyleHeading6, HeadingCenter, "HCENTER", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 0, 0, 3, HT_CENTER, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleHeadingLeft = HTStyleInit( &HTStyleHeadingCenter, HeadingLeft, "HLEFT", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 0, 0, 3, HT_LEFT, 1, 0, tabs_8, YES, YES, 1, 0, 0); static HTStyle HTStyleHeadingRight = HTStyleInit( &HTStyleHeadingLeft, HeadingRight, "HRIGHT", HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0, 0, 0, 3, HT_RIGHT, 1, 0, tabs_8, YES, YES, 1, 0, 0); /* Style sheet points to the last in the list: */ static HTStyleSheet sheet = {"default.style", &HTStyleHeadingRight}; /* sheet */ static HTStyle *st_array[ST_HeadingRight + 1] = {NULL}; static HTStyleSheet *result = NULL; #ifdef LY_FIND_LEAKS static void FreeDefaultStyle(void) { HTStyle *style; while ((style = result->styles) != 0) { result->styles = style->next; FREE(style); } FREE(result); } #endif /* LY_FIND_LEAKS */ HTStyleSheet *DefaultStyle(HTStyle ***result_array) { HTStyle *p, *q; /* * The first time we're called, allocate a copy of the 'sheet' linked * list. Thereafter, simply copy the data from 'sheet' into our copy * (preserving the copy's linked-list pointers). We do this to reset the * parameters of a style that might be altered while processing a page. */ if (result == 0) { /* allocate & copy */ result = HTStyleSheetNew(); *result = sheet; result->styles = 0; #ifdef LY_FIND_LEAKS atexit(FreeDefaultStyle); #endif for (p = sheet.styles; p != 0; p = p->next) { q = HTStyleNew(); *q = *p; if (no_margins) { q->indent1st = 0; q->leftIndent = 0; q->rightIndent = 0; } st_array[q->id] = q; q->next = result->styles; result->styles = q; } } else { /* recopy the data */ for (q = result->styles, p = sheet.styles; p != 0 && q != 0; p = p->next, q = q->next) { HTStyle *r = q->next; *q = *p; if (no_margins) { q->indent1st = 0; q->leftIndent = 0; q->rightIndent = 0; } st_array[q->id] = q; q->next = r; } } *result_array = st_array; return result; } lynx2-8-8/src/GridText.c000644 023711 023712 00001406276 12245762550 015470 0ustar00dickeylynx000000 000000 /* * $LynxId: GridText.c,v 1.274 2013/11/28 11:16:50 tom Exp $ * * Character grid hypertext object * =============================== */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* LYUCTranslateBack... */ #include #include #include #include #include #include #include #ifdef EXP_CHARTRANS_AUTOSWITCH #include #endif /* EXP_CHARTRANS_AUTOSWITCH */ #include #include #ifdef USE_COLOR_STYLE #include #include #include #endif #include #define is_CJK2(b) (IS_CJK_TTY && is8bits(UCH(b))) #ifdef USE_CURSES_PADS # define DISPLAY_COLS (LYwideLines ? MAX_COLS : LYcols) # define WRAP_COLS(text) ((text)->stbl ? \ (LYtableCols <= 0 \ ? DISPLAY_COLS \ : (LYtableCols * LYcols)/12) - LYbarWidth \ : LYcolLimit) #else # define DISPLAY_COLS LYcols # define WRAP_COLS(text) LYcolLimit #endif #define FirstHTLine(text) ((text)->last_line->next) #define LastHTLine(text) ((text)->last_line) static void HText_trimHightext(HText *text, int final, int stop_before); #define IS_UTF_EXTRA(ch) (text->T.output_utf8 && \ (UCH((ch))&0xc0) == 0x80) #define IS_UTF8_EXTRA(ch) (!(text && text->T.output_utf8) || \ !is8bits(ch) || \ (UCH(line->data[i] & 0xc0) == 0xc0)) /* a test in compact form: how many extra UTF-8 chars after initial? - kw */ #define UTF8_XNEGLEN(c) (c&0xC0? 0 :c&32? 1 :c&16? 2 :c&8? 3 :c&4? 4 :c&2? 5:0) #define UTF_XLEN(c) UTF8_XNEGLEN(((char)~(c))) #ifdef KANJI_CODE_OVERRIDE HTkcode last_kcode = NOKANJI; /* 1997/11/14 (Fri) 09:09:26 */ #endif #ifdef CJK_EX #define CHAR_WIDTH 6 #else #define CHAR_WIDTH 1 #endif /* Exports */ HText *HTMainText = NULL; /* Equivalent of main window */ HTParentAnchor *HTMainAnchor = NULL; /* Anchor for HTMainText */ const char *HTAppName = LYNX_NAME; /* Application name */ const char *HTAppVersion = LYNX_VERSION; /* Application version */ static int HTFormNumber = 0; static int HTFormFields = 0; static char *HTCurSelectGroup = NULL; /* Form select group name */ static int HTCurSelectGroupCharset = -1; /* ... and name's charset */ int HTCurSelectGroupType = F_RADIO_TYPE; /* Group type */ char *HTCurSelectGroupSize = NULL; /* Length of select */ static char *HTCurSelectedOptionValue = NULL; /* Select choice */ const char *checked_box = "[X]"; const char *unchecked_box = "[ ]"; const char *checked_radio = "(*)"; const char *unchecked_radio = "( )"; static BOOLEAN underline_on = FALSE; static BOOLEAN bold_on = FALSE; #ifdef USE_SOURCE_CACHE int LYCacheSource = SOURCE_CACHE_NONE; int LYCacheSourceForAborted = SOURCE_CACHE_FOR_ABORTED_DROP; #endif #ifdef USE_SCROLLBAR BOOLEAN LYShowScrollbar = FALSE; BOOLEAN LYsb_arrow = TRUE; int LYsb_begin = -1; int LYsb_end = -1; #endif #ifndef VMS /* VMS has a better way - right? - kw */ #define CHECK_FREE_MEM #endif #ifdef CHECK_FREE_MEM static void *LY_check_calloc(size_t nmemb, size_t size); #define LY_CALLOC LY_check_calloc #else /* using the regular calloc */ #define LY_CALLOC calloc #endif /* * The HTPool.data[] array has to align the same as malloc() would, to make the * ALLOC_POOL scheme portable. For many platforms, that is the same as the * number of bytes in a pointer. It may be larger, e.g., on machines which * have more stringent requirements for floating point. 32-bits are plenty for * representing styles, but we may need 64-bit or 128-bit alignment. * * The real issue is that performance is degraded if the alignment is not met, * and some platforms such as Tru64 generate lots of warning messages. */ #ifndef ALIGN_SIZE #define ALIGN_SIZE sizeof(double) #endif typedef struct { unsigned int sc_direction:2; /* on or off */ unsigned int sc_horizpos:14; /* horizontal position of this change */ unsigned int sc_style:16; /* which style to change to */ } HTStyleChange; #if defined(USE_COLOR_STYLE) #define MAX_STYLES_ON_LINE 64 /* buffers used when current line is being aggregated, in split_line() */ static HTStyleChange stylechanges_buffers[2][MAX_STYLES_ON_LINE]; #endif typedef HTStyleChange pool_data; enum { POOL_SIZE = ((8192 - 4 * sizeof(void *) - sizeof(struct _HTPool *) - sizeof(int)) / sizeof(pool_data)) }; typedef struct _HTPool { pool_data data[POOL_SIZE]; struct _HTPool *prev; unsigned used; } HTPool; /************************************************************************ These are generic macros for any pools (provided those structures have the same members as HTPool). Pools are used for allocation of groups of objects of the same type T. Pools are represented as a list of structures of type P (called pool chunks here). Structure P has an array of N objects of type T named 'data' (the number N in the array can be chosen arbitrary), pointer to the previous pool chunk named 'prev', and the number of used items in that pool chunk named 'used'. Here is a definition of the structure P: struct P { T data[N]; struct P* prev; int used; }; It's recommended that sizeof(P) be memory page size minus 32 in order malloc'd chunks to fit in machine page size. Allocation of 'n' items in the pool is implemented by incrementing member 'used' by 'n' if (used+n <= N), or malloc a new pool chunk and allocating 'n' items in that new chunk. It's the task of the programmer to assert that 'n' is <= N. Only entire pool may be freed - this limitation makes allocation algorithms trivial and fast - so the use of pools is limited to objects that are freed in batch, that are not deallocated not in the batch, and not reallocated. Pools greatly reduce memory fragmentation and memory allocation/deallocation speed due to the simple algorithms used. Due to the fact that memory is 'allocated' in array, alignment overhead is minimal. Allocating strings in a pool provided their length will never exceed N and is much smaller than N seems to be very efficient. [Several types of memory-hungry objects are stored in the pool now: styles, lines, anchors, and FormInfo. Arrays of HTStyleChange are stored as is, other objects are stored using a cast.] Pool is referenced by the pointer to the last chunk that contains free slots. Functions that allocate memory in the pool update that pointer if needed. There are 3 functions - POOL_NEW, POOL_FREE, and ALLOC_IN_POOL. - VH *************************************************************************/ #define POOLallocstyles(ptr, n) ptr = ALLOC_IN_POOL(&HTMainText->pool, (unsigned) ((n) * sizeof(pool_data))) #define POOLallocHTLine(ptr, size) ptr = (HTLine*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) LINE_SIZE(size)) #define POOLallocstring(ptr, len) ptr = (char*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) ((len) + 1)) #define POOLtypecalloc(T, ptr) ptr = (T*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) sizeof(T)) /**************************************************************************/ /* * Allocates 'n' items in the pool of type 'HTPool' pointed by 'poolptr'. * Returns a pointer to the "allocated" memory if successful. * Updates 'poolptr' if necessary. */ static void *ALLOC_IN_POOL(HTPool ** ppoolptr, unsigned request) { HTPool *pool = *ppoolptr; pool_data *ptr; unsigned n; unsigned j; if (!pool) { outofmem(__FILE__, "ALLOC_IN_POOL"); } else { n = request; if (n == 0) n = 1; j = (n % ALIGN_SIZE); if (j != 0) n += (unsigned) (ALIGN_SIZE - j); n /= sizeof(pool_data); if (POOL_SIZE >= (pool->used + n)) { ptr = pool->data + pool->used; pool->used += n; } else { HTPool *newpool = (HTPool *) LY_CALLOC((size_t) 1, sizeof(HTPool)); if (!newpool) { outofmem(__FILE__, "ALLOC_IN_POOL"); } else { newpool->prev = pool; newpool->used = n; ptr = newpool->data; *ppoolptr = newpool; } } } return ptr; } /* * Returns a pointer to initialized pool of type 'HTPool', or NULL if fails. */ static HTPool *POOL_NEW(void) { HTPool *poolptr = (HTPool *) LY_CALLOC((size_t) 1, sizeof(HTPool)); if (poolptr) { poolptr->prev = NULL; poolptr->used = 0; } return poolptr; } /* * Frees a pool of type 'HTPool' pointed by poolptr. */ static void POOL_FREE(HTPool * poolptr) { HTPool *cur = poolptr; HTPool *prev; while (cur) { prev = cur->prev; free(cur); cur = prev; } } /**************************************************************************/ /**************************************************************************/ typedef struct _line { struct _line *next; struct _line *prev; unsigned short offset; /* Implicit initial spaces */ unsigned short size; /* Number of characters */ #if defined(USE_COLOR_STYLE) HTStyleChange *styles; unsigned short numstyles; #endif char data[1]; /* Space for terminator at least! */ } HTLine; /* Allow for terminator */ #define LINE_SIZE(size) (sizeof(HTLine) + (size_t)(size)) #ifndef HTLINE_NOT_IN_POOL #define HTLINE_NOT_IN_POOL 0 /* debug with this set to 1 */ #endif #if HTLINE_NOT_IN_POOL #define allocHTLine(ptr, size) { ptr = (HTLine *)calloc(1, LINE_SIZE(size)); } #define freeHTLine(self, ptr) { \ if (ptr && ptr != TEMP_LINE(self, 0) && ptr != TEMP_LINE(self, 1)) \ FREE(ptr); \ } #else #define allocHTLine(ptr, size) POOLallocHTLine(ptr, size) #define freeHTLine(self, ptr) {} #endif /* * Last line buffer; the second is used in split_line(). Not in pool! * We cannot wrap in middle of multibyte sequences, so allocate 2 extra * for a workspace. This is stored in the HText, to prevent confusion * between different documents. Note also that it is declared with an * HTLine at the beginning so pointers will be properly aligned. */ typedef struct { HTLine base; char data[MAX_LINE + 2]; } HTLineTemp; #define TEMP_LINE(p,n) ((HTLine *)&(p->temp_line[n])) typedef struct _TextAnchor { struct _TextAnchor *next; struct _TextAnchor *prev; /* www_user_search only! */ int sgml_offset; /* used for updating position after reparsing */ int number; /* For user interface */ int show_number; /* For user interface (unique-urls) */ int line_num; /* Place in document */ short line_pos; /* Bytes/chars - extent too */ short extent; /* (see HText_trimHightext) */ BOOL show_anchor; /* Show the anchor? */ BOOL inUnderline; /* context is underlined */ BOOL expansion_anch; /* TEXTAREA edit new anchor */ char link_type; /* Normal, internal, or form? */ FormInfo *input_field; /* Info for form links */ HiliteList lites; HTChildAnchor *anchor; } TextAnchor; typedef struct { char *name; /* ID value of TAB */ int column; /* Zero-based column value */ } HTTabID; typedef enum { S_text, S_esc, S_dollar, S_paren, S_nonascii_text, S_dollar_paren, S_jisx0201_text } eGridState; /* Escape sequence? */ #ifdef USE_TH_JP_AUTO_DETECT typedef enum { /* Detected Kanji code */ DET_SJIS, DET_EUC, DET_NOTYET, DET_MIXED } eDetectedKCode; typedef enum { SJIS_state_neutral, SJIS_state_in_kanji, SJIS_state_has_bad_code } eSJIS_status; typedef enum { EUC_state_neutral, EUC_state_in_kanji, EUC_state_in_kana, EUC_state_has_bad_code } eEUC_status; #endif /* Notes on struct _HText: * next_line is valid if stale is false. * top_of_screen line means the line at the top of the screen * or just under the title if there is one. */ struct _HText { HTParentAnchor *node_anchor; HTLine *last_line; HTLineTemp temp_line[2]; int Lines; /* Number of them */ TextAnchor *first_anchor; /* double-linked on demand */ TextAnchor *last_anchor; TextAnchor *last_anchor_before_stbl; TextAnchor *last_anchor_before_split; HTList *forms; /* also linked internally */ int last_anchor_number; /* user number */ BOOL source; /* Is the text source? */ BOOL toolbar; /* Toolbar set? */ HTList *tabs; /* TAB IDs */ HTList *hidden_links; /* Content-less links ... */ int hiddenlinkflag; /* ... and how to treat them */ BOOL no_cache; /* Always refresh? */ char LastChar; /* For absorbing white space */ /* For Internal use: */ HTStyle *style; /* Current style */ int display_on_the_fly; /* Lines left */ int top_of_screen; /* Line number */ HTLine *top_of_screen_line; /* Top */ HTLine *next_line; /* Bottom + 1 */ unsigned permissible_split; /* in last line */ BOOL in_line_1; /* of paragraph */ BOOL stale; /* Must refresh */ BOOL page_has_target; /* has target on screen */ BOOL has_utf8; /* has utf-8 on screen or line */ BOOL had_utf8; /* had utf-8 when last displayed */ int next_number; /* next a->number value */ #ifdef DISP_PARTIAL int first_lineno_last_disp_partial; int last_lineno_last_disp_partial; #endif STable_info *stbl; HTList *enclosed_stbl; HTkcode kcode; /* Kanji code? */ HTkcode specified_kcode; /* Specified Kanji code */ #ifdef USE_TH_JP_AUTO_DETECT eDetectedKCode detected_kcode; eSJIS_status SJIS_status; eEUC_status EUC_status; #endif eGridState state; /* Escape sequence? */ int kanji_buf; /* Lead multibyte */ int in_sjis; /* SJIS flag */ int halted; /* emergency halt */ BOOL have_8bit_chars; /* Any non-ASCII chars? */ LYUCcharset *UCI; /* node_anchor UCInfo */ int UCLYhndl; /* charset we are fed */ UCTransParams T; HTStream *target; /* Output stream */ HTStreamClass targetClass; /* Output routines */ HTPool *pool; /* this HText memory pool */ #ifdef USE_SOURCE_CACHE /* * Parse settings when this HText was generated. */ BOOL clickable_images; BOOL pseudo_inline_alts; BOOL verbose_img; BOOL raw_mode; BOOL historical_comments; BOOL minimal_comments; BOOL soft_dquotes; short old_dtd; short keypad_mode; short disp_lines; /* Screen size */ short disp_cols; /* Used for reports only */ #endif }; /* exported */ void *HText_pool_calloc(HText *text, unsigned size) { return (void *) ALLOC_IN_POOL(&text->pool, size); } static void HText_AddHiddenLink(HText *text, TextAnchor *textanchor); #ifdef USE_JUSTIFY_ELTS BOOL can_justify_here; BOOL can_justify_here_saved; BOOL can_justify_this_line; /* =FALSE if line contains form objects */ int wait_for_this_stacked_elt; /* -1 if can justify contents of the element on the op of stack. If positive - specifies minimal stack depth plus 1 at which we can justify element (can be MAX_LINE+2 if ok_justify ==FALSE or in psrcview. */ BOOL form_in_htext; /*to indicate that we are in form (since HTML_FORM is not stacked in the HTML.c */ BOOL in_DT = FALSE; #ifdef DEBUG_JUSTIFY BOOL can_justify_stack_depth; /* can be 0 or 1 if all code is correct */ #endif typedef struct { int byte_len; /*length in bytes */ int cell_len; /*length in cells */ } ht_run_info; static int justify_start_position; /* this is an index of char from which justification can start (eg after "* " preceeding
  • text) */ static int ht_num_runs; /*the number of runs filled */ static ht_run_info ht_runs[MAX_LINE]; static BOOL this_line_was_split; static TextAnchor *last_anchor_of_previous_line; static BOOL have_raw_nbsps = FALSE; void ht_justify_cleanup(void) { wait_for_this_stacked_elt = !ok_justify # ifdef USE_PRETTYSRC || psrc_view # endif ? 30000 /*MAX_NESTING */ + 2 /*some unreachable value */ : -1; can_justify_here = TRUE; can_justify_this_line = TRUE; form_in_htext = FALSE; last_anchor_of_previous_line = NULL; this_line_was_split = FALSE; in_DT = FALSE; have_raw_nbsps = FALSE; } void mark_justify_start_position(void *text) { if (text && ((HText *) text)->last_line) justify_start_position = ((HText *) text)->last_line->size; } #define REALLY_CAN_JUSTIFY(text) ( (wait_for_this_stacked_elt<0) && \ ( text->style->alignment == HT_LEFT || \ text->style->alignment == HT_JUSTIFY) && \ !IS_CJK_TTY && !in_DT && \ can_justify_here && can_justify_this_line && !form_in_htext ) #endif /* USE_JUSTIFY_ELTS */ /* * Boring static variable used for moving cursor across */ #define UNDERSCORES(n) \ ((n) >= MAX_LINE ? underscore_string : &underscore_string[(MAX_LINE-1)] - (n)) static char underscore_string[MAX_LINE + 1]; char star_string[MAX_LINE + 1]; static int ctrl_chars_on_this_line = 0; /* num of ctrl chars in current line */ static int utfxtra_on_this_line = 0; /* num of UTF-8 extra bytes in line, they *also* count as ctrl chars. */ #ifdef WIDEC_CURSES #define UTFXTRA_ON_THIS_LINE 0 #else #define UTFXTRA_ON_THIS_LINE utfxtra_on_this_line #endif static HTStyle default_style = {0, NULL, "(Unstyled)", 0, NULL, "", (HTFont) 0, 1, HT_BLACK, 0, 0, 0, 0, 0, HT_LEFT, 1, 0, 0, NO, NO, 0, 0, 0}; static HTList *loaded_texts = NULL; /* A list of all those in memory */ HTList *search_queries = NULL; /* isindex and whereis queries */ #ifdef LY_FIND_LEAKS static void free_all_texts(void); #endif static BOOL HText_TrueEmptyLine(HTLine *line, HText *text, int IgnoreSpaces); static int HText_TrueLineSize(HTLine *line, HText *text, int IgnoreSpaces); #ifdef CHECK_FREE_MEM /* * text->halted = 1: have set fake 'Z' and output a message * 2: next time when HText_appendCharacter is called * it will append *** MEMORY EXHAUSTED ***, then set * to 3. * 3: normal text output will be suppressed (but not anchors, * form fields etc.) */ static void HText_halt(void) { if (HTFormNumber > 0) HText_DisableCurrentForm(); if (!HTMainText) return; if (HTMainText->halted < 2) HTMainText->halted = 2; } #define MIN_NEEDED_MEM 5000 /* * Check whether factor*min(bytes,MIN_NEEDED_MEM) is available, * or bytes if factor is 0. * MIN_NEEDED_MEM and factor together represent a security margin, * to take account of all the memory allocations where we don't check * and of buffers which may be emptied before HTCheckForInterupt() * is (maybe) called and other things happening, with some chance of * success. * This just tries to malloc() the to-be-checked-for amount of memory, * which might make the situation worse depending how allocation works. * There should be a better way... - kw */ static BOOL mem_is_avail(int factor, size_t bytes) { void *p; if (bytes < MIN_NEEDED_MEM && factor > 0) bytes = MIN_NEEDED_MEM; if (factor == 0) factor = 1; p = malloc((size_t) factor * bytes); if (p) { FREE(p); return YES; } else { return NO; } } /* * Replacement for calloc which checks for "enough" free memory * (with some security margins) and tries various recovery actions * if deemed necessary. - kw */ static void *LY_check_calloc(size_t nmemb, size_t size) { int i, n; if (mem_is_avail(4, nmemb * size)) { return (calloc(nmemb, size)); } n = HTList_count(loaded_texts); for (i = n - 1; i > 0; i--) { HText *t = (HText *) HTList_objectAt(loaded_texts, i); CTRACE((tfp, "\nBUG *** Emergency freeing document %d/%d for '%s'%s!\n", i + 1, n, ((t && t->node_anchor && t->node_anchor->address) ? t->node_anchor->address : "unknown anchor"), ((t && t->node_anchor && t->node_anchor->post_data) ? " with POST data" : ""))); HTList_removeObjectAt(loaded_texts, i); HText_free(t); if (mem_is_avail(4, nmemb * size)) { return (calloc(nmemb, size)); } } LYFakeZap(YES); if (!HTMainText || HTMainText->halted <= 1) { if (!mem_is_avail(2, nmemb * size)) { HText_halt(); if (mem_is_avail(0, (size_t) 700)) { HTAlert(gettext("Memory exhausted, display interrupted!")); } } else { if ((!HTMainText || HTMainText->halted == 0) && mem_is_avail(0, (size_t) 700)) { HTAlert(gettext("Memory exhausted, will interrupt transfer!")); if (HTMainText) HTMainText->halted = 1; } } } return (calloc(nmemb, size)); } #endif /* CHECK_FREE_MEM */ #ifdef USE_COLOR_STYLE /* * Color style information is stored with the multibyte-character offset into * the string at which the style would apply. Compute the corresponding column * so we can compare it with the updated column value after writing strings * with curses. * * The offsets count multibyte characters. Other parts of the code assume each * character uses one cell, but some CJK (or UTF-8) codes use two cells. We * need to know the number of cells. */ static int StyleToCols(HText *text, HTLine *line, int nstyle) { int result = line->offset; /* this much is spaces one byte/cell */ int nchars = line->styles[nstyle].sc_horizpos; char *data = line->data; char *last = line->size + data; int utf_extra; while (nchars > 0 && data < last) { if (IsSpecialAttrChar(*data) && *data != LY_SOFT_NEWLINE) { ++data; } else { utf_extra = (int) utf8_length(text->T.output_utf8, data); if (utf_extra++) { result += LYstrExtent(data, utf_extra, 2); data += utf_extra; } else if (is_CJK2(*data)) { data += 2; result += 2; } else { ++data; ++result; } --nchars; } } return result; } #endif /* * Clear highlight information for a given anchor * (text was allocated in the pool). */ static void LYClearHiText(TextAnchor *a) { FREE(a->lites.hl_info); a->lites.hl_base.hl_text = NULL; a->lites.hl_len = 0; } #define LYFreeHiText(a) FREE((a)->lites.hl_info) /* * Set the initial highlight information for a given anchor. */ static void LYSetHiText(TextAnchor *a, const char *text, unsigned len) { if (text != NULL) { POOLallocstring(a->lites.hl_base.hl_text, len + 1); memcpy(a->lites.hl_base.hl_text, text, (size_t) len); *(a->lites.hl_base.hl_text + len) = '\0'; a->lites.hl_len = 1; } } /* * Add highlight information for the next line of a anchor. */ static void LYAddHiText(TextAnchor *a, const char *text, int x) { HiliteInfo *have = a->lites.hl_info; size_t need = (unsigned) (a->lites.hl_len - 1); size_t want; a->lites.hl_len = (short) (a->lites.hl_len + 1); want = (size_t) (a->lites.hl_len) * sizeof(HiliteInfo); if (have != NULL) { have = (HiliteInfo *) realloc(have, want); } else { have = (HiliteInfo *) malloc(want); } a->lites.hl_info = have; POOLallocstring(have[need].hl_text, strlen(text) + 1); strcpy(have[need].hl_text, text); have[need].hl_x = (short) x; } /* * Return an offset to skip leading blanks in the highlighted link. That is * needed to avoid having the color-style paint the leading blanks. */ #ifdef USE_COLOR_STYLE static int LYAdjHiTextPos(TextAnchor *a, int count) { char *result; if (count >= a->lites.hl_len) result = NULL; else if (count > 0) result = a->lites.hl_info[count - 1].hl_text; else result = a->lites.hl_base.hl_text; return (result != 0) ? (int) (LYSkipBlanks(result) - result) : 0; } #else #define LYAdjHiTextPos(a,count) 0 #endif /* * Get the highlight text, counting from zero. */ static char *LYGetHiTextStr(TextAnchor *a, int count) { char *result; if (count >= a->lites.hl_len) result = NULL; else if (count > 0) result = a->lites.hl_info[count - 1].hl_text; else result = a->lites.hl_base.hl_text; result += LYAdjHiTextPos(a, count); return result; } /* * Get the X-ordinate at which to draw the corresponding highlight-text */ static int LYGetHiTextPos(TextAnchor *a, int count) { int result; if (count >= a->lites.hl_len) result = -1; else if (count > 0) result = a->lites.hl_info[count - 1].hl_x; else result = a->line_pos; result += LYAdjHiTextPos(a, count); return result; } /* * Copy highlighting information from anchor 'b' to 'a'. */ static void LYCopyHiText(TextAnchor *a, TextAnchor *b) { int count; char *s; LYClearHiText(a); for (count = 0;; ++count) { if ((s = LYGetHiTextStr(b, count)) == NULL) break; if (count == 0) { LYSetHiText(a, s, (unsigned) strlen(s)); } else { LYAddHiText(a, s, LYGetHiTextPos(b, count)); } } } static void HText_getChartransInfo(HText *me) { me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT); if (me->UCLYhndl < 0) { int chndl = current_char_set; HTAnchor_setUCInfoStage(me->node_anchor, chndl, UCT_STAGE_HTEXT, UCT_SETBY_STRUCTURED); me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT); } me->UCI = HTAnchor_getUCInfoStage(me->node_anchor, UCT_STAGE_HTEXT); } static void PerFormInfo_free(PerFormInfo * form) { if (form) { FREE(form->data.submit_action); FREE(form->data.submit_enctype); FREE(form->data.submit_title); FREE(form->accept_cs); FREE(form->thisacceptcs); FREE(form); } } static void free_form_fields(FormInfo * input_field) { /* * Free form fields. */ if (input_field->type == F_OPTION_LIST_TYPE && input_field->select_list != NULL) { /* * Free off option lists if present. * It should always be present for F_OPTION_LIST_TYPE * unless we had invalid markup which prevented * HText_setLastOptionValue from finishing its job * and left the input field in an insane state. - kw */ OptionType *optptr = input_field->select_list; OptionType *tmp; while (optptr) { tmp = optptr; optptr = tmp->next; FREE(tmp->name); FREE(tmp->cp_submit_value); FREE(tmp); } input_field->select_list = NULL; /* * Don't free the value field on option * lists since it points to a option value * same for orig value. */ input_field->value = NULL; input_field->orig_value = NULL; input_field->cp_submit_value = NULL; input_field->orig_submit_value = NULL; } else { FREE(input_field->value); FREE(input_field->orig_value); FREE(input_field->cp_submit_value); FREE(input_field->orig_submit_value); } FREE(input_field->name); FREE(input_field->submit_action); FREE(input_field->submit_enctype); FREE(input_field->submit_title); FREE(input_field->accept_cs); } static void FormList_delete(HTList *forms) { HTList *cur = forms; PerFormInfo *form; while ((form = (PerFormInfo *) HTList_nextObject(cur)) != NULL) PerFormInfo_free(form); HTList_delete(forms); } #ifdef DISP_PARTIAL static void ResetPartialLinenos(HText *text) { if (text != 0) { text->first_lineno_last_disp_partial = -1; text->last_lineno_last_disp_partial = -1; } } #endif /* Creation Method * --------------- */ HText *HText_new(HTParentAnchor *anchor) { #if defined(VMS) && defined(VAXC) && !defined(__DECC) #include int status, VMType = 3, VMTotal; #endif /* VMS && VAXC && !__DECC */ HTLine *line = NULL; HText *self = typecalloc(HText); if (!self) outofmem(__FILE__, "HText_New"); CTRACE((tfp, "GridText: start HText_new\n")); #if defined(VMS) && defined (VAXC) && !defined(__DECC) status = lib$stat_vm(&VMType, &VMTotal); CTRACE((tfp, "GridText: VMTotal = %d\n", VMTotal)); #endif /* VMS && VAXC && !__DECC */ /* * If the previously shown text had UTF-8 characters on screen, * remember this in the newly created object. Do this now, before * the previous object may become invalid. - kw */ if (HTMainText) { if (HText_hasUTF8OutputSet(HTMainText) && HTLoadedDocumentEightbit() && IS_UTF8_TTY) { self->had_utf8 = HTMainText->has_utf8; } else { self->had_utf8 = HTMainText->has_utf8; } HTMainText->has_utf8 = NO; } if (!loaded_texts) { loaded_texts = HTList_new(); #ifdef LY_FIND_LEAKS atexit(free_all_texts); #endif } /* * Links between anchors & documents are a 1-1 relationship. If * an anchor is already linked to a document we didn't call * HTuncache_current_document(), so we'll check now * and free it before reloading. - Dick Wesseling (ftu@fi.ruu.nl) */ if (anchor->document) { HTList_removeObject(loaded_texts, anchor->document); CTRACE((tfp, "GridText: Auto-uncaching\n")); HTAnchor_delete_links(anchor); ((HText *) anchor->document)->node_anchor = NULL; HText_free((HText *) anchor->document); anchor->document = NULL; } HTList_addObject(loaded_texts, self); #if defined(VMS) && defined(VAXC) && !defined(__DECC) while (HTList_count(loaded_texts) > HTCacheSize && VMTotal > HTVirtualMemorySize) #else if (HTList_count(loaded_texts) > HTCacheSize) #endif /* VMS && VAXC && !__DECC */ { CTRACE((tfp, "GridText: Freeing off cached doc.\n")); HText_free((HText *) HTList_removeFirstObject(loaded_texts)); #if defined(VMS) && defined (VAXC) && !defined(__DECC) status = lib$stat_vm(&VMType, &VMTotal); CTRACE((tfp, "GridText: VMTotal reduced to %d\n", VMTotal)); #endif /* VMS && VAXC && !__DECC */ } self->pool = POOL_NEW(); if (!self->pool) outofmem(__FILE__, "HText_New"); line = self->last_line = TEMP_LINE(self, 0); line->next = line->prev = line; line->offset = line->size = 0; line->data[line->size] = '\0'; #ifdef USE_COLOR_STYLE line->numstyles = 0; line->styles = stylechanges_buffers[0]; #endif self->Lines = 0; self->first_anchor = self->last_anchor = NULL; self->last_anchor_before_split = NULL; self->style = &default_style; self->top_of_screen = 0; self->node_anchor = anchor; self->last_anchor_number = 0; /* Numbering of them for references */ self->stale = YES; self->toolbar = NO; self->tabs = NULL; self->next_number = 1; #ifdef USE_SOURCE_CACHE /* * Remember the parse settings. */ self->clickable_images = clickable_images; self->pseudo_inline_alts = pseudo_inline_alts; self->verbose_img = verbose_img; self->raw_mode = LYUseDefaultRawMode; self->historical_comments = historical_comments; self->minimal_comments = minimal_comments; self->soft_dquotes = soft_dquotes; self->old_dtd = (short) Old_DTD; self->keypad_mode = (short) keypad_mode; self->disp_lines = (short) LYlines; self->disp_cols = (short) DISPLAY_COLS; #endif /* * If we are going to render the List Page, always merge in hidden * links to get the numbering consistent if form fields are numbered * and show up as hidden links in the list of links. * If we are going to render a bookmark file, also always merge in * hidden links, to get the link numbers consistent with the counting * in remove_bookmark_link(). Normally a bookmark file shouldn't * contain any entries with empty titles, but it might happen. - kw */ if (anchor->bookmark || LYIsUIPage3(anchor->address, UIP_LIST_PAGE, 0) || LYIsUIPage3(anchor->address, UIP_ADDRLIST_PAGE, 0)) self->hiddenlinkflag = HIDDENLINKS_MERGE; else self->hiddenlinkflag = LYHiddenLinks; self->hidden_links = NULL; self->no_cache = (BOOLEAN) ((anchor->no_cache || anchor->post_data) ? YES : NO); self->LastChar = '\0'; #ifndef USE_PRETTYSRC if (HTOutputFormat == WWW_SOURCE) self->source = YES; else self->source = NO; #else /* mark_htext_as_source == TRUE if we are parsing html file (and psrc_view * is set temporary to false at creation time) * * psrc_view == TRUE if source of the text produced by some lynx module * (like ftp browsers) is requested). - VH */ self->source = (BOOL) (LYpsrc ? mark_htext_as_source || psrc_view : HTOutputFormat == WWW_SOURCE); mark_htext_as_source = FALSE; #endif HTAnchor_setDocument(anchor, (HyperDoc *) self); HTFormNumber = 0; /* no forms started yet */ HTMainText = self; HTMainAnchor = anchor; self->display_on_the_fly = 0; self->kcode = NOKANJI; self->specified_kcode = NOKANJI; #ifdef USE_TH_JP_AUTO_DETECT self->detected_kcode = DET_NOTYET; self->SJIS_status = SJIS_state_neutral; self->EUC_status = EUC_state_neutral; #endif self->state = S_text; self->kanji_buf = '\0'; self->in_sjis = 0; self->have_8bit_chars = NO; HText_getChartransInfo(self); UCSetTransParams(&self->T, self->UCLYhndl, self->UCI, current_char_set, &LYCharSet_UC[current_char_set]); /* * Check the kcode setting if the anchor has a charset element. -FM */ HText_setKcode(self, anchor->charset, HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT)); /* * Check to see if our underline and star_string need initialization * if the underline is not filled with dots. */ if (underscore_string[0] != '.') { /* * Create an array of dots for the UNDERSCORES macro. -FM */ memset(underscore_string, '.', (size_t) (MAX_LINE - 1)); underscore_string[(MAX_LINE - 1)] = '\0'; underscore_string[MAX_LINE] = '\0'; /* * Create an array of underscores for the STARS macro. -FM */ memset(star_string, '_', (size_t) (MAX_LINE - 1)); star_string[(MAX_LINE - 1)] = '\0'; star_string[MAX_LINE] = '\0'; } underline_on = FALSE; /* reset */ bold_on = FALSE; #ifdef DISP_PARTIAL /* * By this function we create HText object * so we may start displaying the document while downloading. - LP */ if (display_partial_flag) { display_partial = TRUE; /* enable HTDisplayPartial() */ NumOfLines_partial = 0; /* initialize */ } /* * These two fields should only be set to valid line numbers * by calls of display_page during partial displaying. This * is just so that the FIRST display_page AFTER that can avoid * repainting the same lines on the screen. - kw */ ResetPartialLinenos(self); #endif #ifdef USE_JUSTIFY_ELTS ht_justify_cleanup(); #endif return self; } /* Creation Method 2 * --------------- * * Stream is assumed open and left open. */ HText *HText_new2(HTParentAnchor *anchor, HTStream *stream) { HText *result = HText_new(anchor); if (stream) { result->target = stream; result->targetClass = *stream->isa; /* copy action procedures */ } return result; } /* Free Entire Text * ---------------- */ void HText_free(HText *self) { if (!self) return; #if HTLINE_NOT_IN_POOL { HTLine *f = FirstHTLine(self); HTLine *l = self->last_line; while (l != f) { /* Free off line array */ self->last_line = l->prev; freeHTLine(self, l); l = self->last_line; } freeHTLine(self, f); } #endif while (self->first_anchor) { /* Free off anchor array */ TextAnchor *l = self->first_anchor; self->first_anchor = l->next; if (l->link_type == INPUT_ANCHOR && l->input_field) { free_form_fields(l->input_field); } LYFreeHiText(l); } FormList_delete(self->forms); /* * Free the tabs list. -FM */ if (self->tabs) { HTTabID *Tab = NULL; HTList *cur = self->tabs; while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) { FREE(Tab->name); FREE(Tab); } HTList_delete(self->tabs); self->tabs = NULL; } /* * Free the hidden links list. -FM */ if (self->hidden_links) { LYFreeStringList(self->hidden_links); self->hidden_links = NULL; } /* * Invoke HTAnchor_delete() to free the node_anchor * if it is not a destination of other links. -FM */ if (self->node_anchor) { HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_STRUCTURED, UCT_SETBY_NONE); HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_HTEXT, UCT_SETBY_NONE); #ifdef USE_SOURCE_CACHE /* Remove source cache files and chunks always, even if the * HTAnchor_delete call does not actually remove the anchor. * Keeping them would just be a waste of space - they won't * be used any more after the anchor has been disassociated * from a HText structure. - kw */ HTAnchor_clearSourceCache(self->node_anchor); #endif HTAnchor_delete_links(self->node_anchor); HTAnchor_setDocument(self->node_anchor, (HyperDoc *) 0); if (HTAnchor_delete(self->node_anchor->parent)) /* * Make sure HTMainAnchor won't point * to an invalid structure. - KW */ HTMainAnchor = NULL; } POOL_FREE(self->pool); FREE(self); } /* Display Methods * --------------- */ /* Output a line * ------------- */ static int display_line(HTLine *line, HText *text, int scrline GCC_UNUSED, const char *target GCC_UNUSED) { register int i, j; char buffer[7]; char *data; size_t utf_extra = 0; char LastDisplayChar = ' '; #ifdef USE_COLOR_STYLE int current_style = 0; #define inunderline NO #define inbold NO #else BOOL inbold = NO, inunderline = NO; #endif #if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE) const char *cp_tgt; int i_start_tgt = 0, i_after_tgt; int HitOffset, LenNeeded; BOOL intarget = NO; #else #define intarget NO #endif /* SHOW_WHEREIS_TARGETS && !USE_COLOR_STYLE */ #if !(defined(NCURSES_VERSION) || defined(WIDEC_CURSES)) text->has_utf8 = NO; /* use as per-line flag, except with ncurses */ #endif #if defined(WIDEC_CURSES) /* * FIXME: this should not be necessary, but in some wide-character pages * the output line wraps, foiling our attempt to just use newlines to * advance to the next page. */ LYmove(scrline + TITLE_LINES - 1, 0); #endif /* * Set up the multibyte character buffer, * and clear the line to which we will be * writing. */ buffer[0] = buffer[1] = buffer[2] = '\0'; LYclrtoeol(); /* * Add offset, making sure that we do not * go over the COLS limit on the display. */ j = (int) line->offset; if (j >= DISPLAY_COLS) j = DISPLAY_COLS - 1; #ifdef USE_SLANG SLsmg_forward(j); i = j; #else #ifdef USE_COLOR_STYLE if (line->size == 0) i = j; else #endif for (i = 0; i < j; i++) LYaddch(' '); #endif /* USE_SLANG */ /* * Add the data, making sure that we do not * go over the COLS limit on the display. */ data = line->data; i++; #ifndef USE_COLOR_STYLE #if defined(SHOW_WHEREIS_TARGETS) /* * If the target is on this line, it will be emphasized. */ i_after_tgt = i; if (target) { cp_tgt = LYno_attr_mb_strstr(data, target, text->T.output_utf8, YES, &HitOffset, &LenNeeded); if (cp_tgt) { if (((int) line->offset + LenNeeded) >= DISPLAY_COLS) { cp_tgt = NULL; } else { text->page_has_target = YES; i_start_tgt = i + HitOffset; i_after_tgt = i + LenNeeded; } } } else { cp_tgt = NULL; } #endif /* SHOW_WHEREIS_TARGETS */ #endif /* USE_COLOR_STYLE */ while ((i <= DISPLAY_COLS) && ((buffer[0] = *data) != '\0')) { #ifndef USE_COLOR_STYLE #if defined(SHOW_WHEREIS_TARGETS) if (cp_tgt && i >= i_after_tgt) { if (intarget) { cp_tgt = LYno_attr_mb_strstr(data, target, text->T.output_utf8, YES, &HitOffset, &LenNeeded); if (cp_tgt) { i_start_tgt = i + HitOffset; i_after_tgt = i + LenNeeded; } if (!cp_tgt || i_start_tgt != i) { LYstopTargetEmphasis(); intarget = NO; if (inbold) lynx_start_bold(); if (inunderline) lynx_start_underline(); } } } #endif /* SHOW_WHEREIS_TARGETS */ #endif /* USE_COLOR_STYLE */ data++; #if defined(USE_COLOR_STYLE) #define CStyle line->styles[current_style] while (current_style < line->numstyles && i >= (int) (CStyle.sc_horizpos + line->offset + 1)) { LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction); current_style++; } #endif switch (buffer[0]) { #ifndef USE_COLOR_STYLE case LY_UNDERLINE_START_CHAR: if (dump_output_immediately && use_underscore) { LYaddch('_'); i++; } else { inunderline = YES; if (!intarget) { #if defined(PDCURSES) if (LYShowColor == SHOW_COLOR_NEVER) lynx_start_bold(); else lynx_start_underline(); #else lynx_start_underline(); #endif /* PDCURSES */ } } break; case LY_UNDERLINE_END_CHAR: if (dump_output_immediately && use_underscore) { LYaddch('_'); i++; } else { inunderline = NO; if (!intarget) { #if defined(PDCURSES) if (LYShowColor == SHOW_COLOR_NEVER) lynx_stop_bold(); else lynx_stop_underline(); #else lynx_stop_underline(); #endif /* PDCURSES */ } } break; case LY_BOLD_START_CHAR: inbold = YES; if (!intarget) lynx_start_bold(); break; case LY_BOLD_END_CHAR: inbold = NO; if (!intarget) lynx_stop_bold(); break; #endif /* !USE_COLOR_STYLE */ case LY_SOFT_NEWLINE: if (!dump_output_immediately) { LYaddch('+'); i++; #if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE) i_after_tgt++; #endif } break; case LY_SOFT_HYPHEN: if (*data != '\0' || isspace(UCH(LastDisplayChar)) || LastDisplayChar == '-') { /* * Ignore the soft hyphen if it is not the last character in * the line. Also ignore it if is first character following * the margin, or if it is preceded by a white character (we * loaded 'M' into LastDisplayChar if it was a multibyte * character) or hyphen, though it should have been excluded by * HText_appendCharacter() or by split_line() in those cases. * -FM */ break; } else { /* * Make it a hard hyphen and fall through. -FM */ buffer[0] = '-'; } /* FALLTHRU */ default: #ifndef USE_COLOR_STYLE #if defined(SHOW_WHEREIS_TARGETS) if (!intarget && cp_tgt && i >= i_start_tgt) { /* * Start the emphasis. */ if (data > cp_tgt) { LYstartTargetEmphasis(); intarget = YES; } } #endif /* SHOW_WHEREIS_TARGETS */ #endif /* USE_COLOR_STYLE */ if (text->T.output_utf8 && is8bits(buffer[0])) { text->has_utf8 = YES; utf_extra = utf8_length(text->T.output_utf8, data - 1); LastDisplayChar = 'M'; } if (utf_extra) { LYStrNCpy(&buffer[1], data, utf_extra); LYaddstr(buffer); buffer[1] = '\0'; data += utf_extra; utf_extra = 0; } else if (is_CJK2(buffer[0])) { /* * For CJK strings, by Masanobu Kimura. */ if (i <= DISPLAY_COLS) { buffer[1] = *data; buffer[2] = '\0'; data++; i++; LYaddstr(buffer); buffer[1] = '\0'; /* * For now, load 'M' into LastDisplayChar, but we should * check whether it's white and if so, use ' '. I don't * know if there actually are white CJK characters, and * we're loading ' ' for multibyte spacing characters in * this code set, but this will become an issue when the * development code set's multibyte character handling is * used. -FM */ LastDisplayChar = 'M'; #ifndef USE_SLANG { int y, x; getyx(LYwin, y, x); (void) y; if (x >= DISPLAY_COLS || x == 0) break; } #endif } } else { LYaddstr(buffer); LastDisplayChar = buffer[0]; } i++; } /* end of switch */ } /* end of while */ #if !(defined(NCURSES_VERSION) || defined(WIDEC_CURSES)) if (text->has_utf8) { LYtouchline(scrline); text->has_utf8 = NO; /* we had some, but have dealt with it. */ } #endif /* * Add the return. */ LYaddch('\n'); #if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE) if (intarget) LYstopTargetEmphasis(); #else #undef intarget #endif /* SHOW_WHEREIS_TARGETS && !USE_COLOR_STYLE */ #ifndef USE_COLOR_STYLE lynx_stop_underline(); lynx_stop_bold(); #else while (current_style < line->numstyles) { LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction); current_style++; } #undef CStyle #endif return (0); } /* Output the title line * --------------------- */ static void display_title(HText *text) { char *title = NULL; char percent[20]; unsigned char *tmp = NULL; int i = 0, j = 0; int limit; #ifdef USE_COLOR_STYLE int toolbar = 0; #endif /* * Make sure we have a text structure. -FM */ if (!text) return; lynx_start_title_color(); #ifdef USE_COLOR_STYLE /* turn the TITLE style on */ if (last_colorattr_ptr > 0) { LynxChangeStyle(s_title, STACK_ON); } else { LynxChangeStyle(s_title, ABS_ON); } #endif /* USE_COLOR_STYLE */ /* * Load the title field. -FM */ StrAllocCopy(title, (HTAnchor_title(text->node_anchor) ? HTAnchor_title(text->node_anchor) : " ")); /* "" -> " " */ LYReduceBlanks(title); /* * Generate the page indicator (percent) string. */ limit = LYscreenWidth(); if (limit < 10) { percent[0] = '\0'; } else if ((display_lines) <= 0 && LYlines > 0 && text->top_of_screen <= 99999 && text->Lines <= 999999) { sprintf(percent, " (l%d of %d)", text->top_of_screen, text->Lines); } else if ((text->Lines >= display_lines) && (display_lines > 0)) { int total_pages = ((text->Lines + display_lines) / display_lines); int start_of_last_page = ((text->Lines <= display_lines) ? 0 : (text->Lines - display_lines)); sprintf(percent, " (p%d of %d)", ((text->top_of_screen > start_of_last_page) ? total_pages : ((text->top_of_screen + display_lines) / (display_lines))), total_pages); } else { percent[0] = '\0'; } /* * Generate and display the title string, with page indicator * if appropriate, preceded by the toolbar token if appropriate, * and truncated if necessary. -FM & KW */ if (IS_CJK_TTY) { if (*title && (tmp = typecallocn(unsigned char, (strlen(title) * 2 + 256)))) { if (kanji_code == EUC) { TO_EUC((unsigned char *) title, tmp); } else if (kanji_code == SJIS) { TO_SJIS((unsigned char *) title, tmp); } else { for (i = 0, j = 0; title[i]; i++) { if (title[i] != CH_ESC) { /* S/390 -- gil -- 1487 */ tmp[j++] = UCH(title[i]); } } tmp[j] = '\0'; } StrAllocCopy(title, (const char *) tmp); FREE(tmp); } } LYmove(0, 0); LYclrtoeol(); #if defined(SH_EX) && defined(KANJI_CODE_OVERRIDE) LYaddstr(str_kcode(last_kcode)); #endif if (HText_hasToolbar(text)) { LYaddch('#'); #ifdef USE_COLOR_STYLE toolbar = 1; #endif } #ifdef USE_COLOR_STYLE if (s_forw_backw != NOSTYLE && (nhist || nhist_extra > 1)) { chtype c = nhist ? ACS_LARROW : ' '; /* turn the FORWBACKW.ARROW style on */ LynxChangeStyle(s_forw_backw, STACK_ON); if (nhist) { LYaddch(c); LYaddch(c); LYaddch(c); } else LYmove(0, 3 + toolbar); if (nhist_extra > 1) { LYaddch(ACS_RARROW); LYaddch(ACS_RARROW); LYaddch(ACS_RARROW); } LynxChangeStyle(s_forw_backw, STACK_OFF); } #endif /* USE_COLOR_STYLE */ #ifdef WIDEC_CURSES i = limit - LYbarWidth - (int) strlen(percent) - LYstrCells(title); if (i <= 0) { /* title is truncated */ i = limit - LYbarWidth - (int) strlen(percent) - 3; if (i <= 0) { /* no room at all */ title[0] = '\0'; } else { strcpy(title + LYstrFittable(title, i), "..."); } i = 0; } LYmove(0, i); #else i = (limit - 1) - (int) (strlen(percent) + strlen(title)); if (i >= CHAR_WIDTH) { LYmove(0, i); } else { /* * Truncation takes into account the possibility that * multibyte characters might be present. -HS (H. Senshu) */ int last; last = (int) strlen(percent) + CHAR_WIDTH; if (limit - 3 >= last) { title[(limit - 3) - last] = '.'; title[(limit - 2) - last] = '.'; title[(limit - 1) - last] = '\0'; } else { title[(limit - 1) - last] = '\0'; } LYmove(0, CHAR_WIDTH); } #endif LYaddstr(title); if (percent[0] != '\0') LYaddstr(percent); LYaddch('\n'); FREE(title); #if defined(USE_COLOR_STYLE) && defined(CAN_CUT_AND_PASTE) if (s_hot_paste != NOSTYLE) { /* Only if the user set the style */ LYmove(0, LYcolLimit); LynxChangeStyle(s_hot_paste, STACK_ON); LYaddch(ACS_RARROW); LynxChangeStyle(s_hot_paste, STACK_OFF); LYmove(1, 0); /* As after \n */ } #endif /* USE_COLOR_STYLE */ #ifdef USE_COLOR_STYLE /* turn the TITLE style off */ LynxChangeStyle(s_title, STACK_OFF); #endif /* USE_COLOR_STYLE */ lynx_stop_title_color(); return; } /* Output the scrollbar * --------------------- */ #ifdef USE_SCROLLBAR static void display_scrollbar(HText *text) { int i; int h = display_lines - 2 * (LYsb_arrow != 0); /* Height of the scrollbar */ int off = (LYsb_arrow != 0); /* Start of the scrollbar */ int top_skip, bot_skip, sh, shown; LYsb_begin = LYsb_end = -1; if (!LYShowScrollbar || !text || h <= 2 || text->Lines <= display_lines) return; if (text->top_of_screen >= text->Lines - display_lines) { /* Only part of the screen shows actual text */ shown = text->Lines - text->top_of_screen; if (shown <= 0) shown = 1; } else shown = display_lines; /* Each cell of scrollbar represents text->Lines/h lines of text. */ /* Always smaller than h */ sh = (shown * h + text->Lines / 2) / text->Lines; if (sh <= 0) sh = 1; if (sh >= h - 1) sh = h - 2; /* Position at ends indicates BEG and END */ if (text->top_of_screen == 0) top_skip = 0; else if (text->Lines - (text->top_of_screen + display_lines - 1) <= 0) top_skip = h - sh; else { /* text->top_of_screen between 1 and text->Lines - display_lines corresponds to top_skip between 1 and h - sh - 1 */ /* Use rounding to get as many positions into top_skip==h - sh - 1 as into top_skip == 1: 1--->1, text->Lines - display_lines + 1--->h - sh. */ top_skip = (int) (1 + 1. * (h - sh - 1) * text->top_of_screen / (text->Lines - display_lines + 1)); } bot_skip = h - sh - top_skip; LYsb_begin = top_skip; LYsb_end = h - bot_skip; if (LYsb_arrow) { #ifdef USE_COLOR_STYLE int s = top_skip ? s_sb_aa : s_sb_naa; if (last_colorattr_ptr > 0) { LynxChangeStyle(s, STACK_ON); } else { LynxChangeStyle(s, ABS_ON); } #endif /* USE_COLOR_STYLE */ LYmove(1, LYcolLimit + LYshiftWin); addch_raw(ACS_UARROW); #ifdef USE_COLOR_STYLE LynxChangeStyle(s, STACK_OFF); #endif /* USE_COLOR_STYLE */ } #ifdef USE_COLOR_STYLE if (last_colorattr_ptr > 0) { LynxChangeStyle(s_sb_bg, STACK_ON); } else { LynxChangeStyle(s_sb_bg, ABS_ON); } #endif /* USE_COLOR_STYLE */ for (i = 1; i <= h; i++) { #ifdef USE_COLOR_STYLE if (i - 1 <= top_skip && i > top_skip) LynxChangeStyle(s_sb_bar, STACK_ON); if (i - 1 <= h - bot_skip && i > h - bot_skip) LynxChangeStyle(s_sb_bar, STACK_OFF); #endif /* USE_COLOR_STYLE */ LYmove(i + off, LYcolLimit + LYshiftWin); if (i > top_skip && i <= h - bot_skip) { LYaddch(ACS_BLOCK); } else { LYaddch(ACS_CKBOARD); } } #ifdef USE_COLOR_STYLE LynxChangeStyle(s_sb_bg, STACK_OFF); #endif /* USE_COLOR_STYLE */ if (LYsb_arrow) { #ifdef USE_COLOR_STYLE int s = bot_skip ? s_sb_aa : s_sb_naa; if (last_colorattr_ptr > 0) { LynxChangeStyle(s, STACK_ON); } else { LynxChangeStyle(s, ABS_ON); } #endif /* USE_COLOR_STYLE */ LYmove(h + 2, LYcolLimit + LYshiftWin); addch_raw(ACS_DARROW); #ifdef USE_COLOR_STYLE LynxChangeStyle(s, STACK_OFF); #endif /* USE_COLOR_STYLE */ } return; } #else #define display_scrollbar(text) /*nothing */ #endif /* USE_SCROLLBAR */ /* Output a page * ------------- */ static void display_page(HText *text, int line_number, const char *target) { HTLine *line = NULL; int i; int title_lines = TITLE_LINES; #if defined(USE_COLOR_STYLE) && defined(SHOW_WHEREIS_TARGETS) const char *cp; #endif char tmp[7]; TextAnchor *Anchor_ptr = NULL; int stop_before_for_anchors; FormInfo *FormInfo_ptr; BOOL display_flag = FALSE; HTAnchor *link_dest; HTAnchor *link_dest_intl = NULL; static int last_nlinks = 0; static int charset_last_displayed = -1; #ifdef DISP_PARTIAL int last_disp_partial = -1; #endif lynx_mode = NORMAL_LYNX_MODE; if (text == NULL) { /* * Check whether to force a screen clear to enable scrollback, * or as a hack to fix a reverse clear screen problem for some * curses packages. - shf@access.digex.net & seldon@eskimo.com */ if (enable_scrollback) { LYaddch('*'); LYrefresh(); LYclear(); } LYaddstr("\n\nError accessing document!\nNo data available!\n"); LYrefresh(); nlinks = 0; /* set number of links to 0 */ return; } #ifdef DISP_PARTIAL if (display_partial || recent_sizechange || text->stale) { /* Reset them, will be set near end if all is okay. - kw */ ResetPartialLinenos(text); } #endif /* DISP_PARTIAL */ tmp[0] = tmp[1] = tmp[2] = '\0'; if (target && *target == '\0') target = NULL; text->page_has_target = NO; if (display_lines <= 0) { /* No screen space to display anything! * returning here makes it more likely we will survive if * an xterm is temporarily made very small. - kw */ return; } line_number = HText_getPreferredTopLine(text, line_number); for (i = 0, line = FirstHTLine(text); /* Find line */ i < line_number && (line != text->last_line); i++, line = line->next) { /* Loop */ #ifndef VMS if (!LYNoCore) { assert(line->next != NULL); } else if (line->next == NULL) { if (enable_scrollback) { LYaddch('*'); LYrefresh(); LYclear(); } LYaddstr("\n\nError drawing page!\nBad HText structure!\n"); LYrefresh(); nlinks = 0; /* set number of links to 0 */ return; } #else assert(line->next != NULL); #endif /* !VMS */ } /* Loop */ if (LYlowest_eightbit[current_char_set] <= 255 && (current_char_set != charset_last_displayed) && /* * current_char_set has changed since last invocation, * and it's not just 7-bit. * Also we don't want to do this for -dump and -source etc. */ LYCursesON) { #ifdef EXP_CHARTRANS_AUTOSWITCH UCChangeTerminalCodepage(current_char_set, &LYCharSet_UC[current_char_set]); #endif /* EXP_CHARTRANS_AUTOSWITCH */ charset_last_displayed = current_char_set; } /* * Check whether to force a screen clear to enable scrollback, * or as a hack to fix a reverse clear screen problem for some * curses packages. - shf@access.digex.net & seldon@eskimo.com */ if (enable_scrollback) { LYaddch('*'); LYrefresh(); LYclear(); } #ifdef USE_COLOR_STYLE /* * Reset stack of color attribute changes to avoid color leaking, * except if what we last displayed from this text was the previous * screenful, in which case carrying over the state might be beneficial * (although it shouldn't generally be needed any more). - kw */ if (text->stale || line_number != text->top_of_screen + (display_lines)) { last_colorattr_ptr = 0; } #endif text->top_of_screen = line_number; text->top_of_screen_line = line; if (no_title) { LYmove(0, 0); title_lines = 0; } else { display_title(text); /* will move cursor to top of screen */ } display_flag = TRUE; #ifdef USE_COLOR_STYLE #ifdef DISP_PARTIAL if (display_partial || line_number != text->first_lineno_last_disp_partial || line_number > text->last_lineno_last_disp_partial) #endif /* DISP_PARTIAL */ ResetCachedStyles(); #endif /* USE_COLOR_STYLE */ #ifdef DISP_PARTIAL if (display_partial && text->stbl) { stop_before_for_anchors = Stbl_getStartLineDeep(text->stbl); if (stop_before_for_anchors > line_number + (display_lines)) stop_before_for_anchors = line_number + (display_lines); } else #endif stop_before_for_anchors = line_number + (display_lines); /* * Output the page. */ if (line) { #if defined(USE_COLOR_STYLE) && defined(SHOW_WHEREIS_TARGETS) char *data; int offset, LenNeeded; #endif #ifdef DISP_PARTIAL if (display_partial || line_number != text->first_lineno_last_disp_partial) text->has_utf8 = NO; #else text->has_utf8 = NO; #endif for (i = 0; i < (display_lines); i++) { /* * Verify and display each line. */ #ifndef VMS if (!LYNoCore) { assert(line != NULL); } else if (line == NULL) { if (enable_scrollback) { LYaddch('*'); LYrefresh(); LYclear(); } LYaddstr("\n\nError drawing page!\nBad HText structure!\n"); LYrefresh(); nlinks = 0; /* set number of links to 0 */ return; } #else assert(line != NULL); #endif /* !VMS */ #ifdef DISP_PARTIAL if (!display_partial && line_number == text->first_lineno_last_disp_partial && i + line_number <= text->last_lineno_last_disp_partial) LYmove((i + title_lines + 1), 0); else #endif display_line(line, text, i + 1, target); #if defined(SHOW_WHEREIS_TARGETS) #ifdef USE_COLOR_STYLE /* otherwise done in display_line - kw */ /* * If the target is on this line, recursively * seek and emphasize it. -FM */ data = (char *) line->data; offset = (int) line->offset; while (non_empty(target) && (cp = LYno_attr_mb_strstr(data, target, text->T.output_utf8, YES, NULL, &LenNeeded)) != NULL && ((int) line->offset + LenNeeded) <= DISPLAY_COLS) { size_t itmp = 0; size_t written = 0; int x_off = offset + (int) (cp - data); size_t len = strlen(target); size_t utf_extra = 0; text->page_has_target = YES; /* * Start the emphasis. */ LYstartTargetEmphasis(); /* * Output the target characters. */ for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { if (IsSpecialAttrChar(tmp[0]) && tmp[0] != LY_SOFT_NEWLINE) { /* * Ignore special characters. */ x_off--; } else if (&data[itmp] >= cp) { if (cp == &data[itmp]) { /* * First printable character of target. */ LYmove((i + title_lines), line->offset + LYstrExtent2(line->data, x_off - line->offset)); } /* * Output all the printable target chars. */ utf_extra = utf8_length(text->T.output_utf8, data + itmp); if (utf_extra) { LYStrNCpy(&tmp[1], &line->data[itmp + 1], utf_extra); itmp += utf_extra; LYaddstr(tmp); tmp[1] = '\0'; written += (utf_extra + 1); } else if (IS_CJK_TTY && is8bits(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; LYaddstr(tmp); tmp[1] = '\0'; written += 2; } else { LYaddstr(tmp); written++; } } } /* * Stop the emphasis, and reset the offset and * data pointer for our current position in the * line. -FM */ LYstopTargetEmphasis(); data = (char *) &data[itmp]; offset = (int) (data - line->data + line->offset); } /* end while */ LYmove((i + title_lines + 1), 0); #endif /* USE_COLOR_STYLE */ #endif /* SHOW_WHEREIS_TARGETS */ /* * Stop if this is the last line. Otherwise, make sure * display_flag is set and process the next line. -FM */ if (line == text->last_line) { /* * Clear remaining lines of display. */ for (i++; i < (display_lines); i++) { LYmove((i + title_lines), 0); LYclrtoeol(); } break; } #ifdef DISP_PARTIAL if (display_partial) { /* * Remember as fully shown during last partial display, * if it was not the last text line. - kw */ last_disp_partial = i + line_number; } #endif /* DISP_PARTIAL */ display_flag = TRUE; line = line->next; } /* end of "Verify and display each line." loop */ } /* end "Output the page." */ text->next_line = line; /* Line after screen */ text->stale = NO; /* Display is up-to-date */ /* * Add the anchors to Lynx structures. */ nlinks = 0; for (Anchor_ptr = text->first_anchor; Anchor_ptr != NULL && Anchor_ptr->line_num <= stop_before_for_anchors; Anchor_ptr = Anchor_ptr->next) { if (Anchor_ptr->line_num >= line_number && Anchor_ptr->line_num < stop_before_for_anchors) { char *hi_string = LYGetHiTextStr(Anchor_ptr, 0); /* * Load normal hypertext anchors. */ if (Anchor_ptr->show_anchor && non_empty(hi_string) && (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) { int count; char *s; for (count = 0;; ++count) { s = LYGetHiTextStr(Anchor_ptr, count); if (count == 0) LYSetHilite(nlinks, s); if (s == NULL) break; if (count != 0) { LYAddHilite(nlinks, s, LYGetHiTextPos(Anchor_ptr, count)); } } links[nlinks].inUnderline = Anchor_ptr->inUnderline; links[nlinks].sgml_offset = Anchor_ptr->sgml_offset; links[nlinks].anchor_number = Anchor_ptr->number; links[nlinks].anchor_line_num = Anchor_ptr->line_num; link_dest = HTAnchor_followLink(Anchor_ptr->anchor); { auto char *cp_AnchorAddress = NULL; if (traversal) { cp_AnchorAddress = stub_HTAnchor_address(link_dest); } else if (track_internal_links) { if (Anchor_ptr->link_type == INTERNAL_LINK_ANCHOR) { link_dest_intl = HTAnchor_followTypedLink(Anchor_ptr->anchor, HTInternalLink); if (link_dest_intl && link_dest_intl != link_dest) { CTRACE((tfp, "GridText: display_page: unexpected typed link to %s!\n", link_dest_intl->parent->address)); link_dest_intl = NULL; } } else { link_dest_intl = NULL; } if (link_dest_intl) { char *cp2 = HTAnchor_address(link_dest_intl); cp_AnchorAddress = cp2; } else { cp_AnchorAddress = HTAnchor_address(link_dest); } } else { cp_AnchorAddress = HTAnchor_address(link_dest); } FREE(links[nlinks].lname); if (cp_AnchorAddress != NULL) links[nlinks].lname = cp_AnchorAddress; else StrAllocCopy(links[nlinks].lname, empty_string); } links[nlinks].lx = Anchor_ptr->line_pos; links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number); if (link_dest_intl) links[nlinks].type = WWW_INTERN_LINK_TYPE; else links[nlinks].type = WWW_LINK_TYPE; links[nlinks].target = empty_string; links[nlinks].l_form = NULL; nlinks++; display_flag = TRUE; } else if (Anchor_ptr->link_type == INPUT_ANCHOR && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) { /* * Handle form fields. */ lynx_mode = FORMS_LYNX_MODE; FormInfo_ptr = Anchor_ptr->input_field; links[nlinks].sgml_offset = Anchor_ptr->sgml_offset; links[nlinks].anchor_number = Anchor_ptr->number; links[nlinks].anchor_line_num = Anchor_ptr->line_num; links[nlinks].l_form = FormInfo_ptr; links[nlinks].lx = Anchor_ptr->line_pos; links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number); links[nlinks].type = WWW_FORM_LINK_TYPE; links[nlinks].inUnderline = Anchor_ptr->inUnderline; links[nlinks].target = empty_string; StrAllocCopy(links[nlinks].lname, empty_string); if (FormInfo_ptr->type == F_RADIO_TYPE) { LYSetHilite(nlinks, FormInfo_ptr->num_value ? checked_radio : unchecked_radio); } else if (FormInfo_ptr->type == F_CHECKBOX_TYPE) { LYSetHilite(nlinks, FormInfo_ptr->num_value ? checked_box : unchecked_box); } else if (FormInfo_ptr->type == F_PASSWORD_TYPE) { LYSetHilite(nlinks, STARS(LYstrCells(FormInfo_ptr->value))); } else { /* TEXT type */ LYSetHilite(nlinks, FormInfo_ptr->value); } nlinks++; /* * Bold the link after incrementing nlinks. */ LYhighlight(FALSE, (nlinks - 1), target); display_flag = TRUE; } else { /* * Not showing anchor. */ if (non_empty(hi_string)) CTRACE((tfp, "\nGridText: Not showing link, hightext=%s\n", hi_string)); } } if (nlinks == MAXLINKS) { /* * Links array is full. If interactive, tell user * to use half-page or two-line scrolling. -FM */ if (LYCursesON) { HTAlert(MAXLINKS_REACHED); } CTRACE((tfp, "\ndisplay_page: MAXLINKS reached.\n")); break; } } /* end of loop "Add the anchors to Lynx structures." */ /* * Free any un-reallocated links[] entries * from the previous page draw. -FM */ LYFreeHilites(nlinks, last_nlinks); last_nlinks = nlinks; /* * If Anchor_ptr is not NULL and is not pointing to the last * anchor, then there are anchors farther down in the document, * and we need to flag this for traversals. */ more_links = FALSE; if (traversal && Anchor_ptr) { if (Anchor_ptr->next) more_links = TRUE; } if (!display_flag) { /* * Nothing on the page. */ LYaddstr("\n Document is empty"); } display_scrollbar(text); #ifdef DISP_PARTIAL if (display_partial && display_flag && last_disp_partial >= text->top_of_screen && !enable_scrollback && !recent_sizechange) { /* really remember them if ok - kw */ text->first_lineno_last_disp_partial = text->top_of_screen; text->last_lineno_last_disp_partial = last_disp_partial; } else { ResetPartialLinenos(text); } #endif /* DISP_PARTIAL */ #if !defined(WIDEC_CURSES) if (text->has_utf8 || text->had_utf8) { /* * For other than ncurses, repainting is taken care of * by touching lines in display_line and highlight. - kw 1999-10-07 */ text->had_utf8 = text->has_utf8; clearok(curscr, TRUE); } else if (IS_CJK_TTY) { /* * For non-multibyte curses. * * Full repainting is necessary, otherwise only part of a multibyte * character sequence might be written because of curses output * optimizations. */ clearok(curscr, TRUE); } #endif /* WIDEC_CURSES */ LYrefresh(); return; } /* Object Building methods * ----------------------- * * These are used by a parser to build the text in an object */ void HText_beginAppend(HText *text) { text->permissible_split = 0; text->in_line_1 = YES; } /* LYcols_cu is the notion that the display library has of the screen width. Normally it is the same as LYcols, but there may be a difference via SLANG_MBCS_HACK. Checks of the line length (as the non-UTF-8-aware display library would see it) against LYcols_cu are is used to try to prevent that lines with UTF-8 chars get wrapped by the library when they shouldn't. If there is no display library involved, i.e. dump_output_immediately, no such limit should be imposed. MAX_COLS should be just as good as any other large value. (But don't use INT_MAX or something close to it to, avoid over/underflow.) - kw */ #ifdef USE_SLANG #define LYcols_cu(text) (dump_output_immediately ? MAX_COLS : SLtt_Screen_Cols) #else #ifdef WIDEC_CURSES #define LYcols_cu(text) WRAP_COLS(text) #else #define LYcols_cu(text) (dump_output_immediately ? MAX_COLS : DISPLAY_COLS) #endif #endif /* Add a new line of text * ---------------------- * * On entry, * * split is zero for newline function, else number of characters * before split. * text->display_on_the_fly * may be set to indicate direct output of the finished line. * On exit, * A new line has been made, justified according to the * current style. Text after the split (if split nonzero) * is taken over onto the next line. * * If display_on_the_fly is set, then it is decremented and * the finished line is displayed. */ static int set_style_by_embedded_chars(char *s, char *e, unsigned start_c, unsigned end_c) { int ret = NO; while (--e >= s) { if (UCH(*e) == UCH(end_c)) break; if (UCH(*e) == UCH(start_c)) { ret = YES; break; } } return ret; } static void move_anchors_in_region(HTLine *line, int line_number, TextAnchor **prev_anchor, /*updates++ */ int *prev_head_processed, int sbyte, int ebyte, int shift) /* Likewise */ { /* * Update anchor positions for anchors that start on this line. Note: we * rely on a->line_pos counting bytes, not characters. That's one reason * why HText_trimHightext has to be prevented from acting on these anchors * in partial display mode before we get a chance to deal with them here. */ TextAnchor *a; int head_processed = *prev_head_processed; /* * We need to know whether (*prev_anchor)->line_pos is "in new coordinates" * or in old ones. If prev_anchor' head was touched on the previous * iteration, we set head_processed. The tail may need to be treated now. */ for (a = *prev_anchor; a && a->line_num <= line_number; a = a->next, head_processed = 0) { /* extent==0 needs to be special-cased; happens if no text for the anchor was processed yet. */ /* Subtract one so that the space is not inserted at the end of the anchor... */ int last = a->line_pos + (a->extent ? a->extent - 1 : 0); /* Include the anchors started on the previous line */ if (a->line_num < line_number - 1) continue; if (a->line_num == line_number - 1) last -= line->prev->size + 1; /* Fake "\n" "between" lines counted too */ if (last < sbyte) /* Completely before the start */ continue; if (!head_processed /* a->line_pos is not edited yet */ && a->line_num == line_number && a->line_pos >= ebyte) /* Completely after the end */ break; /* Now we know that the anchor context intersects the chunk */ /* Fix the start */ if (!head_processed && a->line_num == line_number && a->line_pos >= sbyte) { a->line_pos = (short) (a->line_pos + shift); a->extent = (short) (a->extent - shift); head_processed = 1; } /* Fix the end */ if (last < ebyte) { a->extent = (short) (a->extent + shift); } else { break; /* Keep this `a' for the next step */ } } *prev_anchor = a; *prev_head_processed = head_processed; } /* * Given a line and two int arrays of old/now position, this function * creates a new line where spaces have been inserted/removed * in appropriate places - so that characters at/after the old * position end up at/after the new position, for each pair, if possible. * Some necessary changes for anchors starting on this line are also done * here if needed. Updates 'prev_anchor' internally. * Returns a newly allocated HTLine* if changes were made * (caller has to free the old one). * Returns NULL if no changes needed. (Remove-spaces code may be buggy...) * - kw */ static HTLine *insert_blanks_in_line(HTLine *line, int line_number, HText *text, TextAnchor **prev_anchor, /*updates++ */ int ninserts, int *oldpos, /* Measured in cells */ int *newpos) /* Likewise */ { int ioldc = 0; /* count visible characters */ int ip; /* count insertion pairs */ #if defined(USE_COLOR_STYLE) int istyle = 0; #endif int added_chars = 0; int shift = 0; int head_processed; HTLine *mod_line; char *newdata; char *s = line->data; char *pre = s; char *copied = line->data, *t; if (!(line && line->size && ninserts)) return NULL; for (ip = 0; ip < ninserts; ip++) if (newpos[ip] > oldpos[ip] && (newpos[ip] - oldpos[ip]) > added_chars) added_chars = newpos[ip] - oldpos[ip]; if (line->size + added_chars > MAX_LINE - 2) return NULL; if (line == text->last_line) { if (line == TEMP_LINE(text, 0)) mod_line = TEMP_LINE(text, 1); else mod_line = TEMP_LINE(text, 0); } else { allocHTLine(mod_line, (unsigned) (line->size + added_chars)); } if (!mod_line) return NULL; if (!*prev_anchor) *prev_anchor = text->first_anchor; head_processed = (*prev_anchor && (*prev_anchor)->line_num < line_number); memcpy(mod_line, line, LINE_SIZE(0)); t = newdata = mod_line->data; ip = 0; while (ip <= ninserts) { /* line->size is in bytes, so it may be larger than needed... */ int curlim = (ip < ninserts ? oldpos[ip] : ((int) line->size <= MAX_LINE ? MAX_LINE + 1 : (int) line->size + 1)); pre = s; /* Fast forward to char==curlim or EOL. Stop *before* the style-change chars. */ while (*s) { if (text && text->T.output_utf8 && UCH(*s) >= 0x80 && UCH(*s) < 0xC0) { pre = s + 1; } else if (!IsSpecialAttrChar(*s)) { /* At a "displayed" char */ if (ioldc >= curlim) break; ioldc++; pre = s + 1; } s++; } /* Now s is at the "displayed" char, pre is before the style change */ if (ip) /* Fix anchor positions */ move_anchors_in_region(line, line_number, prev_anchor /*updates++ */ , &head_processed, (int) (copied - line->data), (int) (pre - line->data), shift); #if defined(USE_COLOR_STYLE) /* Move styles too */ #define NStyle mod_line->styles[istyle] for (; istyle < line->numstyles && (int) NStyle.sc_horizpos < curlim; istyle++) /* Should not we include OFF-styles at curlim? */ NStyle.sc_horizpos += shift; #endif while (copied < pre) /* Copy verbatim to byte == pre */ *t++ = *copied++; if (ip < ninserts) { /* Insert spaces */ int delta = newpos[ip] - oldpos[ip] - shift; if (delta < 0) { /* Not used yet? */ while (delta++ < 0 && t > newdata && t[-1] == ' ') t--, shift--; } else shift = newpos[ip] - oldpos[ip]; while (delta-- > 0) *t++ = ' '; } ip++; } while (pre < s) /* Copy remaining style-codes */ *t++ = *pre++; /* Check whether the last anchor continues on the next line */ if (head_processed && *prev_anchor && (*prev_anchor)->line_num == line_number) { (*prev_anchor)->extent = (short) ((*prev_anchor)->extent + shift); } *t = '\0'; mod_line->size = (unsigned short) (t - newdata); return mod_line; } #if defined(USE_COLOR_STYLE) #define direction2s(d) ((d) == STACK_OFF \ ? "OFF" \ : ((d) == STACK_ON \ ? "ON" \ : "*ON")) /* * Found an OFF change not part of an adjacent matched pair. * * Walk backward looking for the corresponding ON change. * Move everything after split_pos to be at split_pos. * * This can only work correctly if all changes are correctly nested! If this * fails, assume it is safer to leave whatever comes before the OFF on the * previous line alone. */ static HTStyleChange *skip_matched_and_correct_offsets(HTStyleChange *end, HTStyleChange *start, unsigned split_pos) { HTStyleChange *result = 0; int level = 0; HTStyleChange *tmp = end; CTRACE_STYLE((tfp, "SKIP Style %d %d (%s), split %u\n", tmp->sc_horizpos, tmp->sc_style, direction2s(tmp->sc_direction), split_pos)); for (; tmp >= start; tmp--) { CTRACE_STYLE((tfp, "... %d %d (%s)\n", tmp->sc_horizpos, tmp->sc_style, direction2s(tmp->sc_direction))); if (tmp->sc_style == end->sc_style) { if (tmp->sc_direction == STACK_OFF) { level--; } else if (tmp->sc_direction == STACK_ON) { if (++level == 0) { result = tmp; break; } } else { break; } } if (tmp->sc_horizpos > split_pos) { tmp->sc_horizpos = split_pos; } } return result; } #endif /* USE_COLOR_STYLE */ #define reset_horizpos(value) value = 0, value = ~value static void split_line(HText *text, unsigned split) { HTStyle *style = text->style; int spare; int indent = (text->in_line_1 ? text->style->indent1st : text->style->leftIndent); int new_offset; short alignment; TextAnchor *a; int CurLine = text->Lines; int HeadTrim = 0; int SpecialAttrChars = 0; int TailTrim = 0; int s, s_post, s_pre, t_underline = underline_on, t_bold = bold_on; char *p; char *cp; int ctrl_chars_on_previous_line = 0; #ifndef WIDEC_CURSES int utfxtra_on_previous_line = UTFXTRA_ON_THIS_LINE; #endif HTLine *previous = text->last_line; HTLine *line; /* * Set new line. */ if (previous == TEMP_LINE(text, 0)) line = TEMP_LINE(text, 1); else line = TEMP_LINE(text, 0); if (line == NULL) return; memset(line, 0, (size_t) LINE_SIZE(0)); ctrl_chars_on_this_line = 0; /*reset since we are going to a new line */ utfxtra_on_this_line = 0; /*reset too, we'll count them */ text->LastChar = ' '; #ifdef DEBUG_APPCH CTRACE((tfp, "GridText: split_line(%p,%d) called\n", text, split)); CTRACE((tfp, " previous=%s\n", previous->data)); CTRACE((tfp, " bold_on=%d, underline_on=%d\n", bold_on, underline_on)); #endif cp = previous->data; /* Float LY_SOFT_NEWLINE to the start */ if (cp[0] == LY_BOLD_START_CHAR || cp[0] == LY_UNDERLINE_START_CHAR) { switch (cp[1]) { case LY_SOFT_NEWLINE: cp[1] = cp[0]; cp[0] = LY_SOFT_NEWLINE; break; case LY_BOLD_START_CHAR: case LY_UNDERLINE_START_CHAR: if (cp[2] == LY_SOFT_NEWLINE) { cp[2] = cp[1]; cp[1] = cp[0]; cp[0] = LY_SOFT_NEWLINE; } break; } } if (split > previous->size) { CTRACE((tfp, "*** split_line: split==%u greater than last_line->size==%d !\n", split, previous->size)); if (split > MAX_LINE) { split = previous->size; if ((cp = strrchr(previous->data, ' ')) && cp - previous->data > 1) split = (unsigned) (cp - previous->data); CTRACE((tfp, " split adjusted to %u.\n", split)); } } text->Lines++; previous->next->prev = line; line->prev = previous; line->next = previous->next; previous->next = line; text->last_line = line; line->size = 0; line->offset = 0; text->permissible_split = 0; /* 12/13/93 */ line->data[0] = '\0'; alignment = style->alignment; if (split > 0) { /* Restore flags to the value at the splitting point */ if (!(dump_output_immediately && use_underscore)) t_underline = set_style_by_embedded_chars(previous->data, previous->data + split, LY_UNDERLINE_START_CHAR, LY_UNDERLINE_END_CHAR); t_bold = set_style_by_embedded_chars(previous->data, previous->data + split, LY_BOLD_START_CHAR, LY_BOLD_END_CHAR); } if (!(dump_output_immediately && use_underscore) && t_underline) { line->data[line->size++] = LY_UNDERLINE_START_CHAR; line->data[line->size] = '\0'; ctrl_chars_on_this_line++; SpecialAttrChars++; } if (t_bold) { line->data[line->size++] = LY_BOLD_START_CHAR; line->data[line->size] = '\0'; ctrl_chars_on_this_line++; SpecialAttrChars++; } /* * Split at required point */ if (split > 0) { /* Delete space at "split" splitting line */ char *prevdata = previous->data, *linedata = line->data; unsigned plen; int i; /* Split the line. -FM */ prevdata[previous->size] = '\0'; previous->size = (unsigned short) split; /* * Trim any spaces or soft hyphens from the beginning * of our new line. -FM */ p = prevdata + split; while (((*p == ' ' #ifdef USE_JUSTIFY_ELTS /* if justification is allowed for prev line, then raw * HT_NON_BREAK_SPACE are still present in data[] (they'll be * substituted at the end of this function with ' ') - VH */ || *p == HT_NON_BREAK_SPACE #endif ) && (HeadTrim || text->first_anchor || underline_on || bold_on || alignment != HT_LEFT || style->wordWrap || style->freeFormat || style->spaceBefore || style->spaceAfter)) || *p == LY_SOFT_HYPHEN) { p++; HeadTrim++; } plen = (unsigned) strlen(p); if (plen) { /* Count funny characters */ for (i = (int) (plen - 1); i >= 0; i--) { if (p[i] == LY_UNDERLINE_START_CHAR || p[i] == LY_UNDERLINE_END_CHAR || p[i] == LY_BOLD_START_CHAR || p[i] == LY_BOLD_END_CHAR || p[i] == LY_SOFT_HYPHEN) { ctrl_chars_on_this_line++; } else if (IS_UTF_EXTRA(p[i])) { utfxtra_on_this_line++; } if (p[i] == LY_SOFT_HYPHEN && (int) text->permissible_split < i) text->permissible_split = (unsigned) (i + 1); } ctrl_chars_on_this_line += utfxtra_on_this_line; /* Add the data to the new line. -FM */ strcat(linedata, p); line->size = (unsigned short) (line->size + plen); } } /* * Economize on space. */ p = previous->data + previous->size - 1; while (p >= previous->data && (*p == ' ' #ifdef USE_JUSTIFY_ELTS /* if justification is allowed for prev line, then raw * HT_NON_BREAK_SPACE are still present in data[] (they'll be * substituted at the end of this function with ' ') - VH */ || *p == HT_NON_BREAK_SPACE #endif ) #ifdef USE_PRETTYSRC && !psrc_view /*don't strip trailing whites - since next line can start with LY_SOFT_NEWLINE - so we don't lose spaces when 'p'rinting this text to file -VH */ #endif && (ctrl_chars_on_this_line || HeadTrim || text->first_anchor || underline_on || bold_on || alignment != HT_LEFT || style->wordWrap || style->freeFormat || style->spaceBefore || style->spaceAfter)) { p--; /* Strip trailers. */ } /* Strip trailers. */ TailTrim = (int) (previous->data + previous->size - 1 - p); previous->size = (unsigned short) (previous->size - TailTrim); p[1] = '\0'; /* * s is the effective split position, given by either a non-zero * value of split or by the size of the previous line before * trimming. - kw */ if (split == 0) { s = previous->size + TailTrim; /* the original size */ } else { s = (int) split; } s_post = s + HeadTrim; s_pre = s - TailTrim; #ifdef DEBUG_SPLITLINE #ifdef DEBUG_APPCH if (s != (int) split) #endif CTRACE((tfp, "GridText: split_line(%u [now:%d]) called\n", split, s)); #endif #if defined(USE_COLOR_STYLE) if (previous->styles == stylechanges_buffers[0]) line->styles = stylechanges_buffers[1]; else line->styles = stylechanges_buffers[0]; line->numstyles = 0; { HTStyleChange *from = previous->styles + previous->numstyles - 1; HTStyleChange *to = line->styles + MAX_STYLES_ON_LINE - 1; HTStyleChange *scan, *at_end; /* Color style changes after the split position * are transferred to the new line. Ditto for changes * in the trimming region, but we stop when we reach an OFF change. * The second loop below may then handle remaining changes. - kw */ while (from >= previous->styles && to >= line->styles) { *to = *from; if ((int) to->sc_horizpos > s_post) { to->sc_horizpos += -s_post + SpecialAttrChars; } else if ((int) to->sc_horizpos > s_pre && (to->sc_direction == STACK_ON || to->sc_direction == ABS_ON)) { to->sc_horizpos = ((int) to->sc_horizpos < s) ? 0 : SpecialAttrChars; } else { break; } to--; from--; } /* FROM may be invalid, otherwise it is either an ON change at or before s_pre, or is an OFF change at or before s_post. */ scan = from; at_end = from; /* Now on the previous line we have a correctly nested but possibly non-terminated sequence of style changes. Terminate it, and duplicate unterminated changes at the beginning of the new line. */ while (scan >= previous->styles && at_end >= previous->styles) { /* The algorithm: scan back though the styles on the previous line. a) If OFF, skip the matched group. Report a bug on failure. b) If ON, (try to) cancel the corresponding ON at at_end, and the corresponding OFF at to; If not, put the corresponding OFF at at_end, and copy to to; */ if (scan->sc_direction == STACK_OFF) { scan = skip_matched_and_correct_offsets(scan, previous->styles, (unsigned) s_pre); if (!scan) { CTRACE((tfp, "BUG: styles improperly nested.\n")); break; } } else if (scan->sc_direction == STACK_ON) { if (at_end->sc_direction == STACK_ON && at_end->sc_style == scan->sc_style && (int) at_end->sc_horizpos >= s_pre) at_end--; else if (at_end >= previous->styles + MAX_STYLES_ON_LINE - 1) { CTRACE((tfp, "BUG: style overflow before split_line.\n")); break; } else { at_end++; at_end->sc_direction = STACK_OFF; at_end->sc_style = scan->sc_style; at_end->sc_horizpos = s_pre; CTRACE_STYLE((tfp, "split_line, %d:style[%d] %d (dir=%d)\n", s_pre, (int) (at_end - from), scan->sc_style, at_end->sc_direction)); } if (to < line->styles + MAX_STYLES_ON_LINE - 1 && to[1].sc_direction == STACK_OFF && to[1].sc_horizpos <= (unsigned) SpecialAttrChars && to[1].sc_style == scan->sc_style) to++; else if (to >= line->styles) { *to = *scan; to->sc_horizpos = SpecialAttrChars; to--; } else { CTRACE((tfp, "BUG: style overflow after split_line.\n")); break; } } if ((int) scan->sc_horizpos > s_pre) { scan->sc_horizpos = s_pre; } scan--; } line->numstyles = (unsigned short) (line->styles + MAX_STYLES_ON_LINE - 1 - to); if (line->numstyles > 0 && line->numstyles < MAX_STYLES_ON_LINE) { int n; for (n = 0; n < line->numstyles; n++) line->styles[n] = to[n + 1]; } else if (line->numstyles == 0) { reset_horizpos(line->styles[0].sc_horizpos); } previous->numstyles = (unsigned short) (at_end - previous->styles + 1); if (previous->numstyles == 0) { reset_horizpos(previous->styles[0].sc_horizpos); } } #endif /*USE_COLOR_STYLE */ { HTLine *temp; allocHTLine(temp, previous->size); if (!temp) outofmem(__FILE__, "split_line_2"); assert(temp != NULL); memcpy(temp, previous, LINE_SIZE(previous->size)); #if defined(USE_COLOR_STYLE) POOLallocstyles(temp->styles, previous->numstyles); if (!temp->styles) outofmem(__FILE__, "split_line_2"); memcpy(temp->styles, previous->styles, sizeof(HTStyleChange) * previous->numstyles); #endif previous = temp; } previous->prev->next = previous; /* Link in new line */ previous->next->prev = previous; /* Could be same node of course */ /* * Terminate finished line for printing. */ previous->data[previous->size] = '\0'; /* * Align left, right or center. */ spare = 0; if ( #ifdef USE_JUSTIFY_ELTS this_line_was_split || #endif (alignment == HT_CENTER || alignment == HT_RIGHT) || text->stbl) { /* Calculate spare character positions if needed */ for (cp = previous->data; *cp; cp++) { if (*cp == LY_UNDERLINE_START_CHAR || *cp == LY_UNDERLINE_END_CHAR || *cp == LY_BOLD_START_CHAR || *cp == LY_BOLD_END_CHAR || #ifndef WIDEC_CURSES IS_UTF_EXTRA(*cp) || #endif *cp == LY_SOFT_HYPHEN) { ctrl_chars_on_previous_line++; } } if ((previous->size > 0) && (int) (previous->data[previous->size - 1] == LY_SOFT_HYPHEN)) ctrl_chars_on_previous_line--; /* @@ first line indent */ #ifdef WIDEC_CURSES spare = WRAP_COLS(text) - (int) style->rightIndent - indent + ctrl_chars_on_previous_line - LYstrExtent2(previous->data, previous->size); if (spare < 0 && LYwideLines) /* Can be wider than screen */ spare = 0; #else spare = WRAP_COLS(text) - (int) style->rightIndent - indent + ctrl_chars_on_previous_line - previous->size; if (spare < 0 && LYwideLines) /* Can be wider than screen */ spare = 0; if (spare > 0 && !dump_output_immediately && text->T.output_utf8 && ctrl_chars_on_previous_line) { utfxtra_on_previous_line -= UTFXTRA_ON_THIS_LINE; if (utfxtra_on_previous_line) { int spare_cu = (LYcols_cu(text) - utfxtra_on_previous_line - indent + ctrl_chars_on_previous_line - previous->size); /* * Shift non-leftaligned UTF-8 lines that would be * mishandled by the display library towards the left * if this would make them fit. The resulting display * will not be as intended, but this is better than * having them split by curses. (Curses cursor movement * optimization may still cause wrong positioning within * the line, in particular after a sequence of spaces). * - kw */ if (spare_cu < spare) { if (spare_cu >= 0) { if (alignment == HT_CENTER && (int) (previous->offset + indent + spare / 2 + previous->size) - ctrl_chars_on_previous_line + utfxtra_on_previous_line <= LYcols_cu(text)) /* do nothing - it still fits - kw */ ; else { spare = spare_cu; } } else if (indent + (int) previous->offset + spare_cu >= 0) { /* subtract overdraft from effective indentation */ indent += (int) previous->offset + spare_cu; previous->offset = 0; spare = 0; } } } } #endif } new_offset = previous->offset; switch (style->alignment) { case HT_CENTER: new_offset += indent + spare / 2; break; case HT_RIGHT: new_offset += indent + spare; break; case HT_LEFT: case HT_JUSTIFY: /* Not implemented */ default: new_offset += indent; break; } /* switch */ previous->offset = (unsigned short) ((new_offset < 0) ? 0 : new_offset); if (text->stbl) /* * Notify simple table stuff of line split, so that it can * set the last cell's length. The last cell should and * its row should really end here, or on one of the following * lines with no more characters added after the break. * We don't know whether a cell has been started, so ignore * errors here. * This call is down here because we need the * ctrl_chars_on_previous_line, which have just been re- * counted above. - kw */ Stbl_lineBreak(text->stbl, text->Lines - 1, previous->offset, previous->size - ctrl_chars_on_previous_line); text->in_line_1 = NO; /* unless caller sets it otherwise */ /* * If we split the line, adjust the anchor * structure values for the new line. -FM */ if (s > 0) { /* if not completely empty */ int moved = 0; /* In the algorithm below we move or not move anchors between lines using some heuristic criteria. However, it is desirable not to have two consequent anchors on different lines *in a wrong order*! (How can this happen?) So when the "reasonable choice" is not unique, we use the MOVED flag to choose one. */ /* Our operations can make a non-empty all-whitespace link empty. So what? */ if ((a = text->last_anchor_before_split) == 0) a = text->first_anchor; for (; a; a = a->next) { if (a->line_num == CurLine) { int len = a->extent, n = a->number, start = a->line_pos; int end = start + len; text->last_anchor_before_split = a; /* Which anchors do we leave on the previous line? a) empty finished (We need a cut-off value. "Just because": those before s; this is the only case when we use s, not s_pre/s_post); b) Those which start before s_pre; */ if (start < s_pre) { if (end <= s_pre) continue; /* No problem */ CTRACE_SPLITLINE((tfp, "anchor %d: no relocation", n)); if (end > s_post) { CTRACE_SPLITLINE((tfp, " of the start.\n")); a->extent = (short) (a->extent - (TailTrim + HeadTrim) + SpecialAttrChars); } else { CTRACE_SPLITLINE((tfp, ", cut the end.\n")); a->extent = (short) (s_pre - start); } continue; } else if (start < s && !len && (!n || (a->show_anchor && !moved))) { CTRACE_SPLITLINE((tfp, "anchor %d: no relocation, empty-finished", n)); a->line_pos = (short) s_pre; /* Leave at the end of line */ continue; } /* The rest we relocate */ moved = 1; a->line_num++; CTRACE_SPLITLINE((tfp, "anchor %d: (T,H,S)=(%d,%d,%d); (line,pos,ext):(%d,%d,%d), ", n, TailTrim, HeadTrim, SpecialAttrChars, a->line_num, a->line_pos, a->extent)); if (end < s_post) { /* Move the end to s_post */ CTRACE_SPLITLINE((tfp, "Move end +%d, ", s_post - end)); len += s_post - end; } if (start < s_post) { /* Move the start to s_post */ CTRACE_SPLITLINE((tfp, "Move start +%d, ", s_post - start)); len -= s_post - start; start = s_post; } a->line_pos = (short) (start - s_post + SpecialAttrChars); a->extent = (short) len; CTRACE_SPLITLINE((tfp, "->(%d,%d,%d)\n", a->line_num, a->line_pos, a->extent)); } else if (a->line_num > CurLine) break; } } #ifdef USE_JUSTIFY_ELTS /* now perform justification - by VH */ if (this_line_was_split && spare > 0 && !text->stbl /* We don't inform TRST on the cell width change yet */ && justify_max_void_percent > 0 && justify_max_void_percent <= 100 && justify_max_void_percent >= ((100 * spare) / (WRAP_COLS(text) - (int) style->rightIndent - indent + ctrl_chars_on_previous_line))) { /* this is the only case when we need justification */ char *jp = previous->data + justify_start_position; ht_run_info *r = ht_runs; char c; int d_, r_; HTLine *jline; ht_num_runs = 0; r->byte_len = r->cell_len = 0; for (; (c = *jp) != 0; ++jp) { if (c == ' ') { ++r; ++ht_num_runs; r->byte_len = r->cell_len = 0; continue; } ++r->byte_len; if (IsSpecialAttrChar(c)) continue; ++r->cell_len; if (c == HT_NON_BREAK_SPACE) { *jp = ' '; /* substitute it */ continue; } if (text->T.output_utf8 && is8bits(c)) { int utf_extra = (int) utf8_length(text->T.output_utf8, jp); r->byte_len += utf_extra; jp += utf_extra; } } ++ht_num_runs; if (ht_num_runs != 1) { int *oldpos = (int *) malloc(sizeof(int) * 2 * (size_t) (ht_num_runs - 1)); int *newpos = oldpos + ht_num_runs - 1; int i = 1; if (oldpos == NULL) outofmem(__FILE__, "split_line_3"); d_ = spare / (ht_num_runs - 1); r_ = spare % (ht_num_runs - 1); /* The first run is not moved, proceed to the second one */ oldpos[0] = justify_start_position + ht_runs[0].cell_len + 1; newpos[0] = oldpos[0] + (d_ + (r_-- > 0)); while (i < ht_num_runs - 1) { int delta = ht_runs[i].cell_len + 1; oldpos[i] = oldpos[i - 1] + delta; newpos[i] = newpos[i - 1] + delta + (d_ + (r_-- > 0)); i++; } jline = insert_blanks_in_line(previous, CurLine, text, &last_anchor_of_previous_line /*updates++ */ , ht_num_runs - 1, oldpos, newpos); free(oldpos); if (jline == NULL) outofmem(__FILE__, "split_line_4"); previous->next->prev = jline; previous->prev->next = jline; freeHTLine(text, previous); previous = jline; } if (justify_start_position) { char *p2 = previous->data; for (; p2 < previous->data + justify_start_position; ++p2) *p2 = (char) (*p2 == HT_NON_BREAK_SPACE ? ' ' : *p2); } } else { if (REALLY_CAN_JUSTIFY(text)) { char *p2; /* it was permitted to justify line, but this function was called * to end paragraph - we must substitute HT_NON_BREAK_SPACEs with * spaces in previous line */ if (line->size && !text->stbl) { CTRACE((tfp, "BUG: justification: shouldn't happen - new line is not empty!\n\t'%.*s'\n", line->size, line->data)); } for (p2 = previous->data; *p2; ++p2) if (*p2 == HT_NON_BREAK_SPACE) *p2 = ' '; } else if (have_raw_nbsps) { /* this is very rare case, that can happen in forms placed in table cells */ unsigned i; for (i = 0; i < previous->size; ++i) if (previous->data[i] == HT_NON_BREAK_SPACE) previous->data[i] = ' '; /*next line won't be justified, so substitute nbsps in it too */ for (i = 0; i < line->size; ++i) if (line->data[i] == HT_NON_BREAK_SPACE) line->data[i] = ' '; } /* else HT_NON_BREAK_SPACEs were substituted with spaces in HText_appendCharacter */ } /* cleanup */ can_justify_this_line = TRUE; justify_start_position = 0; this_line_was_split = FALSE; have_raw_nbsps = FALSE; #endif /* USE_JUSTIFY_ELTS */ return; } /* split_line */ #ifdef DEBUG_SPLITLINE static void do_new_line(HText *text, const char *fn, int ln) { CTRACE_SPLITLINE((tfp, "new_line %s@%d\n", fn, ln)); split_line(text, 0); } #define new_line(text) do_new_line(text, __FILE__, __LINE__) #else #define new_line(text) split_line(text, 0) #endif /* Allow vertical blank space * -------------------------- */ static void blank_lines(HText *text, int newlines) { if (HText_TrueEmptyLine(text->last_line, text, FALSE)) { /* No text on current line */ HTLine *line = text->last_line->prev; BOOL first = (BOOL) (line == text->last_line); if (no_title && first) return; #ifdef USE_COLOR_STYLE /* Style-change petty requests at the start of the document: */ if (first && newlines == 1) return; /* Do not add a blank line at start */ #endif while (line != NULL && line != text->last_line && HText_TrueEmptyLine(line, text, FALSE)) { if (newlines == 0) break; newlines--; /* Don't bother: already blank */ line = line->prev; } } else { newlines++; /* Need also to finish this line */ } for (; newlines; newlines--) { new_line(text); } text->in_line_1 = YES; } /* New paragraph in current style * ------------------------------ * See also: setStyle. */ void HText_appendParagraph(HText *text) { int after = text->style->spaceAfter; int before = text->style->spaceBefore; blank_lines(text, ((after > before) ? after : before)); } /* Set Style * --------- * * Does not filter unnecessary style changes. */ void HText_setStyle(HText *text, HTStyle *style) { int after, before; if (!style) return; /* Safety */ after = text->style->spaceAfter; before = style->spaceBefore; CTRACE((tfp, "GridText: Change to style %s\n", GetHTStyleName(style))); blank_lines(text, ((after > before) ? after : before)); text->style = style; } /* Append a character to the text object * ------------------------------------- */ void HText_appendCharacter(HText *text, int ch) { HTLine *line; HTStyle *style; int indent; int actual; #ifdef DEBUG_APPCH #ifdef CJK_EX static unsigned char save_ch = 0; #endif if (TRACE) { char *special = NULL; /* make trace a little more readable */ switch (ch) { case HT_NON_BREAK_SPACE: special = "HT_NON_BREAK_SPACE"; break; case HT_EN_SPACE: special = "HT_EN_SPACE"; break; case LY_UNDERLINE_START_CHAR: special = "LY_UNDERLINE_START_CHAR"; break; case LY_UNDERLINE_END_CHAR: special = "LY_UNDERLINE_END_CHAR"; break; case LY_BOLD_START_CHAR: special = "LY_BOLD_START_CHAR"; break; case LY_BOLD_END_CHAR: special = "LY_BOLD_END_CHAR"; break; case LY_SOFT_HYPHEN: special = "LY_SOFT_HYPHEN"; break; case LY_SOFT_NEWLINE: special = "LY_SOFT_NEWLINE"; break; default: special = NULL; break; } if (special != NULL) { CTRACE((tfp, "add(%s %d special char) %d/%d\n", special, ch, HTisDocumentSource(), HTOutputFormat != WWW_SOURCE)); } else { #ifdef CJK_EX /* 1998/08/30 (Sun) 13:26:23 */ if (save_ch == 0) { if (IS_SJIS_HI1(ch) || IS_SJIS_HI2(ch)) { save_ch = ch; } else { CTRACE((tfp, "add(%c) %d/%d\n", ch, HTisDocumentSource(), HTOutputFormat != WWW_SOURCE)); } } else { CTRACE((tfp, "add(%c%c) %d/%d\n", save_ch, ch, HTisDocumentSource(), HTOutputFormat != WWW_SOURCE)); save_ch = 0; } #else if (UCH(ch) < 0x80) { CTRACE((tfp, "add(%c) %d/%d\n", UCH(ch), HTisDocumentSource(), HTOutputFormat != WWW_SOURCE)); } else { CTRACE((tfp, "add(%02x) %d/%d\n", UCH(ch), HTisDocumentSource(), HTOutputFormat != WWW_SOURCE)); } #endif /* CJK_EX */ } } /* trace only */ #endif /* DEBUG_APPCH */ /* * Make sure we don't crash on NULLs. */ if (!text) return; if (text->halted > 1) { /* * We should stop outputting more text, because low memory was * detected. - kw */ if (text->halted == 2) { /* * But if we haven't done so yet, first append a warning. * We should still have a few bytes left for that :). * We temporarily reset test->halted to 0 for this, since * this function will get called recursively. - kw */ text->halted = 0; text->kanji_buf = '\0'; HText_appendText(text, gettext(" *** MEMORY EXHAUSTED ***")); } text->halted = 3; return; } #ifdef USE_TH_JP_AUTO_DETECT if ((HTCJK == JAPANESE) && (text->detected_kcode != DET_MIXED) && (text->specified_kcode != SJIS) && (text->specified_kcode != EUC)) { unsigned char c; eDetectedKCode save_d_kcode; c = UCH(ch); save_d_kcode = text->detected_kcode; switch (text->SJIS_status) { case SJIS_state_has_bad_code: break; case SJIS_state_neutral: if (IS_SJIS_HI1(c) || IS_SJIS_HI2(c)) { text->SJIS_status = SJIS_state_in_kanji; } else if ((c & 0x80) && !IS_SJIS_X0201KANA(c)) { text->SJIS_status = SJIS_state_has_bad_code; if (text->EUC_status == EUC_state_has_bad_code) text->detected_kcode = DET_MIXED; else text->detected_kcode = DET_EUC; } break; case SJIS_state_in_kanji: if (IS_SJIS_LO(c)) { text->SJIS_status = SJIS_state_neutral; } else { text->SJIS_status = SJIS_state_has_bad_code; if (text->EUC_status == EUC_state_has_bad_code) text->detected_kcode = DET_MIXED; else text->detected_kcode = DET_EUC; } break; } switch (text->EUC_status) { case EUC_state_has_bad_code: break; case EUC_state_neutral: if (IS_EUC_HI(c)) { text->EUC_status = EUC_state_in_kanji; } else if (c == 0x8e) { text->EUC_status = EUC_state_in_kana; } else if (c & 0x80) { text->EUC_status = EUC_state_has_bad_code; if (text->SJIS_status == SJIS_state_has_bad_code) text->detected_kcode = DET_MIXED; else text->detected_kcode = DET_SJIS; } break; case EUC_state_in_kanji: if (IS_EUC_LOX(c)) { text->EUC_status = EUC_state_neutral; } else { text->EUC_status = EUC_state_has_bad_code; if (text->SJIS_status == SJIS_state_has_bad_code) text->detected_kcode = DET_MIXED; else text->detected_kcode = DET_SJIS; } break; case EUC_state_in_kana: if ((0xA1 <= c) && (c <= 0xDF)) { text->EUC_status = EUC_state_neutral; } else { text->EUC_status = EUC_state_has_bad_code; if (text->SJIS_status == SJIS_state_has_bad_code) text->detected_kcode = DET_MIXED; else text->detected_kcode = DET_SJIS; } break; } if (save_d_kcode != text->detected_kcode) { switch (text->detected_kcode) { case DET_SJIS: CTRACE((tfp, "TH_JP_AUTO_DETECT: This document's kcode seems SJIS.\n")); break; case DET_EUC: CTRACE((tfp, "TH_JP_AUTO_DETECT: This document's kcode seems EUC.\n")); break; case DET_MIXED: CTRACE((tfp, "TH_JP_AUTO_DETECT: This document's kcode seems mixed!\n")); break; default: CTRACE((tfp, "TH_JP_AUTO_DETECT: This document's kcode is unexpected!\n")); break; } } } #endif /* USE_TH_JP_AUTO_DETECT */ /* * Make sure we don't hang on escape sequences. */ if (ch == CH_ESC && !IS_CJK_TTY) { /* decimal 27 S/390 -- gil -- 1504 */ return; } #ifndef USE_SLANG /* * Block 8-bit chars not allowed by the current display character * set if they are below what LYlowest_eightbit indicates. * Slang used its own replacements, so for USE_SLANG blocking here * is not necessary to protect terminals from those characters. * They should have been filtered out or translated by an earlier * processing stage anyway. - kw */ #ifndef EBCDIC /* S/390 -- gil -- 1514 */ if (is8bits(ch) && !IS_CJK_TTY && !text->T.transp && !text->T.output_utf8 && UCH(ch) < LYlowest_eightbit[current_char_set]) { return; } #endif /* EBCDIC */ #endif /* !USE_SLANG */ if (UCH(ch) == 155 && !IS_CJK_TTY) { /* octal 233 */ if (!HTPassHighCtrlRaw && !text->T.transp && !text->T.output_utf8 && (155 < LYlowest_eightbit[current_char_set])) { return; } } line = text->last_line; style = text->style; indent = text->in_line_1 ? (int) style->indent1st : (int) style->leftIndent; if (IS_CJK_TTY) { switch (text->state) { case S_text: if (ch == CH_ESC) { /* S/390 -- gil -- 1536 */ /* * Setting up for CJK escape sequence handling (based on * Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). -FM */ text->state = S_esc; text->kanji_buf = '\0'; return; } break; case S_esc: /* * Expecting '$'or '(' following CJK ESC. */ if (ch == '$') { text->state = S_dollar; return; } else if (ch == '(') { text->state = S_paren; return; } else { text->state = S_text; } /* FALLTHRU */ case S_dollar: /* * Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */ if (ch == '@' || ch == 'B' || ch == 'A') { text->state = S_nonascii_text; if (ch == '@' || ch == 'B') text->kcode = JIS; return; } else if (ch == '(') { text->state = S_dollar_paren; return; } else { text->state = S_text; } break; case S_dollar_paren: /* * Expecting 'C' after CJK "ESC$(". */ if (ch == 'C') { text->state = S_nonascii_text; return; } else { text->state = S_text; } break; case S_paren: /* * Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */ if (ch == 'B' || ch == 'J' || ch == 'T') { /* * Can split here. -FM */ text->permissible_split = text->last_line->size; text->state = S_text; return; } else if (ch == 'I') { text->state = S_jisx0201_text; /* * Can split here. -FM */ text->permissible_split = text->last_line->size; text->kcode = JIS; return; } else { text->state = S_text; } break; case S_nonascii_text: /* * Expecting CJK ESC after non-ASCII text. */ if (ch == CH_ESC) { /* S/390 -- gil -- 1553 */ text->state = S_esc; text->kanji_buf = '\0'; if (HTCJK == JAPANESE) { text->kcode = NOKANJI; } return; } else if (UCH(ch) < 32) { text->state = S_text; text->kanji_buf = '\0'; if (HTCJK == JAPANESE) { text->kcode = NOKANJI; } } else { ch |= 0200; } break; /* * JIS X0201 Kana in JIS support. - by ASATAKU */ case S_jisx0201_text: if (ch == CH_ESC) { /* S/390 -- gil -- 1570 */ text->state = S_esc; text->kanji_buf = '\0'; text->kcode = NOKANJI; return; } else { text->kanji_buf = '\216'; ch |= 0200; } break; } /* end switch */ if (!text->kanji_buf) { if ((ch & 0200) != 0) { /* * JIS X0201 Kana in SJIS support. - by ASATAKU */ if ((text->kcode != JIS) #ifdef USE_TH_JP_AUTO_DETECT && (text->specified_kcode != EUC) && (text->detected_kcode != DET_EUC) #endif && ( #ifdef KANJI_CODE_OVERRIDE (last_kcode == SJIS) || ((last_kcode == NOKANJI) && #endif ((text->kcode == SJIS) || #ifdef USE_TH_JP_AUTO_DETECT ((text->detected_kcode == DET_SJIS) && (text->specified_kcode == NOKANJI)) || #endif ((text->kcode == NOKANJI) && (text->specified_kcode == SJIS))) #ifdef KANJI_CODE_OVERRIDE ) #endif ) && (UCH(ch) >= 0xA1) && (UCH(ch) <= 0xDF)) { if (conv_jisx0201kana) { unsigned char c = UCH(ch); unsigned char kb = UCH(text->kanji_buf); JISx0201TO0208_SJIS(c, (unsigned char *) &kb, (unsigned char *) &c); ch = (char) c; text->kanji_buf = kb; } /* 1998/01/19 (Mon) 09:06:15 */ text->permissible_split = (int) text->last_line->size; } else { text->kanji_buf = ch; /* * Can split here. -FM */ text->permissible_split = text->last_line->size; return; } } } else { goto check_WrapSource; } } else if (ch == CH_ESC) { /* S/390 -- gil -- 1587 */ return; } #ifdef CJK_EX /* MOJI-BAKE Fix! 1997/10/12 -- 10/31 (Fri) 00:22:57 - JH7AYN */ if (IS_CJK_TTY && /* added condition - kw */ (ch == LY_BOLD_START_CHAR || ch == LY_BOLD_END_CHAR)) { text->permissible_split = (int) line->size; /* Can split here */ if (HTCJK == JAPANESE) text->kcode = NOKANJI; } #endif if (IsSpecialAttrChar(ch) && ch != LY_SOFT_NEWLINE) { #if !defined(USE_COLOR_STYLE) || !defined(NO_DUMP_WITH_BACKSPACES) if (line->size >= (MAX_LINE - 1)) { return; } #if defined(USE_COLOR_STYLE) && !defined(NO_DUMP_WITH_BACKSPACES) if (with_backspaces && !IS_CJK_TTY && !text->T.output_utf8) { #endif if (ch == LY_UNDERLINE_START_CHAR) { line->data[line->size++] = LY_UNDERLINE_START_CHAR; line->data[line->size] = '\0'; underline_on = TRUE; if (!(dump_output_immediately && use_underscore)) ctrl_chars_on_this_line++; return; } else if (ch == LY_UNDERLINE_END_CHAR) { line->data[line->size++] = LY_UNDERLINE_END_CHAR; line->data[line->size] = '\0'; underline_on = FALSE; if (!(dump_output_immediately && use_underscore)) ctrl_chars_on_this_line++; return; } else if (ch == LY_BOLD_START_CHAR) { line->data[line->size++] = LY_BOLD_START_CHAR; line->data[line->size] = '\0'; bold_on = TRUE; ctrl_chars_on_this_line++; return; } else if (ch == LY_BOLD_END_CHAR) { line->data[line->size++] = LY_BOLD_END_CHAR; line->data[line->size] = '\0'; bold_on = FALSE; ctrl_chars_on_this_line++; return; } else if (ch == LY_SOFT_HYPHEN) { int i; /* * Ignore the soft hyphen if it is the first character * on the line, or if it is preceded by a space or * hyphen. -FM */ if (line->size < 1 || text->permissible_split >= line->size) { return; } for (i = (int) (text->permissible_split + 1); line->data[i]; i++) { if (!IsSpecialAttrChar(UCH(line->data[i])) && !isspace(UCH(line->data[i])) && UCH(line->data[i]) != '-' && UCH(line->data[i]) != HT_NON_BREAK_SPACE && UCH(line->data[i]) != HT_EN_SPACE) { break; } } if (line->data[i] == '\0') { return; } } #if defined(USE_COLOR_STYLE) && !defined(NO_DUMP_WITH_BACKSPACES) } else { /* if (with_backspaces && HTCJK==HTNOCJK && !text->T.output_utf8) */ return; } #endif #else return; #endif } else if (ch == LY_SOFT_NEWLINE) { line->data[line->size++] = LY_SOFT_NEWLINE; line->data[line->size] = '\0'; return; } if (text->T.output_utf8) { /* * Some extra checks for UTF-8 output here to make sure * memory is not overrun. For a non-first char, append * to the line here and return. - kw */ if (IS_UTF_EXTRA(ch)) { if ((line->size > (MAX_LINE - 1)) || (indent + (int) (line->offset + line->size) + UTFXTRA_ON_THIS_LINE - ctrl_chars_on_this_line + ((line->size > 0) && (int) (line->data[line->size - 1] == LY_SOFT_HYPHEN ? 1 : 0)) >= LYcols_cu(text)) ) { if (!text->permissible_split || text->source) { text->permissible_split = line->size; while (text->permissible_split > 0 && IS_UTF_EXTRA(line->data[text->permissible_split - 1])) text->permissible_split--; if (text->permissible_split && (line->data[text->permissible_split - 1] & 0x80)) text->permissible_split--; if (text->permissible_split == line->size) text->permissible_split = 0; } split_line(text, text->permissible_split); line = text->last_line; if (text->source && line->size - ctrl_chars_on_this_line + UTFXTRA_ON_THIS_LINE == 0) HText_appendCharacter(text, LY_SOFT_NEWLINE); } line->data[line->size++] = (char) ch; line->data[line->size] = '\0'; utfxtra_on_this_line++; ctrl_chars_on_this_line++; return; } else if (ch & 0x80) { /* a first char of UTF-8 sequence - kw */ if ((line->size > (MAX_LINE - 7))) { if (!text->permissible_split || text->source) { text->permissible_split = line->size; while (text->permissible_split > 0 && (line->data[text->permissible_split - 1] & 0xc0) == 0x80) { text->permissible_split--; } if (text->permissible_split == line->size) text->permissible_split = 0; } split_line(text, text->permissible_split); line = text->last_line; if (text->source && line->size - ctrl_chars_on_this_line + UTFXTRA_ON_THIS_LINE == 0) HText_appendCharacter(text, LY_SOFT_NEWLINE); } } } /* * New Line. */ if (ch == '\n') { new_line(text); text->in_line_1 = YES; /* First line of new paragraph */ /* * There are some pages written in * different kanji codes. - TA & kw */ if (HTCJK == JAPANESE) text->kcode = NOKANJI; return; } /* * Convert EN_SPACE to a space here so that it doesn't get collapsed. */ if (ch == HT_EN_SPACE) ch = ' '; #ifdef SH_EX /* 1997/11/01 (Sat) 12:08:54 */ if (ch == 0x0b) { /* ^K ??? */ ch = '\r'; } if (ch == 0x1a) { /* ^Z ??? */ ch = '\r'; } #endif /* * I'm going to cheat here in a BIG way. Since I know that all * \r's will be trapped by HTML_put_character I'm going to use * \r to mean go down a line but don't start a new paragraph. * i.e., use the second line indenting. */ if (ch == '\r') { new_line(text); text->in_line_1 = NO; /* * There are some pages written in * different kanji codes. - TA & kw */ if (HTCJK == JAPANESE) text->kcode = NOKANJI; return; } /* * Tabs. */ if (ch == '\t') { const HTTabStop *Tab; int target, target_cu; /* Where to tab to */ int here, here_cu; /* in _cu we try to guess what curses thinks */ if (line->size > 0 && line->data[line->size - 1] == LY_SOFT_HYPHEN) { /* * A tab shouldn't follow a soft hyphen, so * if one does, we'll dump the soft hyphen. -FM */ line->data[--line->size] = '\0'; ctrl_chars_on_this_line--; } here = ((int) (line->size + line->offset) + indent) - ctrl_chars_on_this_line; /* Consider special chars GAB */ here_cu = here + UTFXTRA_ON_THIS_LINE; if (style->tabs) { /* Use tab table */ for (Tab = style->tabs; Tab->position <= here; Tab++) { if (!Tab->position) { new_line(text); return; } } target = Tab->position; } else if (text->in_line_1) { /* Use 2nd indent */ if (here >= (int) style->leftIndent) { new_line(text); /* wrap */ return; } else { target = (int) style->leftIndent; } } else { /* Default tabs align with left indent mod 8 */ #ifdef DEFAULT_TABS_8 target = (((int) line->offset + (int) line->size + 8) & (-8)) + (int) style->leftIndent; #else new_line(text); return; #endif } if (target >= here) target_cu = target; else target_cu = target + (here_cu - here); if (target > WRAP_COLS(text) - (int) style->rightIndent && HTOutputFormat != WWW_SOURCE) { new_line(text); } else { /* * Can split here. -FM */ text->permissible_split = line->size; if (target_cu > WRAP_COLS(text)) target -= target_cu - WRAP_COLS(text); if (line->size == 0) { line->offset = (unsigned short) (line->offset + (target - here)); } else { for (; here < target; here++) { /* Put character into line */ line->data[line->size++] = ' '; line->data[line->size] = '\0'; } } } return; } /* if tab */ check_WrapSource: if ((text->source || dont_wrap_pre) && text == HTMainText) { /* * If we're displaying document source, wrap long lines to keep all of * the source visible. */ int target = (int) (line->offset + line->size) - ctrl_chars_on_this_line; int target_cu = target + UTFXTRA_ON_THIS_LINE; if (target >= WRAP_COLS(text) - style->rightIndent - ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0) || (text->T.output_utf8 && target_cu + UTF_XLEN(ch) >= LYcols_cu(text))) { int saved_kanji_buf; eGridState saved_state; BOOL add_blank = (dont_wrap_pre && line->size && (line->data[line->size - 1] == ' ')); new_line(text); line = text->last_line; saved_kanji_buf = text->kanji_buf; saved_state = text->state; text->kanji_buf = '\0'; text->state = S_text; HText_appendCharacter(text, LY_SOFT_NEWLINE); if (add_blank) HText_appendCharacter(text, ' '); text->kanji_buf = saved_kanji_buf; text->state = saved_state; } } if (ch == ' ') { /* * Can split here. -FM */ text->permissible_split = text->last_line->size; /* * There are some pages written in * different kanji codes. - TA */ if (HTCJK == JAPANESE) text->kcode = NOKANJI; } /* * Check for end of line. * * Notes: * 1) text->permissible_split is nonzero if we found a place to split the * line. If there is no such place, we still will wrap at the display * limits (the comparison against LYcols_cu). Furthermore, if the * curses-pads feature is active, we will ignore the first comparison * (against WRAP_COLS) to allow wide preformatted text to be displayed * without wrapping. * 2) ctrl_chars_on_this_line are nonprintable bytes used for formatting. */ actual = ((indent + (int) line->offset + (int) line->size) + ((line->size > 0) && (int) (line->data[line->size - 1] == LY_SOFT_HYPHEN ? 1 : 0)) - ctrl_chars_on_this_line); if (((text->permissible_split #ifdef USE_CURSES_PADS || !LYwideLines #endif ) && (actual + (int) style->rightIndent + ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0) ) >= WRAP_COLS(text)) || (text->T.output_utf8 && ((actual + UTFXTRA_ON_THIS_LINE + UTF_XLEN(ch) ) > (LYcols_cu(text) - 1)))) { if (style->wordWrap && HTOutputFormat != WWW_SOURCE) { #ifdef USE_JUSTIFY_ELTS if (REALLY_CAN_JUSTIFY(text)) this_line_was_split = TRUE; #endif split_line(text, text->permissible_split); if (ch == ' ') { return; /* Ignore space causing split */ } } else if (HTOutputFormat == WWW_SOURCE) { /* * For source output we don't want to wrap this stuff * unless absolutely necessary. - LJM * ! * If we don't wrap here we might get a segmentation fault. * but let's see what happens */ if ((int) line->size >= (int) (MAX_LINE - 1)) { new_line(text); /* try not to linewrap */ } } else { /* * For normal stuff like pre let's go ahead and * wrap so the user can see all of the text. */ if ((dump_output_immediately || (crawl && traversal)) && dont_wrap_pre) { if ((int) line->size >= (int) (MAX_LINE - 1)) { new_line(text); } } else { new_line(text); } } } else if ((int) line->size >= (int) (MAX_LINE - 1)) { /* * Never overrun memory if DISPLAY_COLS is set to a large value - KW */ new_line(text); } /* * Insert normal characters. */ if (ch == HT_NON_BREAK_SPACE #ifdef USE_JUSTIFY_ELTS && !REALLY_CAN_JUSTIFY(text) #endif ) ch = ' '; #ifdef USE_JUSTIFY_ELTS else have_raw_nbsps = TRUE; #endif /* we leave raw HT_NON_BREAK_SPACE otherwise (we'll substitute it later) */ if (ch & 0x80) text->have_8bit_chars = YES; /* * Kanji charactor handling. */ { HTFont font = style->font; unsigned char hi, lo, tmp[2]; line = text->last_line; /* May have changed */ if (IS_CJK_TTY && text->kanji_buf) { hi = UCH(text->kanji_buf); lo = UCH(ch); if (HTCJK == JAPANESE) { if (text->kcode != JIS) { if (IS_SJIS_2BYTE(hi, lo)) { if (IS_EUC(hi, lo)) { #ifdef KANJI_CODE_OVERRIDE if (last_kcode != NOKANJI) text->kcode = last_kcode; else #endif if (text->specified_kcode != NOKANJI) text->kcode = text->specified_kcode; #ifdef USE_TH_JP_AUTO_DETECT else if (text->detected_kcode == DET_EUC) text->kcode = EUC; else if (text->detected_kcode == DET_SJIS) text->kcode = SJIS; #endif else if (IS_EUC_X0201KANA(hi, lo) && (text->kcode != EUC)) text->kcode = SJIS; } else text->kcode = SJIS; } else if (IS_EUC(hi, lo)) text->kcode = EUC; else text->kcode = NOKANJI; } switch (kanji_code) { case EUC: if (text->kcode == SJIS) { SJIS_TO_EUC1(hi, lo, tmp); line->data[line->size++] = (char) tmp[0]; line->data[line->size++] = (char) tmp[1]; } else if (IS_EUC(hi, lo)) { if (conv_jisx0201kana) { JISx0201TO0208_EUC(hi, lo, &hi, &lo); } line->data[line->size++] = (char) hi; line->data[line->size++] = (char) lo; } else { CTRACE((tfp, "This character (%X:%X) doesn't seem Japanese\n", hi, lo)); line->data[line->size++] = '='; line->data[line->size++] = '='; } break; case SJIS: if ((text->kcode == EUC) || (text->kcode == JIS)) { if (!conv_jisx0201kana && IS_EUC_X0201KANA(hi, lo)) { if (IS_EUC_X0201KANA(hi, lo)) { line->data[line->size++] = (char) lo; } else { EUC_TO_SJIS1(hi, lo, tmp); line->data[line->size++] = (char) tmp[0]; line->data[line->size++] = (char) tmp[1]; } } } else if (IS_SJIS_2BYTE(hi, lo)) { line->data[line->size++] = (char) hi; line->data[line->size++] = (char) lo; } else { line->data[line->size++] = '='; line->data[line->size++] = '='; CTRACE((tfp, "This character (%X:%X) doesn't seem Japanese\n", hi, lo)); } break; default: break; } } else { line->data[line->size++] = (char) hi; line->data[line->size++] = (char) lo; } text->kanji_buf = 0; } else if (!conv_jisx0201kana && (HTCJK == JAPANESE) && IS_SJIS_X0201KANA(UCH((ch))) && (kanji_code == EUC)) { line->data[line->size++] = (char) UCH(0x8e); line->data[line->size++] = (char) ch; } else if (IS_CJK_TTY) { line->data[line->size++] = (char) ((kanji_code != NOKANJI) ? ch : (font & HT_CAPITALS) ? TOUPPER(ch) : ch); } else { line->data[line->size++] = /* Put character into line */ (char) (font & HT_CAPITALS ? TOUPPER(ch) : ch); } line->data[line->size] = '\0'; if (font & HT_DOUBLE) /* Do again if doubled */ HText_appendCharacter(text, HT_NON_BREAK_SPACE); /* NOT a permissible split */ if (ch == LY_SOFT_HYPHEN) { ctrl_chars_on_this_line++; /* * Can split here. -FM */ text->permissible_split = text->last_line->size; } if (ch == LY_SOFT_NEWLINE) { ctrl_chars_on_this_line++; } } return; } #ifdef USE_COLOR_STYLE /* Insert a style change into the current line * ------------------------------------------- */ void _internal_HTC(HText *text, int style, int dir) { HTLine *line; /* can't change style if we have no text to change style with */ if (text != 0) { line = text->last_line; if (line->numstyles > 0 && dir == 0 && line->styles[line->numstyles - 1].sc_direction && line->styles[line->numstyles - 1].sc_style == (unsigned) style && (int) line->styles[line->numstyles - 1].sc_horizpos == (int) line->size - ctrl_chars_on_this_line) { /* * If this is an OFF change directly preceded by an * ON for the same style, just remove the previous one. - kw */ line->numstyles--; } else if (line->numstyles < MAX_STYLES_ON_LINE) { line->styles[line->numstyles].sc_horizpos = line->size; /* * Special chars for bold and underlining usually don't * occur with color style, but soft hyphen can. * And in UTF-8 display mode all non-initial bytes are * counted as ctrl_chars. - kw */ if ((int) line->styles[line->numstyles].sc_horizpos >= ctrl_chars_on_this_line) { line->styles[line->numstyles].sc_horizpos -= ctrl_chars_on_this_line; } line->styles[line->numstyles].sc_style = style; line->styles[line->numstyles].sc_direction = dir; CTRACE_STYLE((tfp, "internal_HTC %d:style[%d] %d (dir=%d)\n", line->size, line->numstyles, style, dir)); line->numstyles++; } } } #endif /* Set LastChar element in the text object. * ---------------------------------------- */ void HText_setLastChar(HText *text, int ch) { if (!text) return; text->LastChar = (char) ch; } /* Get LastChar element in the text object. * ---------------------------------------- */ char HText_getLastChar(HText *text) { if (!text) return ('\0'); return ((char) text->LastChar); } /* Simple table handling - private * ------------------------------- */ /* * HText_insertBlanksInStblLines fixes up table lines when simple table * processing is closed, by calling insert_blanks_in_line for lines * that need fixup. Also recalculates alignment for those lines, * does additional updating of anchor positions, and makes sure the * display of the lines on screen will be updated after partial display * upon return to mainloop. - kw */ static int HText_insertBlanksInStblLines(HText *me, int ncols) { HTLine *line; HTLine *mod_line, *first_line = NULL; int *oldpos; int *newpos; int ninserts, lineno; int last_lineno, first_lineno_pass2; #ifdef EXP_NESTED_TABLES int last_nonempty = -1; #endif int added_chars_before = 0; int lines_changed = 0; int max_width = 0, indent, spare, table_offset; HTStyle *style; short alignment; int i = 0; lineno = Stbl_getStartLine(me->stbl); if (lineno < 0 || lineno > me->Lines) return -1; /* * oldpos, newpos: allocate space for two int arrays. */ oldpos = typecallocn(int, 2 * (size_t)ncols); if (!oldpos) return -1; else newpos = oldpos + ncols; for (line = FirstHTLine(me); i < lineno; line = line->next, i++) { if (!line) { free(oldpos); return -1; } } first_lineno_pass2 = last_lineno = me->Lines; for (; line && lineno <= last_lineno; line = line->next, lineno++) { ninserts = Stbl_getFixupPositions(me->stbl, lineno, oldpos, newpos); if (ninserts < 0) continue; if (!first_line) { first_line = line; first_lineno_pass2 = lineno; if (TRACE) { int ip; CTRACE((tfp, "line %d first to adjust -- newpos:", lineno)); for (ip = 0; ip < ncols; ip++) CTRACE((tfp, " %d", newpos[ip])); CTRACE((tfp, "\n")); } } if (line == me->last_line) { if (line->size == 0 || HText_TrueEmptyLine(line, me, FALSE)) continue; /* * Last ditch effort to end the table with a line break, * if HTML_end_element didn't do it. - kw */ if (first_line == line) /* obscure: all table on last line... */ first_line = NULL; new_line(me); line = me->last_line->prev; if (first_line == NULL) first_line = line; } if (ninserts == 0) { /* Do it also for no positions (but not error) */ int width = HText_TrueLineSize(line, me, FALSE); if (width > max_width) max_width = width; #ifdef EXP_NESTED_TABLES if (nested_tables) { if (width && last_nonempty < lineno) last_nonempty = lineno; } #endif CTRACE((tfp, "line %d true/max width:%d/%d oldpos: NONE\n", lineno, width, max_width)); continue; } mod_line = insert_blanks_in_line(line, lineno, me, &me->last_anchor_before_stbl /*updates++ */ , ninserts, oldpos, newpos); if (mod_line) { if (line == me->last_line) { me->last_line = mod_line; } else { added_chars_before += (mod_line->size - line->size); } line->prev->next = mod_line; line->next->prev = mod_line; lines_changed++; if (line == first_line) first_line = mod_line; freeHTLine(me, line); line = mod_line; #ifdef DISP_PARTIAL /* * Make sure modified lines get fully re-displayed after * loading with partial display is done. */ if (me->first_lineno_last_disp_partial >= 0) { if (me->first_lineno_last_disp_partial >= lineno) { ResetPartialLinenos(me); } else if (me->last_lineno_last_disp_partial >= lineno) { me->last_lineno_last_disp_partial = lineno - 1; } } #endif } { int width = HText_TrueLineSize(line, me, FALSE); if (width > max_width) max_width = width; #ifdef EXP_NESTED_TABLES if (nested_tables) { if (width && last_nonempty < lineno) last_nonempty = lineno; } #endif if (TRACE) { int ip; CTRACE((tfp, "line %d true/max width:%d/%d oldpos:", lineno, width, max_width)); for (ip = 0; ip < ninserts; ip++) CTRACE((tfp, " %d", oldpos[ip])); CTRACE((tfp, "\n")); } } } /* * Line offsets have been set based on the paragraph style, and * have already been updated for centering or right-alignment * for each line in split_line. Here we want to undo all that, and * align the table as a whole (i.e. all lines for which * Stbl_getFixupPositions returned >= 0). All those lines have to * get the same offset, for the simple table formatting mechanism * to make sense, and that may not actually be the case at this point. * * What indentation and alignment do we want for the table as * a whole? Let's take most style properties from me->style. * With some luck, it is the appropriate style for the element * enclosing the TABLE. But let's take alignment from the attribute * of the TABLE itself instead, if it was specified. * * Note that this logic assumes that all lines have been finished * by split_line. The order of calls made by HTML_end_element for * HTML_TABLE should take care of this. */ style = me->style; alignment = Stbl_getAlignment(me->stbl); if (alignment == HT_ALIGN_NONE) alignment = style->alignment; indent = style->leftIndent; /* Calculate spare character positions */ spare = WRAP_COLS(me) - (int) style->rightIndent - indent - max_width; if (spare < 0 && (int) style->rightIndent + spare >= 0) { /* * Not enough room! But we can fit if we ignore right indentation, * so let's do that. */ spare = 0; } else if (spare < 0) { spare += style->rightIndent; /* ignore right indent, but need more */ } if (spare < 0 && indent + spare >= 0) { /* * Still not enough room. But we can move to the left. */ indent += spare; spare = 0; } else if (spare < 0) { /* * Still not enough. Something went wrong. Try the best we * can do. */ CTRACE((tfp, "BUG: insertBlanks: resulting table too wide by %d positions!\n", -spare)); indent = spare = 0; } /* * Align left, right or center. */ switch (alignment) { case HT_CENTER: table_offset = indent + spare / 2; break; case HT_RIGHT: table_offset = indent + spare; break; case HT_LEFT: case HT_JUSTIFY: default: table_offset = indent; break; } /* switch */ CTRACE((tfp, "changing offsets")); for (line = first_line, lineno = first_lineno_pass2; line && lineno <= last_lineno && line != me->last_line; line = line->next, lineno++) { ninserts = Stbl_getFixupPositions(me->stbl, lineno, oldpos, newpos); if (ninserts >= 0 && (int) line->offset != table_offset) { #ifdef DISP_PARTIAL /* As above make sure modified lines get fully re-displayed */ if (me->first_lineno_last_disp_partial >= 0) { if (me->first_lineno_last_disp_partial >= lineno) { ResetPartialLinenos(me); } else if (me->last_lineno_last_disp_partial >= lineno) { me->last_lineno_last_disp_partial = lineno - 1; } } #endif CTRACE((tfp, " %d:%d", lineno, table_offset - line->offset)); line->offset = (unsigned short) (table_offset > 0 ? table_offset : 0); } } #ifdef EXP_NESTED_TABLES if (nested_tables) { if (max_width) Stbl_update_enclosing(me->stbl, max_width, last_nonempty); } #endif CTRACE((tfp, " %d:done\n", lineno)); free(oldpos); return lines_changed; } /* Simple table handling - public functions * ---------------------------------------- */ /* Cancel simple table handling */ void HText_cancelStbl(HText *me) { if (!me || !me->stbl) { CTRACE((tfp, "cancelStbl: ignored.\n")); return; } CTRACE((tfp, "cancelStbl: ok, will do.\n")); #ifdef EXP_NESTED_TABLES if (nested_tables) { STable_info *stbl = me->stbl; while (stbl) { STable_info *enclosing = Stbl_get_enclosing(stbl); Stbl_free(stbl); stbl = enclosing; } } else #endif Stbl_free(me->stbl); me->stbl = NULL; } /* Start simple table handling */ void HText_startStblTABLE(HText *me, int alignment) { if (me) { #ifdef EXP_NESTED_TABLES STable_info *current = me->stbl; #endif #ifdef EXP_NESTED_TABLES if (nested_tables) { if (current) new_line(me); } else #endif { if (me->stbl) HText_cancelStbl(me); /* auto cancel previously open table */ } me->stbl = Stbl_startTABLE(alignment); if (me->stbl) { CTRACE((tfp, "startStblTABLE: started.\n")); #ifdef EXP_NESTED_TABLES if (nested_tables) { Stbl_set_enclosing(me->stbl, current, me->last_anchor_before_stbl); } #endif me->last_anchor_before_stbl = me->last_anchor; } else { CTRACE((tfp, "startStblTABLE: failed.\n")); } } } #ifdef EXP_NESTED_TABLES static void free_enclosed_stbl(HText *me) { if (me != NULL && me->enclosed_stbl != NULL) { HTList *list = me->enclosed_stbl; STable_info *stbl; while (NULL != (stbl = (STable_info *) HTList_nextObject(list))) { CTRACE((tfp, "endStblTABLE: finally free %p\n", (void *) me->stbl)); Stbl_free(stbl); } HTList_delete(me->enclosed_stbl); me->enclosed_stbl = NULL; } } #else #define free_enclosed_stbl(me) /* nothing */ #endif /* Finish simple table handling * Return TRUE if the table is nested inside another table. */ BOOLEAN HText_endStblTABLE(HText *me) { int ncols, lines_changed = 0; STable_info *enclosing = NULL; if (!me || !me->stbl) { CTRACE((tfp, "endStblTABLE: ignored.\n")); free_enclosed_stbl(me); return FALSE; } CTRACE((tfp, "endStblTABLE: ok, will try.\n")); ncols = Stbl_finishTABLE(me->stbl); CTRACE((tfp, "endStblTABLE: ncols = %d.\n", ncols)); if (ncols > 0) { lines_changed = HText_insertBlanksInStblLines(me, ncols); CTRACE((tfp, "endStblTABLE: changed %d lines, done.\n", lines_changed)); #ifdef DISP_PARTIAL /* allow HTDisplayPartial() to redisplay the changed lines. * There is no harm if we got several stbl in the document, hope so. */ NumOfLines_partial -= lines_changed; /* fake */ #endif /* DISP_PARTIAL */ } #ifdef EXP_NESTED_TABLES if (nested_tables) { enclosing = Stbl_get_enclosing(me->stbl); me->last_anchor_before_stbl = Stbl_get_last_anchor_before(me->stbl); if (enclosing == NULL) { Stbl_free(me->stbl); free_enclosed_stbl(me); } else { if (me->enclosed_stbl == NULL) me->enclosed_stbl = HTList_new(); HTList_addObject(me->enclosed_stbl, me->stbl); CTRACE((tfp, "endStblTABLE: postpone free %p\n", (void *) me->stbl)); } me->stbl = enclosing; } else { Stbl_free(me->stbl); me->stbl = NULL; } #else Stbl_free(me->stbl); me->stbl = NULL; #endif CTRACE((tfp, "endStblTABLE: have%s enclosing table (%p)\n", enclosing == 0 ? " NO" : "", (void *) enclosing)); return (BOOLEAN) (enclosing != 0); } /* Start simple table row */ void HText_startStblTR(HText *me, int alignment) { if (!me || !me->stbl) return; if (Stbl_addRowToTable(me->stbl, alignment, me->Lines) < 0) HText_cancelStbl(me); /* give up */ } /* Finish simple table row */ void HText_endStblTR(HText *me) { if (!me || !me->stbl) return; /* should this do something?? */ } /* Start simple table cell */ void HText_startStblTD(HText *me, int colspan, int rowspan, int alignment, int isheader) { if (!me || !me->stbl) return; if (colspan < 0) colspan = 1; if (colspan > TRST_MAXCOLSPAN) { CTRACE((tfp, "*** COLSPAN=%d is too large, ignored!\n", colspan)); colspan = 1; } if (rowspan > TRST_MAXROWSPAN) { CTRACE((tfp, "*** ROWSPAN=%d is too large, ignored!\n", rowspan)); rowspan = 1; } if (Stbl_addCellToTable(me->stbl, colspan, rowspan, alignment, isheader, me->Lines, HText_LastLineOffset(me), HText_LastLineSize(me, FALSE)) < 0) HText_cancelStbl(me); /* give up */ } /* Finish simple table cell */ void HText_endStblTD(HText *me) { if (!me || !me->stbl) return; if (Stbl_finishCellInTable(me->stbl, TRST_ENDCELL_ENDTD, me->Lines, HText_LastLineOffset(me), HText_LastLineSize(me, FALSE)) < 0) HText_cancelStbl(me); /* give up */ } /* Remember COL info / Start a COLGROUP and remember info */ void HText_startStblCOL(HText *me, int span, int alignment, int isgroup) { if (!me || !me->stbl) return; if (span <= 0) span = 1; if (span > TRST_MAXCOLSPAN) { CTRACE((tfp, "*** SPAN=%d is too large, ignored!\n", span)); span = 1; } if (Stbl_addColInfo(me->stbl, span, alignment, isgroup) < 0) HText_cancelStbl(me); /* give up */ } /* Finish a COLGROUP */ void HText_endStblCOLGROUP(HText *me) { if (!me || !me->stbl) return; if (Stbl_finishColGroup(me->stbl) < 0) HText_cancelStbl(me); /* give up */ } /* Start a THEAD / TFOOT / TBODY - remember its alignment info */ void HText_startStblRowGroup(HText *me, int alignment) { if (!me || !me->stbl) return; if (Stbl_addRowGroup(me->stbl, alignment) < 0) HText_cancelStbl(me); /* give up */ } static void compute_show_number(TextAnchor *a) { HTAnchor *cur, *tst; TextAnchor *b; int match; a->show_number = a->number; if (unique_urls && HTMainText != 0 && HTMainText->first_anchor != 0 && a->anchor != 0 && (cur = a->anchor->dest) != 0 && cur->parent != 0 && cur->parent->address != 0) { match = 0; for (b = HTMainText->first_anchor; b != a; b = b->next) { if (b->anchor != 0 && (tst = b->anchor->dest) != 0 && tst->parent != 0 && tst->parent->address != 0 && !strcmp(cur->parent->address, tst->parent->address) && !strcmp(NonNull(a->anchor->tag), NonNull(b->anchor->tag))) { match = b->show_number; break; } } if (match) a->show_number = match; else a->show_number = HTMainText->next_number++; } } /* Anchor handling * --------------- */ static void add_link_number(HText *text, TextAnchor *a, int save_position) { char marker[32]; /* * If we are doing link_numbering add the link number. */ if ((a->number > 0) #ifdef USE_PRETTYSRC && (text->source ? !psrcview_no_anchor_numbering : 1) #endif && links_are_numbered()) { char saved_lastchar = text->LastChar; int saved_linenum = text->Lines; HTAnchor *link_dest; char *link_text; compute_show_number(a); if (dump_links_inline && (link_dest = HTAnchor_followLink(a->anchor)) != 0 && (link_text = HTAnchor_address(link_dest)) != 0) { HText_appendText(text, "["); HText_appendText(text, link_text); HText_appendText(text, "]"); } else { sprintf(marker, "[%d]", a->show_number); HText_appendText(text, marker); } if (saved_linenum && text->Lines && saved_lastchar != ' ') text->LastChar = ']'; /* if marker not after space caused split */ if (save_position) { a->line_num = text->Lines; a->line_pos = (short) text->last_line->size; } } } /* Start an anchor field */ int HText_beginAnchor(HText *text, int underline, HTChildAnchor *anc) { TextAnchor *a; POOLtypecalloc(TextAnchor, a); if (a == NULL) outofmem(__FILE__, "HText_beginAnchor"); assert(a != NULL); a->inUnderline = (BOOLEAN) underline; a->sgml_offset = SGML_offset(); a->line_num = text->Lines; a->line_pos = (short) text->last_line->size; if (text->last_anchor) { text->last_anchor->next = a; } else { text->first_anchor = a; } a->next = 0; a->anchor = anc; a->extent = 0; a->link_type = HYPERTEXT_ANCHOR; text->last_anchor = a; if (track_internal_links && HTAnchor_followTypedLink(anc, HTInternalLink)) { a->number = ++(text->last_anchor_number); a->link_type = INTERNAL_LINK_ANCHOR; } else if (HTAnchor_followLink(anc)) { a->number = ++(text->last_anchor_number); } else { a->number = 0; } a->show_number = 0; if (number_links_on_left) add_link_number(text, a, TRUE); return (a->number); } /* If !really, report whether the anchor is empty. */ static BOOL HText_endAnchor0(HText *text, int number, int really) { TextAnchor *a; /* * The number argument is set to 0 in HTML.c and * LYCharUtils.c when we want to end the anchor * for the immediately preceding HText_beginAnchor() * call. If it's greater than 0, we want to handle * a particular anchor. This allows us to set links * for positions indicated by NAME or ID attributes, * without needing to close any anchor with an HREF * within which that link might be embedded. -FM */ if (number <= 0 || number == text->last_anchor->number) { a = text->last_anchor; } else { for (a = text->first_anchor; a; a = a->next) { if (a->number == number) { break; } } if (a == NULL) { /* * There's no anchor with that number, * so we'll default to the last anchor, * and cross our fingers. -FM */ a = text->last_anchor; } } CTRACE((tfp, "GridText:HText_endAnchor0: number:%d link_type:%d\n", a->number, a->link_type)); if (a->link_type == INPUT_ANCHOR) { /* * Shouldn't happen, but put test here anyway to be safe. - LE */ CTRACE((tfp, "BUG: HText_endAnchor0: internal error: last anchor was input field!\n")); return FALSE; } if (a->number) { /* * If it goes somewhere... */ int i, j, k, l; BOOL remove_numbers_on_empty = (BOOL) ((links_are_numbered() && ((text->hiddenlinkflag != HIDDENLINKS_MERGE) || (LYNoISMAPifUSEMAP && !(text->node_anchor && text->node_anchor->bookmark) && HTAnchor_isISMAPScript (HTAnchor_followLink(a->anchor)))))); HTLine *last = text->last_line; HTLine *prev = text->last_line->prev; HTLine *start = last; int CurBlankExtent = 0; int BlankExtent = 0; int extent_adjust = 0; /* Find the length taken by the anchor */ l = text->Lines; /* lineno of last */ /* the last line of an anchor may contain a trailing blank, * which will be trimmed later. Discount it from the extent. */ if (l > a->line_num) { for (i = start->size; i > 0; --i) { if (isspace(UCH(start->data[i - 1]))) { --extent_adjust; } else { break; } } } while (l > a->line_num) { extent_adjust += start->size; start = start->prev; l--; } /* Now start is the start line of the anchor */ extent_adjust += start->size - a->line_pos; start = last; /* Used later */ /* * Check if the anchor content has only * white and special characters, starting * with the content on the last line. -FM */ a->extent = (short) (a->extent + extent_adjust); if (a->extent > (int) last->size) { /* * The anchor extends over more than one line, * so set up to check the entire last line. -FM */ i = last->size; } else { /* * The anchor is restricted to the last line, * so check from the start of the anchor. -FM */ i = a->extent; } k = j = (last->size - i); while (j < (int) last->size) { if (!IsSpecialAttrChar(last->data[j]) && !isspace(UCH(last->data[j])) && last->data[j] != HT_NON_BREAK_SPACE && last->data[j] != HT_EN_SPACE) break; i--; j++; } if (i == 0) { if (a->extent > (int) last->size) { /* * The anchor starts on a preceding line, and * the last line has only white and special * characters, so declare the entire extent * of the last line as blank. -FM */ CurBlankExtent = BlankExtent = last->size; } else { /* * The anchor starts on the last line, and * has only white or special characters, so * declare the anchor's extent as blank. -FM */ CurBlankExtent = BlankExtent = a->extent; } } /* * While the anchor starts on a line preceding * the one we just checked, and the one we just * checked has only white and special characters, * check whether the anchor's content on the * immediately preceding line also has only * white and special characters. -FM */ while (i == 0 && (a->extent > CurBlankExtent || (a->extent == CurBlankExtent && k == 0 && prev != text->last_line && (prev->size == 0 || prev->data[prev->size - 1] == ']')))) { start = prev; k = j = prev->size - a->extent + CurBlankExtent; if (j < 0) { /* * The anchor starts on a preceding line, * so check all of this line. -FM */ j = 0; i = prev->size; } else { /* * The anchor starts on this line. -FM */ i = a->extent - CurBlankExtent; } while (j < (int) prev->size) { if (!IsSpecialAttrChar(prev->data[j]) && !isspace(UCH(prev->data[j])) && prev->data[j] != HT_NON_BREAK_SPACE && prev->data[j] != HT_EN_SPACE) break; i--; j++; } if (i == 0) { if (a->extent > (CurBlankExtent + (int) prev->size) || (a->extent == CurBlankExtent + (int) prev->size && k == 0 && prev->prev != text->last_line && (prev->prev->size == 0 || prev->prev->data[prev->prev->size - 1] == ']'))) { /* * This line has only white and special * characters, so treat its entire extent * as blank, and decrement the pointer for * the line to be analyzed. -FM */ CurBlankExtent += prev->size; BlankExtent = CurBlankExtent; prev = prev->prev; } else { /* * The anchor starts on this line, and it * has only white or special characters, so * declare the anchor's extent as blank. -FM */ BlankExtent = a->extent; break; } } } if (!really) { /* Just report whether it is empty */ a->extent = (short) (a->extent - extent_adjust); return (BOOL) (i == 0); } if (i == 0) { /* * It's an invisible anchor probably from an ALT="" * or an ignored ISMAP attribute due to a companion * USEMAP. -FM */ a->show_anchor = NO; CTRACE((tfp, "HText_endAnchor0: hidden (line,pos,ext,BlankExtent):(%d,%d,%d,%d)", a->line_num, a->line_pos, a->extent, BlankExtent)); /* * If links are numbered, then try to get rid of the * numbered bracket and adjust the anchor count. -FM * * Well, let's do this only if -hiddenlinks=merged is not in * effect, or if we can be reasonably sure that * this is the result of an intentional non-generation of * anchor text via NO_ISMAP_IF_USEMAP. In other cases it can * actually be a feature that numbered links alert the viewer * to the presence of a link which is otherwise not selectable - * possibly caused by HTML errors. - kw */ if (remove_numbers_on_empty) { int NumSize = 0; TextAnchor *anc; /* * Set start->data[j] to the close-square-bracket, * or to the beginning of the line on which the * anchor start. -FM */ if (start == last) { /* * The anchor starts on the last line. -FM */ j = (last->size - a->extent - 1); } else { /* * The anchor starts on a previous line. -FM */ prev = start->prev; j = (start->size - a->extent + CurBlankExtent - 1); } if (j < 0) j = 0; i = j; /* * If start->data[j] is a close-square-bracket, verify * that it's the end of the numbered bracket, and if so, * strip the numbered bracket. If start->data[j] is not * a close-square-bracket, check whether we had a wrap * and the close-square-bracket is at the end of the * previous line. If so, strip the numbered bracket * from that line. -FM */ if (start->data[j] == ']') { j--; NumSize++; while (j >= 0 && isdigit(UCH(start->data[j]))) { j--; NumSize++; } while (j < 0) { j++; NumSize--; } if (start->data[j] == '[') { /* * The numbered bracket is entirely * on this line. -FM */ NumSize++; if (start == last && (int) text->permissible_split > j) { if ((int) text->permissible_split - NumSize < j) text->permissible_split = (unsigned) j; else text->permissible_split -= (unsigned) NumSize; } k = j + NumSize; while (k < (int) start->size) start->data[j++] = start->data[k++]; for (anc = a; anc; anc = anc->next) { if (anc->line_num == a->line_num && anc->line_pos >= NumSize) { anc->line_pos = (short) (anc->line_pos - NumSize); } } start->size = (unsigned short) j; start->data[j++] = '\0'; while (j < k) start->data[j++] = '\0'; } else if (prev && prev->size > 1) { k = (i + 1); j = (prev->size - 1); while ((j >= 0) && IsSpecialAttrChar(prev->data[j])) j--; i = (j + 1); while (j >= 0 && isdigit(UCH(prev->data[j]))) { j--; NumSize++; } while (j < 0) { j++; NumSize--; } if (prev->data[j] == '[') { /* * The numbered bracket started on the * previous line, and part of it was * wrapped to this line. -FM */ while (i < (int) prev->size) prev->data[j++] = prev->data[i++]; prev->size = (unsigned short) j; prev->data[j] = '\0'; while (j < i) prev->data[j++] = '\0'; if (start == last && text->permissible_split > 0) { if ((int) text->permissible_split < k) text->permissible_split = 0; else text->permissible_split -= (unsigned) k; } j = 0; i = k; while (k < (int) start->size) start->data[j++] = start->data[k++]; for (anc = a; anc; anc = anc->next) { if (anc->line_num == a->line_num && anc->line_pos >= i) { anc->line_pos = (short) (anc->line_pos - i); } } start->size = (unsigned short) j; start->data[j++] = '\0'; while (j < k) start->data[j++] = '\0'; } else { /* * Shucks! We didn't find the * numbered bracket. -FM */ a->show_anchor = YES; } } else { /* * Shucks! We didn't find the * numbered bracket. -FM */ a->show_anchor = YES; } } else if (prev && prev->size > 2) { j = (prev->size - 1); while ((j >= 0) && IsSpecialAttrChar(prev->data[j])) j--; if (j < 0) j = 0; if ((j >= 2) && (prev->data[j] == ']' && isdigit(UCH(prev->data[j - 1])))) { j--; NumSize++; while (j >= 0 && isdigit(UCH(prev->data[j]))) { j--; NumSize++; } while (j < 0) { j++; NumSize--; } if (prev->data[j] == '[') { /* * The numbered bracket is all on the * previous line, and the anchor content * was wrapped to the last line. -FM */ NumSize++; k = j + NumSize; while (k < (int) prev->size) prev->data[j++] = prev->data[k++]; prev->size = (unsigned short) j; prev->data[j++] = '\0'; while (j < k) prev->data[j++] = '\0'; } else { /* * Shucks! We didn't find the * numbered bracket. -FM */ a->show_anchor = YES; } } else { /* * Shucks! We didn't find the * numbered bracket. -FM */ a->show_anchor = YES; } } else { /* * Shucks! We didn't find the * numbered bracket. -FM */ a->show_anchor = YES; } } } else { if (!number_links_on_left) add_link_number(text, a, FALSE); /* * The anchor's content is not restricted to only * white and special characters, so we'll show it * as a link. -FM */ a->show_anchor = YES; if (BlankExtent) { CTRACE((tfp, "HText_endAnchor0: blanks (line,pos,ext,BlankExtent):(%d,%d,%d,%d)", a->line_num, a->line_pos, a->extent, BlankExtent)); } } if (a->show_anchor == NO) { /* * The anchor's content is restricted to white * and special characters, so set its number * and extent to zero, decrement the visible * anchor number counter, and add this anchor * to the hidden links list. -FM */ a->extent = 0; if (text->hiddenlinkflag != HIDDENLINKS_MERGE) { a->number = 0; text->last_anchor_number--; HText_AddHiddenLink(text, a); } } else { /* * The anchor's content is not restricted to white * and special characters, so we'll display the * content, but shorten its extent by any trailing * blank lines we've detected. -FM */ a->extent = (short) (a->extent - ((BlankExtent < a->extent) ? BlankExtent : 0)); } if (BlankExtent || a->extent <= 0 || a->number <= 0) { CTRACE((tfp, "->[%d](%d,%d,%d,%d)\n", a->number, a->line_num, a->line_pos, a->extent, BlankExtent)); } } else { if (!really) /* Just report whether it is empty */ return FALSE; /* * It's a named anchor without an HREF, so it * should be registered but not shown as a * link. -FM */ a->show_anchor = NO; a->extent = 0; } return FALSE; } void HText_endAnchor(HText *text, int number) { HText_endAnchor0(text, number, 1); } /* This returns whether the given anchor has blank content. Shamelessly copied from HText_endAnchor. The values returned are meaningful only for "normal" links - like ones produced by foo, no inputs, etc. - VH */ #ifdef MARK_HIDDEN_LINKS BOOL HText_isAnchorBlank(HText *text, int number) { return HText_endAnchor0(text, number, 0); } #endif /* MARK_HIDDEN_LINKS */ void HText_appendText(HText *text, const char *str) { const char *p; if (str != NULL && text != NULL && text->halted != 3) { for (p = str; *p; p++) { HText_appendCharacter(text, *p); } } } static int remove_special_attr_chars(char *buf) { register char *cp; register int soft_newline_count = 0; for (cp = buf; *cp != '\0'; cp++) { /* * Don't print underline chars. */ soft_newline_count += (*cp == LY_SOFT_NEWLINE); if (!IsSpecialAttrChar(*cp)) { *buf++ = *cp; } } *buf = '\0'; return soft_newline_count; } /* * This function trims blank lines from the end of the document, and * then gets the hightext from the text by finding the char position, * and brings the anchors in line with the text by adding the text * offset to each of the anchors. */ void HText_endAppend(HText *text) { HTLine *line_ptr; if (!text) return; CTRACE((tfp, "GridText: Entering HText_endAppend\n")); /* * Create a blank line at the bottom. */ new_line(text); if (text->halted) { if (text->stbl) HText_cancelStbl(text); /* * If output was stopped because memory was low, and we made * it to the end of the document, reset those flags and hope * things are better now. - kw */ LYFakeZap(NO); text->halted = 0; } else if (text->stbl) { /* * Could happen if TABLE end tag was missing. * Alternatively we could cancel in this case. - kw */ HText_endStblTABLE(text); } /* * Get the first line. */ if ((line_ptr = FirstHTLine(text)) != 0) { /* * Remove the blank lines at the end of document. */ while (text->last_line->data[0] == '\0' && text->Lines > 2) { HTLine *next_to_the_last_line = text->last_line->prev; CTRACE((tfp, "GridText: Removing bottom blank line: `%s'\n", text->last_line->data)); /* * line_ptr points to the first line. */ next_to_the_last_line->next = line_ptr; line_ptr->prev = next_to_the_last_line; freeHTLine(text, text->last_line); text->last_line = next_to_the_last_line; text->Lines--; CTRACE((tfp, "GridText: New bottom line: `%s'\n", text->last_line->data)); } } /* * Fix up the anchor structure values and * create the hightext strings. -FM */ HText_trimHightext(text, TRUE, -1); } /* * This function gets the hightext from the text by finding the char * position, and brings the anchors in line with the text by adding the text * offset to each of the anchors. * * `Forms input' fields cannot be displayed properly without this function * to be invoked (detected in display_partial mode). * * If final is set, this is the final fixup; if not set, we don't have * to do everything because there should be another call later. * * BEFORE this function has treated a TextAnchor, its line_pos and * extent fields are counting bytes in the HTLine data, including * invisible special attribute chars and counting UTF-8 multibyte * characters as multiple bytes. * * AFTER the adjustment, the anchor line_pos (and hightext offset if * applicable) fields indicate x positions in terms of displayed character * cells, and the extent field apparently is unimportant; the anchor text has * been copied to the hightext fields (which should have been NULL up to that * point), with special attribute chars removed. * * This needs to be done so that display_page finds the anchors in the * form it expects when it sets the links[] elements. */ static void HText_trimHightext(HText *text, int final, int stop_before) { int cur_line, cur_shift; TextAnchor *anchor_ptr; TextAnchor *prev_a = NULL; HTLine *line_ptr; HTLine *line_ptr2; unsigned char ch; char *hilite_str; int hilite_len; int actual_len; int count_line; if (!text) return; if (final) { CTRACE((tfp, "GridText: Entering HText_trimHightext (final)\n")); } else { if (stop_before < 0 || stop_before > text->Lines) stop_before = text->Lines; CTRACE((tfp, "GridText: Entering HText_trimHightext (partial: 0..%d/%d)\n", stop_before, text->Lines)); } /* * Get the first line. */ line_ptr = FirstHTLine(text); cur_line = 0; /* * Fix up the anchor structure values and * create the hightext strings. -FM */ for (anchor_ptr = text->first_anchor; anchor_ptr != NULL; prev_a = anchor_ptr, anchor_ptr = anchor_ptr->next) { int anchor_col; re_parse: /* * Find the right line. */ for (; anchor_ptr->line_num > cur_line; line_ptr = line_ptr->next, cur_line++) { ; /* null body */ } if (!final) { /* * If this is not the final call, stop when we have reached * the last line, or the very end of preceding line. * The last line is probably still not finished. - kw */ if (cur_line >= stop_before) break; if (anchor_ptr->line_num >= text->Lines - 1 && anchor_ptr->line_pos >= (int) text->last_line->prev->size) break; /* * Also skip this anchor if it looks like HText_endAnchor * is not yet done with it. - kw */ if (!anchor_ptr->extent && anchor_ptr->number && (anchor_ptr->link_type & HYPERTEXT_ANCHOR) && !anchor_ptr->show_anchor && anchor_ptr->number == text->last_anchor_number) continue; } /* * If hightext has already been set, then we must have already * done the trimming & adjusting for this anchor, so avoid * doing it a second time. - kw */ if (LYGetHiTextStr(anchor_ptr, 0) != NULL) continue; if (anchor_ptr->line_pos > (int) line_ptr->size) { anchor_ptr->line_pos = (short) line_ptr->size; } if (anchor_ptr->line_pos < 0) { anchor_ptr->line_pos = 0; anchor_ptr->line_num = cur_line; } CTRACE((tfp, "GridText: Anchor found on line:%d col:%d [%05d:%d] ext:%d\n", cur_line, anchor_ptr->line_pos, anchor_ptr->sgml_offset, anchor_ptr->number, anchor_ptr->extent)); cur_shift = 0; /* * Strip off any spaces or SpecialAttrChars at the beginning, * if they exist, but only on HYPERTEXT_ANCHORS. */ if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) { ch = UCH(line_ptr->data[anchor_ptr->line_pos]); while (isspace(ch) || IsSpecialAttrChar(ch)) { anchor_ptr->line_pos++; anchor_ptr->extent--; cur_shift++; ch = UCH(line_ptr->data[anchor_ptr->line_pos]); } } if (anchor_ptr->extent < 0) { anchor_ptr->extent = 0; } CTRACE((tfp, "anchor text: '%s'\n", line_ptr->data)); /* * If the link begins with an end of line and we have more lines, then * start the highlighting on the next line. -FM. * * But if an empty anchor is at the end of line and empty, keep it * where it is, unless the previous anchor in the list (if any) already * starts later. - kw */ if ((unsigned) anchor_ptr->line_pos >= strlen(line_ptr->data)) { if (cur_line < text->Lines && (anchor_ptr->extent || anchor_ptr->line_pos != (int) line_ptr->size || (prev_a && /* How could this happen? */ (prev_a->line_num > anchor_ptr->line_num)))) { anchor_ptr->line_num++; anchor_ptr->line_pos = 0; CTRACE((tfp, "found anchor at end of line\n")); goto re_parse; } else { CTRACE((tfp, "found anchor at end of line, leaving it there\n")); } } /* * Copy the link name into the data structure. */ if (anchor_ptr->extent > 0 && anchor_ptr->line_pos >= 0) { int size = (int) line_ptr->size - anchor_ptr->line_pos; if (size > anchor_ptr->extent) size = anchor_ptr->extent; LYClearHiText(anchor_ptr); LYSetHiText(anchor_ptr, &line_ptr->data[anchor_ptr->line_pos], (unsigned) size); } else { LYClearHiText(anchor_ptr); LYSetHiText(anchor_ptr, "", 0); } /* * If the anchor extends over more than one line, copy that into the * data structure. */ hilite_str = LYGetHiTextStr(anchor_ptr, 0); hilite_len = (int) strlen(hilite_str); actual_len = anchor_ptr->extent; line_ptr2 = line_ptr; assert(line_ptr2 != 0); count_line = cur_line; while (actual_len > hilite_len) { HTLine *old_line_ptr2 = line_ptr2; count_line++; if ((line_ptr2 = line_ptr2->next) == NULL) break; if (!final && count_line >= stop_before) { LYClearHiText(anchor_ptr); break; } else if (old_line_ptr2 == text->last_line) { break; } /* * Double check that we have a line pointer, and if so, copy into * highlight text. */ if (line_ptr2) { char *hi_string = NULL; int hi_offset = line_ptr2->offset; StrnAllocCopy(hi_string, line_ptr2->data, (size_t) (actual_len - hilite_len)); actual_len -= (int) strlen(hi_string); /*handle LY_SOFT_NEWLINEs -VH */ hi_offset += remove_special_attr_chars(hi_string); if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) { LYTrimTrailing(hi_string); } if (non_empty(hi_string)) { LYAddHiText(anchor_ptr, hi_string, hi_offset); } else if (actual_len > hilite_len) { LYAddHiText(anchor_ptr, "", hi_offset); } FREE(hi_string); } } if (!final && count_line >= stop_before) { break; } hilite_str = LYGetHiTextStr(anchor_ptr, 0); remove_special_attr_chars(hilite_str); if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) { LYTrimTrailing(hilite_str); } /* * Save the offset (bytes) of the anchor in the line's data. */ anchor_col = anchor_ptr->line_pos; /* * Subtract any formatting characters from the x position of the link. */ #ifdef WIDEC_CURSES if (anchor_ptr->line_pos > 0) { /* * LYstrExtent filters out the formatting characters, so we do not * have to count them here, except for soft newlines. */ anchor_ptr->line_pos = (short) LYstrExtent2(line_ptr->data, anchor_col); if (line_ptr->data[0] == LY_SOFT_NEWLINE) anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + 1); } #else /* 8-bit curses, etc. */ if (anchor_ptr->line_pos > 0) { register int offset = 0, i = 0; int have_soft_newline_in_1st_line = 0; for (; i < anchor_col; i++) { if (IS_UTF_EXTRA(line_ptr->data[i]) || IsSpecialAttrChar(line_ptr->data[i])) { offset++; have_soft_newline_in_1st_line += (line_ptr->data[i] == LY_SOFT_NEWLINE); } } anchor_ptr->line_pos = (short) (anchor_ptr->line_pos - offset); /*handle LY_SOFT_NEWLINEs -VH */ anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + have_soft_newline_in_1st_line); } #endif /* WIDEC_CURSES */ /* * Set the line number. */ anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + line_ptr->offset); anchor_ptr->line_num = cur_line; CTRACE((tfp, "GridText: add link on line %d col %d [%d] %s\n", cur_line, anchor_ptr->line_pos, anchor_ptr->number, "in HText_trimHightext")); } } /* Return the anchor associated with this node */ HTParentAnchor *HText_nodeAnchor(HText *text) { return text->node_anchor; } /* GridText specials * ================= */ /* * HText_childNextNumber() returns the anchor with index [number], * using a pointer from the previous number (=optimization) or NULL. */ HTChildAnchor *HText_childNextNumber(int number, void **prev) { /* Sorry, TextAnchor is not declared outside this file, use a cast. */ TextAnchor *a = (TextAnchor *) *prev; if (!HTMainText || number <= 0) return (HTChildAnchor *) 0; /* Fail */ if (number == 1 || !a) a = HTMainText->first_anchor; /* a strange thing: positive a->number's are sorted, * and between them several a->number's may be 0 -- skip them */ for (; a && a->number != number; a = a->next) ; if (!a) return (HTChildAnchor *) 0; /* Fail */ *prev = (void *) a; return a->anchor; } /* * For the -unique-urls option, find the anchor-number of the first occurrence * of a given address. */ int HText_findAnchorNumber(void *avoid) { TextAnchor *a = (TextAnchor *) avoid; if (a->number > 0 && a->show_number == 0) compute_show_number(a); return a->show_number; } static const char *inputFieldDesc(FormInfo * input) { const char *result = 0; switch (input->type) { case F_TEXT_TYPE: result = gettext("text entry field"); break; case F_PASSWORD_TYPE: result = gettext("password entry field"); break; case F_CHECKBOX_TYPE: result = gettext("checkbox"); break; case F_RADIO_TYPE: result = gettext("radio button"); break; case F_SUBMIT_TYPE: result = gettext("submit button"); break; case F_RESET_TYPE: result = gettext("reset button"); break; case F_BUTTON_TYPE: result = gettext("script button"); break; case F_OPTION_LIST_TYPE: result = gettext("popup menu"); break; case F_HIDDEN_TYPE: result = gettext("hidden form field"); break; case F_TEXTAREA_TYPE: result = gettext("text entry area"); break; case F_RANGE_TYPE: result = gettext("range entry field"); break; case F_FILE_TYPE: result = gettext("file entry field"); break; case F_TEXT_SUBMIT_TYPE: result = gettext("text-submit field"); break; case F_IMAGE_SUBMIT_TYPE: result = gettext("image-submit button"); break; case F_KEYGEN_TYPE: result = gettext("keygen field"); break; default: result = gettext("unknown form field"); break; } return result; } /* * HText_FormDescNumber() returns a description of the form field * with index N. The index corresponds to the [number] we print * for the field. -FM & LE */ void HText_FormDescNumber(int number, const char **desc) { TextAnchor *a; if (!desc) return; if (!(HTMainText && HTMainText->first_anchor) || number <= 0) { *desc = gettext("unknown field or link"); return; } for (a = HTMainText->first_anchor; a; a = a->next) { if (a->number == number) { if (!(a->input_field && a->input_field->type)) { *desc = gettext("unknown field or link"); return; } break; } } if (a != NULL) *desc = inputFieldDesc(a->input_field); } /* HTGetRelLinkNum returns the anchor number to which follow_link_number() * is to jump (input was 123+ or 123- or 123+g or 123-g or 123 or 123g) * num is the number specified * rel is 0 or '+' or '-' * cur is the current link */ int HTGetRelLinkNum(int num, int rel, int cur) { TextAnchor *a, *l = 0; int scrtop = HText_getTopOfScreen(); /*XXX +1? */ int curline = links[cur].anchor_line_num; int curpos = links[cur].lx; int on_screen = (curline >= scrtop && curline < (scrtop + display_lines)); /* curanchor may or may not be the "current link", depending whether it's * on the current screen */ int curanchor = links[cur].anchor_number; CTRACE((tfp, "HTGetRelLinkNum(%d,%d,%d) -- HTMainText=%p\n", num, rel, cur, (void *) HTMainText)); CTRACE((tfp, " scrtop=%d, curline=%d, curanchor=%d, display_lines=%d, %s\n", scrtop, curline, curanchor, display_lines, on_screen ? "on_screen" : "0")); if (!HTMainText) return 0; if (rel == 0) return num; /* if cur numbered link is on current page, use it */ if (on_screen && curanchor) { CTRACE((tfp, "curanchor=%d at line %d on screen\n", curanchor, curline)); if (rel == '+') return curanchor + num; else if (rel == '-') return curanchor - num; else return num; /* shouldn't happen */ } /* no current link on screen, or current link is not numbered * -- find previous closest numbered link */ for (a = HTMainText->first_anchor; a; a = a->next) { CTRACE((tfp, " a->line_num=%d, a->number=%d\n", a->line_num, a->number)); if (a->line_num >= scrtop) break; if (a->number == 0) continue; l = a; curanchor = l->number; } CTRACE((tfp, " a=%p, l=%p, curanchor=%d\n", (void *) a, (void *) l, curanchor)); if (on_screen) { /* on screen but not a numbered link */ for (; a; a = a->next) { if (a->number) { l = a; curanchor = l->number; } if (curline == a->line_num && curpos == a->line_pos) break; } } if (rel == '+') { return curanchor + num; } else if (rel == '-') { if (l) return curanchor + 1 - num; else { for (; a && a->number == 0; a = a->next) ; return a ? a->number - num : 0; } } else return num; /* shouldn't happen */ } /* * HTGetLinkInfo returns some link info based on the number. * * If want_go is not 0, caller requests to know a line number for * the link indicated by number. It will be returned in *go_line, and * *linknum will be set to an index into the links[] array, to use after * the line in *go_line has been made the new top screen line. * *hightext and *lname are unchanged. - KW * * If want_go is 0 and the number doesn't represent an input field, info * on the link indicated by number is deposited in *hightext and *lname. */ int HTGetLinkInfo(int number, int want_go, int *go_line, int *linknum, char **hightext, char **lname) { TextAnchor *a; HTAnchor *link_dest; HTAnchor *link_dest_intl = NULL; int anchors_this_line = 0, anchors_this_screen = 0; int prev_anchor_line = -1, prev_prev_anchor_line = -1; if (!HTMainText) return (NO); for (a = HTMainText->first_anchor; a; a = a->next) { /* * Count anchors, first on current line if there is more * than one. We have to count all links, including form * field anchors and others with a->number == 0, because * they are or will be included in the links[] array. * The exceptions are hidden form fields and anchors with * show_anchor not set, because they won't appear in links[] * and don't count towards nlinks. - KW */ if ((a->show_anchor) && !(a->link_type == INPUT_ANCHOR && a->input_field->type == F_HIDDEN_TYPE)) { if (a->line_num == prev_anchor_line) { anchors_this_line++; } else { /* * This anchor is on a different line than the previous one. * Remember which was the line number of the previous anchor, * for use in screen positioning later. - KW */ anchors_this_line = 1; prev_prev_anchor_line = prev_anchor_line; prev_anchor_line = a->line_num; } if (a->line_num >= HTMainText->top_of_screen) { /* * Count all anchors starting with the top line of the * currently displayed screen. Just keep on counting * beyond this screen's bottom line - we'll know whether * a found anchor is below the current screen by a check * against nlinks later. - KW */ anchors_this_screen++; } } if (a->number == number) { /* * We found it. Now process it, depending * on what kind of info is requested. - KW */ if (want_go || a->link_type == INPUT_ANCHOR) { if (a->show_anchor == NO) { /* * The number requested has been assigned to an anchor * without any selectable text, so we cannot position * on it. The code for suppressing such anchors in * HText_endAnchor() may not have applied, or it may * have failed. Return a failure indication so that * the user will notice that something is wrong, * instead of positioning on some other anchor which * might result in inadvertent activation. - KW */ return (NO); } if (anchors_this_screen > 0 && anchors_this_screen <= nlinks && a->line_num >= HTMainText->top_of_screen && a->line_num < HTMainText->top_of_screen + (display_lines)) { /* * If the requested anchor is within the current screen, * just set *go_line so that the screen window won't move * (keep it as it is), and set *linknum to the index of * this link in the current links[] array. - KW */ *go_line = HTMainText->top_of_screen; if (linknum) *linknum = anchors_this_screen - 1; } else { /* * if the requested anchor is not within the currently * displayed screen, set *go_line such that the top line * will be either * (1) the line immediately below the previous * anchor, or * (2) about one third of a screenful above the line * with the target, or * (3) the first line of the document - * whichever comes last. In all cases the line with our * target will end up being the first line with any links * on the new screen, so that we can use the * anchors_this_line counter to point to the anchor in * the new links[] array. - kw */ int max_offset = SEARCH_GOAL_LINE - 1; if (max_offset < 0) max_offset = 0; else if (max_offset >= display_lines) max_offset = display_lines - 1; *go_line = prev_anchor_line - max_offset; if (*go_line <= prev_prev_anchor_line) *go_line = prev_prev_anchor_line + 1; if (*go_line < 0) *go_line = 0; if (linknum) *linknum = anchors_this_line - 1; } return (LINK_LINE_FOUND); } else { *hightext = LYGetHiTextStr(a, 0); link_dest = HTAnchor_followLink(a->anchor); { char *cp_freeme = NULL; if (traversal) { cp_freeme = stub_HTAnchor_address(link_dest); } else if (track_internal_links) { if (a->link_type == INTERNAL_LINK_ANCHOR) { link_dest_intl = HTAnchor_followTypedLink(a->anchor, HTInternalLink); if (link_dest_intl && link_dest_intl != link_dest) { CTRACE((tfp, "HTGetLinkInfo: unexpected typed link to %s!\n", link_dest_intl->parent->address)); link_dest_intl = NULL; } } if (link_dest_intl) { char *cp2 = HTAnchor_address(link_dest_intl); FREE(*lname); *lname = cp2; return (WWW_INTERN_LINK_TYPE); } else { cp_freeme = HTAnchor_address(link_dest); } } else { cp_freeme = HTAnchor_address(link_dest); } StrAllocCopy(*lname, cp_freeme); FREE(cp_freeme); } return (WWW_LINK_TYPE); } } } return (NO); } static BOOLEAN same_anchor_or_field(int numberA, FormInfo * formA, int numberB, FormInfo * formB, int ta_same) { if (numberA > 0 || numberB > 0) { if (numberA == numberB) return (YES); else if (!ta_same) return (NO); } if (formA || formB) { if (formA == formB) { return (YES); } else if (!ta_same) { return (NO); } else if (!(formA && formB)) { return (NO); } } else { return (NO); } if (formA->type != formB->type || formA->type != F_TEXTAREA_TYPE || formB->type != F_TEXTAREA_TYPE) { return (NO); } if (formA->number != formB->number) return (NO); if (!formA->name || !formB->name) return (YES); return (BOOL) (strcmp(formA->name, formB->name) == 0); } #define same_anchor_as_link(i,a,ta_same) (BOOL) (i >= 0 && a && \ same_anchor_or_field(links[i].anchor_number,\ (links[i].type == WWW_FORM_LINK_TYPE) ? links[i].l_form : NULL,\ a->number,\ (a->link_type == INPUT_ANCHOR) ? a->input_field : NULL,\ ta_same)) #define same_anchors(a1,a2,ta_same) (BOOL) (a1 && a2 && \ same_anchor_or_field(a1->number,\ (a1->link_type == INPUT_ANCHOR) ? a1->input_field : NULL,\ a2->number,\ (a2->link_type == INPUT_ANCHOR) ? a2->input_field : NULL,\ ta_same)) /* * Are there more textarea lines belonging to the same textarea before * (direction < 0) or after (direction > 0) the current one? * On entry, curlink must be the index in links[] of a textarea field. - kw */ BOOL HText_TAHasMoreLines(int curlink, int direction) { TextAnchor *a; TextAnchor *prev_a = NULL; if (!HTMainText) return (NO); if (direction < 0) { for (a = HTMainText->first_anchor; a; prev_a = a, a = a->next) { if (a->link_type == INPUT_ANCHOR && links[curlink].l_form == a->input_field) { return same_anchors(a, prev_a, TRUE); } if (links[curlink].anchor_number && a->number >= links[curlink].anchor_number) break; } return NO; } else { for (a = HTMainText->first_anchor; a; a = a->next) { if (a->link_type == INPUT_ANCHOR && links[curlink].l_form == a->input_field) { return same_anchors(a, a->next, TRUE); } if (links[curlink].anchor_number && a->number >= links[curlink].anchor_number) break; } return NO; } } /* * HTGetLinkOrFieldStart - moving to previous or next link or form field. * * On input, * curlink: current link, as index in links[] array (-1 if none) * direction: whether to move up or down (or stay where we are) * ta_skip: if FALSE, input fields belonging to the same textarea are * are treated as different fields, as usual; * if TRUE, fields of the same textarea are treated as a * group for skipping. * The caller wants information for positioning on the new link to be * deposited in *go_line and (if linknum is not NULL) *linknum. * * On failure (no more links in the requested direction) returns NO * and doesn't change *go_line or *linknum. Otherwise, LINK_DO_ARROWUP * may be returned, and *go_line and *linknum not changed, to indicate that * the caller should use a normal PREV_LINK or PREV_PAGE mechanism. * Otherwise: * The number (0-based counting) for the new top screen line will be returned * in *go_line, and *linknum will be set to an index into the links[] array, * to use after the line in *go_line has been made the new top screen * line. - kw */ int HTGetLinkOrFieldStart(int curlink, int *go_line, int *linknum, int direction, int ta_skip) { TextAnchor *a; int anchors_this_line = 0; int prev_anchor_line = -1, prev_prev_anchor_line = -1; struct agroup { TextAnchor *anc; int prev_anchor_line; int anchors_this_line; int anchors_this_group; } previous, current; struct agroup *group_to_go = NULL; if (!HTMainText) return (NO); previous.anc = current.anc = NULL; previous.prev_anchor_line = current.prev_anchor_line = -1; previous.anchors_this_line = current.anchors_this_line = 0; previous.anchors_this_group = current.anchors_this_group = 0; for (a = HTMainText->first_anchor; a; a = a->next) { /* * Count anchors, first on current line if there is more * than one. We have to count all links, including form * field anchors and others with a->number == 0, because * they are or will be included in the links[] array. * The exceptions are hidden form fields and anchors with * show_anchor not set, because they won't appear in links[] * and don't count towards nlinks. - KW */ if ((a->show_anchor) && !(a->link_type == INPUT_ANCHOR && a->input_field->type == F_HIDDEN_TYPE)) { if (a->line_num == prev_anchor_line) { anchors_this_line++; } else { /* * This anchor is on a different line than the previous one. * Remember which was the line number of the previous anchor, * for use in screen positioning later. - KW */ anchors_this_line = 1; prev_prev_anchor_line = prev_anchor_line; prev_anchor_line = a->line_num; } if (!same_anchors(current.anc, a, ta_skip)) { previous.anc = current.anc; previous.prev_anchor_line = current.prev_anchor_line; previous.anchors_this_line = current.anchors_this_line; previous.anchors_this_group = current.anchors_this_group; current.anc = a; current.prev_anchor_line = prev_prev_anchor_line; current.anchors_this_line = anchors_this_line; current.anchors_this_group = 1; } else { current.anchors_this_group++; } if (curlink >= 0) { if (same_anchor_as_link(curlink, a, ta_skip)) { if (direction == -1) { group_to_go = &previous; break; } else if (direction == 0) { group_to_go = ¤t; break; } } else if (direction > 0 && same_anchor_as_link(curlink, previous.anc, ta_skip)) { group_to_go = ¤t; break; } } else { if (a->line_num >= HTMainText->top_of_screen) { if (direction < 0) { group_to_go = &previous; break; } else if (direction == 0) { if (previous.anc) { group_to_go = &previous; break; } else { group_to_go = ¤t; break; } } else { group_to_go = ¤t; break; } } } } } if (!group_to_go && curlink < 0 && direction <= 0) { group_to_go = ¤t; } if (group_to_go) { a = group_to_go->anc; if (a) { int max_offset; /* * We know where to go; most of the stuff below is just * tweaks to try to position the new screen in a specific * way. * * In some cases going to a previous link can be done * via the normal LYK_PREV_LINK action, which may give * better positioning of the new screen. - kw */ if (a->line_num < HTMainText->top_of_screen && a->line_num >= HTMainText->top_of_screen - (display_lines)) { if ((curlink < 0 && group_to_go->anchors_this_group == 1) || (direction < 0 && group_to_go != ¤t && current.anc && current.anc->line_num >= HTMainText->top_of_screen && group_to_go->anchors_this_group == 1) || (a->next && a->next->line_num >= HTMainText->top_of_screen)) { return (LINK_DO_ARROWUP); } } /* * The fundamental limitation of the current anchors_this_line * counter method is that we only can set *linknum to the right * index into the future links[] array if the line with our link * ends up being the first line with any links (that count) on * the new screen. Subject to that restriction we still have * some vertical liberty (sometimes), and try to make the best * of it. It may be a question of taste though. - kw */ if (a->line_num <= (display_lines)) { max_offset = 0; } else if (a->line_num < HTMainText->top_of_screen) { int screensback = (HTMainText->top_of_screen - a->line_num + (display_lines) - 1) / (display_lines); max_offset = a->line_num - (HTMainText->top_of_screen - screensback * (display_lines)); } else if (HTMainText->Lines - a->line_num <= (display_lines)) { max_offset = a->line_num - (HTMainText->Lines + 1 - (display_lines)); } else if (a->line_num >= HTMainText->top_of_screen + (display_lines)) { int screensahead = (a->line_num - HTMainText->top_of_screen) / (display_lines); max_offset = a->line_num - HTMainText->top_of_screen - screensahead * (display_lines); } else { max_offset = SEARCH_GOAL_LINE - 1; } /* Stuff below should remain unchanged if line positioning is tweaked. - kw */ if (max_offset < 0) max_offset = 0; else if (max_offset >= display_lines) max_offset = display_lines - 1; *go_line = a->line_num - max_offset; if (*go_line <= group_to_go->prev_anchor_line) *go_line = group_to_go->prev_anchor_line + 1; if (*go_line < 0) *go_line = 0; if (linknum) *linknum = group_to_go->anchors_this_line - 1; return (LINK_LINE_FOUND); } } return (NO); } /* * This function finds the line indicated by line_num in the * HText structure indicated by text, and searches that line * for the first hit with the string indicated by target. If * there is no hit, FALSE is returned. If there is a hit, then * a copy of the line starting at that first hit is loaded into * *data with all IsSpecial characters stripped, its offset and * the printable target length (without IsSpecial, or extra CJK * or utf8 characters) are loaded into *offset and *tLen, and * TRUE is returned. -FM */ BOOL HText_getFirstTargetInLine(HText *text, int line_num, int utf_flag, int *offset, int *tLen, char **data, const char *target) { HTLine *line; char *LineData; int LineOffset, HitOffset, LenNeeded, i; const char *cp; /* * Make sure we have an HText structure, that line_num is * in its range, and that we have a target string. -FM */ if (!(text && line_num >= 0 && line_num <= text->Lines && non_empty(target))) { return (FALSE); } /* * Find the line and set up its data and offset -FM */ for (i = 0, line = FirstHTLine(text); i < line_num && (line != text->last_line); i++, line = line->next) { if (line->next == NULL) { return (FALSE); } } if (!(line && line->data[0])) return (FALSE); LineData = (char *) line->data; LineOffset = (int) line->offset; /* * If the target is on the line, load the offset of * its first character and the subsequent line data, * strip any special characters from the loaded line * data, and return TRUE. -FM */ if (((cp = LYno_attr_mb_strstr(LineData, target, utf_flag, YES, &HitOffset, &LenNeeded)) != NULL) && (LineOffset + LenNeeded) <= DISPLAY_COLS) { /* * We had a hit so load the results, * remove IsSpecial characters from * the allocated data string, and * return TRUE. -FM */ *offset = (LineOffset + HitOffset); *tLen = (LenNeeded - HitOffset); StrAllocCopy(*data, cp); remove_special_attr_chars(*data); return (TRUE); } /* * The line does not contain the target. -FM */ return (FALSE); } /* * HText_getNumOfLines returns the number of lines in the * current document. */ int HText_getNumOfLines(void) { return (HTMainText ? HTMainText->Lines : 0); } /* * HText_getNumOfBytes returns the size of the document, as rendered. This * may be different from the original filesize. */ int HText_getNumOfBytes(void) { int result = -1; HTLine *line = NULL; if (HTMainText != 0) { for (line = FirstHTLine(HTMainText); line != HTMainText->last_line; line = line->next) { result += 1 + (int) strlen(line->data); } } return result; } /* * HText_getTitle returns the title of the * current document. */ const char *HText_getTitle(void) { return (HTMainText ? HTAnchor_title(HTMainText->node_anchor) : 0); } #ifdef USE_COLOR_STYLE const char *HText_getStyle(void) { return (HTMainText ? HTAnchor_style(HTMainText->node_anchor) : 0); } #endif /* * HText_getSugFname returns the suggested filename of the current * document (normally derived from a Content-Disposition header with * attachment; filename=name.suffix). -FM */ const char *HText_getSugFname(void) { return (HTMainText ? HTAnchor_SugFname(HTMainText->node_anchor) : 0); } /* * HTCheckFnameForCompression receives the address of an allocated * string containing a filename, and an anchor pointer, and expands * or truncates the string's suffix if appropriate, based on whether * the anchor indicates that the file is compressed. We assume * that the file was not uncompressed (as when downloading), and * believe the headers about whether it's compressed or not. -FM * * Added third arg - if strip_ok is FALSE, we don't trust the anchor * info enough to remove a compression suffix if the anchor object * does not indicate compression. - kw */ void HTCheckFnameForCompression(char **fname, HTParentAnchor *anchor, int strip_ok) { char *fn = *fname; char *dot = NULL; char *cp = NULL; const char *suffix = ""; CompressFileType method; CompressFileType second; /* * Make sure we have a string and anchor. -FM */ if (!(fn && anchor)) return; /* * Make sure we have a file, not directory, name. -FM */ if (*(fn = LYPathLeaf(fn)) == '\0') return; method = HTContentToCompressType(anchor); /* * If no Content-Encoding has been detected via the anchor * pointer, but strip_ok is not set, there is nothing left * to do. - kw */ if ((method == cftNone) && !strip_ok) return; /* * Treat .tgz specially */ if ((dot = strrchr(fn, '.')) != NULL && !strcasecomp(dot, ".tgz")) { if (method == cftNone) { strcpy(dot, ".tar"); } return; } /* * Seek the last dot, and check whether * we have a gzip or compress suffix. -FM */ if ((dot = strrchr(fn, '.')) != NULL) { int rootlen = 0; if (HTCompressFileType(fn, ".", &rootlen) != cftNone) { if (method == cftNone) { /* * It has a suffix which signifies a gzipped * or compressed file for us, but the anchor * claims otherwise, so tweak the suffix. -FM */ *dot = '\0'; } return; } if ((second = HTCompressFileType(fn, "-_", &rootlen)) != cftNone) { cp = fn + rootlen; if (method == cftNone) { /* * It has a tail which signifies a gzipped * file for us, but the anchor claims otherwise, * so tweak the suffix. -FM */ if (cp == dot + 1) cp--; *cp = '\0'; } else { /* * The anchor claims it's gzipped, and we * believe it, so force this tail to the * conventional suffix. -FM */ #ifdef VMS *cp = '-'; #else *cp = '.'; #endif /* VMS */ if (second == cftCompress) LYUpperCase(cp); else LYLowerCase(cp); } return; } } suffix = HTCompressTypeToSuffix(method); /* * Add the appropriate suffix. -FM */ if (*suffix) { if (!dot) { StrAllocCat(*fname, suffix); } else if (*++dot == '\0') { StrAllocCat(*fname, suffix + 1); } else { StrAllocCat(*fname, suffix); #ifdef VMS (*fname)[strlen(*fname) - strlen(suffix)] = '-'; #endif /* !VMS */ } } } /* * HText_getLastModified returns the Last-Modified header * if available, for the current document. -FM */ const char *HText_getLastModified(void) { return (HTMainText ? HTAnchor_last_modified(HTMainText->node_anchor) : 0); } /* * HText_getDate returns the Date header * if available, for the current document. -FM */ const char *HText_getDate(void) { return (HTMainText ? HTAnchor_date(HTMainText->node_anchor) : 0); } /* * HText_getServer returns the Server header * if available, for the current document. -FM */ const char *HText_getServer(void) { return (HTMainText ? HTAnchor_server(HTMainText->node_anchor) : 0); } #ifdef EXP_HTTP_HEADERS /* * Returns the full text of HTTP headers, if available, for the current * document. */ const char *HText_getHttpHeaders(void) { return (HTMainText ? HTAnchor_http_headers(HTMainText->node_anchor) : 0); } #endif /* * HText_pageDisplay displays a screen of text * starting from the line 'line_num'-1. * This is the primary call for lynx. */ void HText_pageDisplay(int line_num, char *target) { #ifdef DISP_PARTIAL if (debug_display_partial || (LYTraceLogFP != NULL)) { CTRACE((tfp, "GridText: HText_pageDisplay at line %d started\n", line_num)); } if (display_partial) { int stop_before = -1; /* * Garbage is reported from forms input fields in incremental mode. * So we start HText_trimHightext() to forget this side effect. * This function was split-out from HText_endAppend(). * It may not be the best solution but it works. - LP * * (FALSE = indicate that we are in partial mode) * Multiple calls of HText_trimHightext works without problem now. */ if (HTMainText && HTMainText->stbl) stop_before = Stbl_getStartLineDeep(HTMainText->stbl); HText_trimHightext(HTMainText, FALSE, stop_before); } #endif display_page(HTMainText, line_num - 1, target); #ifdef DISP_PARTIAL if (display_partial && debug_display_partial) LYSleepMsg(); #endif is_www_index = HTAnchor_isIndex(HTMainAnchor); #ifdef DISP_PARTIAL if (debug_display_partial || (LYTraceLogFP != NULL)) { CTRACE((tfp, "GridText: HText_pageDisplay finished\n")); } #endif } /* * Return YES if we have a whereis search target on the displayed * page. - kw */ BOOL HText_pageHasPrevTarget(void) { if (!HTMainText) return NO; else return HTMainText->page_has_target; } /* * Find the number of the closest anchor to the given document offset. Used * in reparsing, this will usually find an exact match, as a link shifts around * on the display. It will not find a match when (for example) the source view * shows images that are not links in the html. */ int HText_closestAnchor(HText *text, int offset) { int result = -1; int absdiff = 0; int newdiff; TextAnchor *Anchor_ptr = NULL; TextAnchor *closest = NULL; for (Anchor_ptr = text->first_anchor; Anchor_ptr != NULL; Anchor_ptr = Anchor_ptr->next) { if (Anchor_ptr->sgml_offset == offset) { result = Anchor_ptr->number; break; } else { newdiff = abs(Anchor_ptr->sgml_offset - offset); if (absdiff == 0 || absdiff > newdiff) { absdiff = newdiff; closest = Anchor_ptr; } } } if (result < 0 && closest != 0) { result = closest->number; } return result; } /* * Find the offset for the given anchor, e.g., the inverse of * HText_closestAnchor(). */ int HText_locateAnchor(HText *text, int anchor_number) { int result = -1; TextAnchor *Anchor_ptr = NULL; for (Anchor_ptr = text->first_anchor; Anchor_ptr != NULL; Anchor_ptr = Anchor_ptr->next) { if (Anchor_ptr->number == anchor_number) { result = Anchor_ptr->sgml_offset; break; } } return result; } /* * This is supposed to give the same result as the inline checks in * display_page(), so we can decide which anchors will be visible. */ static BOOL anchor_is_numbered(TextAnchor *Anchor_ptr) { BOOL result = FALSE; if (Anchor_ptr->show_anchor && (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) { result = TRUE; } else if (Anchor_ptr->link_type == INPUT_ANCHOR && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) { result = TRUE; } return result; } /* * Return the absolute line number (counting from the beginning of the * document) for the given absolute anchor number. Normally line numbers are * computed within the screen, and for that we use the links[] array. A few * uses require the absolute anchor number. For example, reparsing a document, * e.g., switching between normal and source views will alter the line numbers * of each link, and may require adjusting the top line number used for the * display, before we recompute the links[] array. */ int HText_getAbsLineNumber(HText *text, int anchor_number) { int result = -1; if (anchor_number >= 0 && text != 0) { TextAnchor *Anchor_ptr = NULL; for (Anchor_ptr = text->first_anchor; Anchor_ptr != NULL; Anchor_ptr = Anchor_ptr->next) { if (anchor_is_numbered(Anchor_ptr) && Anchor_ptr->number == anchor_number) { result = Anchor_ptr->line_num; break; } } } return result; } /* * Compute the link-number in a page, given the top line number of the page and * the absolute anchor number. */ int HText_anchorRelativeTo(HText *text, int top_lineno, int anchor_number) { int result = 0; int from_top = 0; TextAnchor *Anchor_ptr = NULL; for (Anchor_ptr = text->first_anchor; Anchor_ptr != NULL; Anchor_ptr = Anchor_ptr->next) { if (Anchor_ptr->number == anchor_number) { result = from_top; break; } if (!anchor_is_numbered(Anchor_ptr)) continue; if (Anchor_ptr->line_num >= top_lineno) { ++from_top; } } return result; } /* * HText_LinksInLines returns the number of links in the * 'Lines' number of lines beginning with 'line_num'-1. -FM */ int HText_LinksInLines(HText *text, int line_num, int Lines) { int total = 0; int start = (line_num - 1); int end = (start + Lines); TextAnchor *Anchor_ptr = NULL; if (!text) return total; for (Anchor_ptr = text->first_anchor; Anchor_ptr != NULL && Anchor_ptr->line_num <= end; Anchor_ptr = Anchor_ptr->next) { if (Anchor_ptr->line_num >= start && Anchor_ptr->line_num < end && Anchor_ptr->show_anchor && !(Anchor_ptr->link_type == INPUT_ANCHOR && Anchor_ptr->input_field->type == F_HIDDEN_TYPE)) ++total; } return total; } void HText_setStale(HText *text) { text->stale = YES; } void HText_refresh(HText *text) { if (text->stale) display_page(text, text->top_of_screen, ""); } int HText_sourceAnchors(HText *text) { return (text ? text->last_anchor_number : -1); } BOOL HText_canScrollUp(HText *text) { return (BOOL) (text->top_of_screen != 0); } /* * Check if there is more info below this page. */ BOOL HText_canScrollDown(void) { HText *text = HTMainText; return (BOOL) ((text != 0) && ((text->top_of_screen + display_lines) <= text->Lines)); } /* Scroll actions */ void HText_scrollTop(HText *text) { display_page(text, 0, ""); } void HText_scrollDown(HText *text) { display_page(text, text->top_of_screen + display_lines, ""); } void HText_scrollUp(HText *text) { display_page(text, text->top_of_screen - display_lines, ""); } void HText_scrollBottom(HText *text) { display_page(text, text->Lines - display_lines, ""); } /* Browsing functions * ================== */ /* Bring to front and highlight it */ BOOL HText_select(HText *text) { if (text != HTMainText) { /* * Reset flag for whereis search string - cannot be true here * since text is not our HTMainText. - kw */ if (text) text->page_has_target = NO; #ifdef DISP_PARTIAL /* Reset these for the previous and current text. - kw */ ResetPartialLinenos(text); ResetPartialLinenos(HTMainText); #endif /* DISP_PARTIAL */ #ifdef CAN_SWITCH_DISPLAY_CHARSET /* text->UCLYhndl is not reset by META, so use a more circumvent way */ if (text->node_anchor->UCStages->s[UCT_STAGE_HTEXT].LYhndl != current_char_set) Switch_Display_Charset(text->node_anchor->UCStages->s[UCT_STAGE_HTEXT].LYhndl, SWITCH_DISPLAY_CHARSET_MAYBE); #endif assert(text != NULL); if (HTMainText) { if (HText_hasUTF8OutputSet(HTMainText) && HTLoadedDocumentEightbit() && IS_UTF8_TTY) { text->had_utf8 = HTMainText->has_utf8; } else { text->had_utf8 = NO; } HTMainText->has_utf8 = NO; text->has_utf8 = NO; } HTMainText = text; HTMainAnchor = text->node_anchor; /* * Make this text the most current in the loaded texts list. -FM */ if (loaded_texts && HTList_removeObject(loaded_texts, text)) HTList_addObject(loaded_texts, text); } return YES; } /* * This function returns TRUE if doc's post_data, address * and isHEAD elements are identical to those of a loaded * (memory cached) text. -FM */ BOOL HText_POSTReplyLoaded(DocInfo *doc) { HText *text = NULL; HTList *cur = loaded_texts; bstring *post_data; char *address; BOOL is_head; /* * Make sure we have the structures. -FM */ if (!cur || !doc) return (FALSE); /* * Make sure doc is for a POST reply. -FM */ if ((post_data = doc->post_data) == NULL || (address = doc->address) == NULL) return (FALSE); is_head = doc->isHEAD; /* * Loop through the loaded texts looking for a * POST reply match. -FM */ while (NULL != (text = (HText *) HTList_nextObject(cur))) { if (text->node_anchor && text->node_anchor->post_data && BINEQ(post_data, text->node_anchor->post_data) && text->node_anchor->address && !strcmp(address, text->node_anchor->address) && is_head == text->node_anchor->isHEAD) { return (TRUE); } } return (FALSE); } BOOL HTFindPoundSelector(const char *selector) { TextAnchor *a; CTRACE((tfp, "FindPound: searching for \"%s\"\n", selector)); for (a = HTMainText->first_anchor; a != 0; a = a->next) { if (a->anchor && a->anchor->tag) { if (!strcmp(a->anchor->tag, selector)) { www_search_result = a->line_num + 1; CTRACE((tfp, "FindPound: Selecting anchor [%d] at line %d\n", a->number, www_search_result)); if (!strcmp(selector, LYToolbarName)) { --www_search_result; } return (YES); } } } return (NO); } BOOL HText_selectAnchor(HText *text, HTChildAnchor *anchor) { TextAnchor *a; int l; for (a = text->first_anchor; a; a = a->next) { if (a->anchor == anchor) break; } if (!a) { CTRACE((tfp, "HText: No such anchor in this text!\n")); return NO; } if (text != HTMainText) { /* Comment out by ??? */ HTMainText = text; /* Put back in by tbl 921208 */ HTMainAnchor = text->node_anchor; } l = a->line_num; CTRACE((tfp, "HText: Selecting anchor [%d] at line %d\n", a->number, l)); if (!text->stale && (l >= text->top_of_screen) && (l < text->top_of_screen + display_lines + 1)) return YES; www_search_result = l - (display_lines / 3); /* put in global variable */ return YES; } /* Editing functions - NOT IMPLEMENTED * ================= * * These are called from the application. There are many more functions * not included here from the original text object. */ /* Style handling: */ /* Apply this style to the selection */ void HText_applyStyle(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED) { } /* Update all text with changed style. */ void HText_updateStyle(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED) { } /* Return style of selection */ HTStyle *HText_selectionStyle(HText *me GCC_UNUSED, HTStyleSheet *sheet GCC_UNUSED) { return 0; } /* Paste in styled text */ void HText_replaceSel(HText *me GCC_UNUSED, const char *aString GCC_UNUSED, HTStyle *aStyle GCC_UNUSED) { } /* Apply this style to the selection and all similarly formatted text * (style recovery only) */ void HTextApplyToSimilar(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED) { } /* Select the first unstyled run. * (style recovery only) */ void HTextSelectUnstyled(HText *me GCC_UNUSED, HTStyleSheet *sheet GCC_UNUSED) { } /* Anchor handling: */ void HText_unlinkSelection(HText *me GCC_UNUSED) { } HTAnchor *HText_referenceSelected(HText *me GCC_UNUSED) { return 0; } int HText_getTopOfScreen(void) { HText *text = HTMainText; return text != 0 ? text->top_of_screen : 0; } int HText_getLines(HText *text) { return text->Lines; } /* * Constrain the line number to be within the document. The line number is * zero-based. */ int HText_getPreferredTopLine(HText *text, int line_number) { int last_screen = text->Lines - (display_lines - 2); if (text->Lines < display_lines) { line_number = 0; } else if (line_number > text->Lines) { line_number = last_screen; } else if (line_number < 0) { line_number = 0; } return line_number; } HTAnchor *HText_linkSelTo(HText *me GCC_UNUSED, HTAnchor * anchor GCC_UNUSED) { return 0; } /* * Utility for freeing the list of previous isindex and whereis queries. -FM */ void HTSearchQueries_free(void) { LYFreeStringList(search_queries); search_queries = NULL; } /* * Utility for listing isindex and whereis queries, making * any repeated queries the most current in the list. -FM */ void HTAddSearchQuery(char *query) { char *new_query = NULL; char *old; HTList *cur; if (!non_empty(query)) return; StrAllocCopy(new_query, query); if (!search_queries) { search_queries = HTList_new(); #ifdef LY_FIND_LEAKS atexit(HTSearchQueries_free); #endif HTList_addObject(search_queries, new_query); return; } cur = search_queries; while (NULL != (old = (char *) HTList_nextObject(cur))) { if (!strcmp(old, new_query)) { HTList_removeObject(search_queries, old); FREE(old); break; } } HTList_addObject(search_queries, new_query); return; } int do_www_search(DocInfo *doc) { bstring *searchstring = NULL; bstring *temp = NULL; char *cp; char *tmpaddress = NULL; int ch; RecallType recall; int QueryTotal; int QueryNum; BOOLEAN PreviousSearch = FALSE; int code; /* * Load the default query buffer */ if ((cp = StrChr(doc->address, '?')) != NULL) { /* * This is an index from a previous search. * Use its query as the default. */ PreviousSearch = TRUE; BStrCopy0(searchstring, ++cp); for (cp = searchstring->str; *cp; cp++) if (*cp == '+') *cp = ' '; HTUnEscape(searchstring->str); BStrCopy(temp, searchstring); /* * Make sure it's treated as the most recent query. -FM */ HTAddSearchQuery(searchstring->str); } else { /* * New search; no default. */ BStrCopy0(searchstring, ""); BStrCopy0(temp, ""); } /* * Prompt for a query string. */ if (isBEmpty(searchstring)) { if (HTMainAnchor->isIndexPrompt) _statusline(HTMainAnchor->isIndexPrompt); else _statusline(ENTER_DATABASE_QUERY); } else _statusline(EDIT_CURRENT_QUERY); QueryTotal = (search_queries ? HTList_count(search_queries) : 0); recall = (((PreviousSearch && QueryTotal >= 2) || (!PreviousSearch && QueryTotal >= 1)) ? RECALL_URL : NORECALL); QueryNum = QueryTotal; get_query: if ((ch = LYgetBString(&searchstring, FALSE, 0, recall)) < 0 || isBEmpty(searchstring) || ch == UPARROW_KEY || ch == DNARROW_KEY) { if (recall && ch == UPARROW_KEY) { if (PreviousSearch) { /* * Use the second to last query in the list. -FM */ QueryNum = 1; PreviousSearch = FALSE; } else { /* * Go back to the previous query in the list. -FM */ QueryNum++; } if (QueryNum >= QueryTotal) /* * Roll around to the last query in the list. -FM */ QueryNum = 0; if ((cp = (char *) HTList_objectAt(search_queries, QueryNum)) != NULL) { BStrCopy0(searchstring, cp); if (!isBEmpty(temp) && !strcmp(temp->str, searchstring->str)) { _statusline(EDIT_CURRENT_QUERY); } else if ((!isBEmpty(temp) && QueryTotal == 2) || (isBEmpty(temp) && QueryTotal == 1)) { _statusline(EDIT_THE_PREV_QUERY); } else { _statusline(EDIT_A_PREV_QUERY); } goto get_query; } } else if (recall && ch == DNARROW_KEY) { if (PreviousSearch) { /* * Use the first query in the list. -FM */ QueryNum = QueryTotal - 1; PreviousSearch = FALSE; } else { /* * Advance to the next query in the list. -FM */ QueryNum--; } if (QueryNum < 0) /* * Roll around to the first query in the list. -FM */ QueryNum = QueryTotal - 1; if ((cp = (char *) HTList_objectAt(search_queries, QueryNum)) != NULL) { BStrCopy0(searchstring, cp); if (!isBEmpty(temp) && !strcmp(temp->str, searchstring->str)) { _statusline(EDIT_CURRENT_QUERY); } else if ((!isBEmpty(temp) && QueryTotal == 2) || (isBEmpty(temp) && QueryTotal == 1)) { _statusline(EDIT_THE_PREV_QUERY); } else { _statusline(EDIT_A_PREV_QUERY); } goto get_query; } } /* * Search cancelled. */ HTInfoMsg(CANCELLED); code = NULLFILE; } else { LYTrimLeading(searchstring->str); LYTrimTrailing(searchstring->str); if (isBEmpty(searchstring)) { HTInfoMsg(CANCELLED); code = NULLFILE; } else if (!LYforce_no_cache && !isBEmpty(temp) && !strcmp(temp->str, searchstring->str)) { /* * Don't resubmit the same query unintentionally. */ HTUserMsg(USE_C_R_TO_RESUB_CUR_QUERY); code = NULLFILE; } else { /* * Add searchstring to the query list, * or make it the most current. -FM */ HTAddSearchQuery(searchstring->str); /* * Show the URL with the new query. */ if ((cp = StrChr(doc->address, '?')) != NULL) *cp = '\0'; StrAllocCopy(tmpaddress, doc->address); StrAllocCat(tmpaddress, "?"); StrAllocCat(tmpaddress, searchstring->str); user_message(WWW_WAIT_MESSAGE, tmpaddress); #ifdef SYSLOG_REQUESTED_URLS LYSyslog(tmpaddress); #endif FREE(tmpaddress); if (cp) *cp = '?'; /* * OK, now we do the search. */ if (HTSearch(searchstring->str, HTMainAnchor)) { auto char *cp_freeme = NULL; if (traversal) cp_freeme = stub_HTAnchor_address((HTAnchor *) HTMainAnchor); else cp_freeme = HTAnchor_address((HTAnchor *) HTMainAnchor); StrAllocCopy(doc->address, cp_freeme); FREE(cp_freeme); CTRACE((tfp, "\ndo_www_search: newfile: %s\n", doc->address)); /* * Yah, the search succeeded. */ code = NORMAL; } else { /* * Either the search failed (Yuk), or we got redirection. * If it's redirection, use_this_url_instead is set, and * mainloop() will deal with it such that security features * and restrictions are checked before acting on the URL, or * rejecting it. -FM */ code = NOT_FOUND; } } } BStrFree(searchstring); BStrFree(temp); return code; } static void write_offset(FILE *fp, HTLine *line) { int i; if (line->data[0]) { for (i = 0; i < (int) line->offset; i++) { fputc(' ', fp); } } } static void write_hyphen(FILE *fp) { if (dump_output_immediately && LYRawMode && LYlowest_eightbit[current_char_set] <= 173 && (LYCharSet_UC[current_char_set].enc == UCT_ENC_8859 || (LYCharSet_UC[current_char_set].like8859 & UCT_R_8859SPECL)) != 0) { fputc(0xad, fp); /* the iso8859 byte for SHY */ } else { fputc('-', fp); } } /* * Returns the length after trimming trailing blanks. Modify the string as * needed so that any special character which follows a trailing blank is moved * before the (trimmed) blank, so the result which will be dumped has no * trailing blanks. */ static int TrimmedLength(char *string) { int result = (int) strlen(string); if (!HTisDocumentSource()) { int adjust = result; unsigned ch; while (adjust > 0) { ch = UCH(string[adjust - 1]); if (isspace(ch) || IsSpecialAttrChar(ch)) { --adjust; } else { break; } } if (result != adjust) { char *dst = string + adjust; char *src = dst; for (;;) { src = LYSkipBlanks(src); if ((*dst++ = *src++) == '\0') break; } result = (int) (dst - string - 1); } } return result; } typedef struct _AnchorIndex { struct _AnchorIndex *next; int type; /* field type */ int size; /* character-width of field */ int length; /* byte-count for field's data */ int offset; /* byte-offset in line's data */ int filler; /* character to use for filler */ const char *value; /* field's value */ } AnchorIndex; static unsigned countHTLines(void) { unsigned result = 0; HTLine *line = FirstHTLine(HTMainText); while (line != 0) { ++result; if (line == HTMainText->last_line) break; line = line->next; } CTRACE((tfp, "countHTLines %u\n", result)); return result; } /* * The TextAnchor list is not organized to allow efficient dumping of a page. * Make an array with one item per line of the page, and store (by byte-offset) * pointers to the TextAnchor's we want to use. */ static AnchorIndex **allocAnchorIndex(unsigned *size) { AnchorIndex **result = NULL; AnchorIndex *p, *q; TextAnchor *anchor = NULL; FormInfo *input = NULL; *size = countHTLines(); if (*size != 0) { result = typecallocn(AnchorIndex *, *size + 1); if (result == NULL) outofmem(__FILE__, "allocAnchorIndex"); for (anchor = HTMainText->first_anchor; anchor != NULL; anchor = anchor->next) { if (anchor->link_type == INPUT_ANCHOR && anchor->show_anchor && anchor->line_num < (int) *size && (input = anchor->input_field) != NULL) { CTRACE2(TRACE_GRIDTEXT, (tfp, "line %d.%d %d %s->%s(%s)\n", anchor->line_num, anchor->line_pos, input->size, inputFieldDesc(input), input->value, input->orig_value)); switch (input->type) { case F_SUBMIT_TYPE: case F_RESET_TYPE: case F_TEXT_SUBMIT_TYPE: case F_IMAGE_SUBMIT_TYPE: CTRACE2(TRACE_GRIDTEXT, (tfp, "skipping\n")); continue; case F_TEXT_TYPE: case F_PASSWORD_TYPE: case F_CHECKBOX_TYPE: case F_RADIO_TYPE: case F_OPTION_LIST_TYPE: case F_TEXTAREA_TYPE: case F_RANGE_TYPE: case F_FILE_TYPE: p = typecalloc(AnchorIndex); if (p == NULL) outofmem(__FILE__, "allocAnchorIndex"); assert(p != NULL); p->type = input->type; p->size = input->size; p->offset = anchor->line_pos; p->value = input->value; switch (input->type) { case F_TEXTAREA_TYPE: case F_TEXT_TYPE: case F_PASSWORD_TYPE: p->filler = '_'; break; case F_OPTION_LIST_TYPE: p->filler = '_'; break; case F_CHECKBOX_TYPE: p->value = (input->num_value ? checked_box : unchecked_box); break; case F_RADIO_TYPE: p->value = (input->num_value ? checked_radio : unchecked_radio); break; default: p->filler = ' '; break; } p->length = (int) strlen(p->value); if ((q = result[anchor->line_num]) != NULL) { /* insert, ordering by offset */ if (q->offset < p->offset) { while (q->next != NULL && q->next->offset < p->offset) { q = q->next; } p->next = q->next; q->next = p; } else { p->next = q; result[anchor->line_num] = p; } } else { result[anchor->line_num] = p; } break; } } } } return result; } /* * Free the data allocated in allocAnchorIndex(). */ static void freeAnchorIndex(AnchorIndex ** inx, unsigned inx_size) { AnchorIndex *cur; unsigned num; if (inx != 0) { if (inx_size != 0) { for (num = 0; num < inx_size; ++num) { while ((cur = inx[num]) != NULL) { inx[num] = cur->next; free(cur); } } } free(inx); } } /* * Print the contents of the file in HTMainText to * the file descriptor fp. * If is_email is TRUE add ">" before each "From " line. * If is_reply is TRUE add ">" to the beginning of each * line to specify the file is a reply to message. */ #define FieldFirst(p) (this_wrap ? 0 : (p)->offset) #define FieldLast(p) (FieldFirst(p) + (p)->size - this_wrap) void print_wwwfile_to_fd(FILE *fp, int is_email, int is_reply) { int line_num, byte_num, byte_count; int first = TRUE; HTLine *line; AnchorIndex **inx; /* sorted index of input-fields */ AnchorIndex *cur = 0; /* current input-field */ unsigned inx_size; /* number of entries in inx[] */ int in_field = -1; /* if positive, is index in cur->value[] */ int this_wrap = 0; /* current wrapping point of cur->value[] */ int next_wrap = 0; /* next wrapping point of cur->value[] */ #ifndef NO_DUMP_WITH_BACKSPACES HText *text = HTMainText; BOOL in_b = FALSE; BOOL in_u = FALSE; BOOL bs = (BOOL) (!is_email && !is_reply && text != 0 && with_backspaces && !IS_CJK_TTY && !text->T.output_utf8); #endif if (!HTMainText) return; /* * Build an index of anchors for each line, so we can override the * static text which is stored in the list of HTLine's. */ inx = allocAnchorIndex(&inx_size); line = FirstHTLine(HTMainText); for (line_num = 0;; ++line_num, line = line->next) { if (in_field >= 0) { this_wrap = next_wrap; next_wrap = 0; /* FIXME - allow for multiple continuations */ CTRACE2(TRACE_GRIDTEXT, (tfp, "wrap %d:%d, offset %d\n", in_field, cur ? cur->length : -1, this_wrap)); } else { cur = inx[line_num]; } CTRACE2(TRACE_GRIDTEXT, (tfp, "dump %d:%s\n", line_num, line->data)); if (first) { first = FALSE; if (is_reply) { fputc('>', fp); } else if (is_email && !StrNCmp(line->data, "From ", 5)) { fputc('>', fp); } } else if (line->data[0] != LY_SOFT_NEWLINE) { fputc('\n', fp); /* * Add news-style quotation if requested. -FM */ if (is_reply) { fputc('>', fp); } else if (is_email && !StrNCmp(line->data, "From ", 5)) { fputc('>', fp); } } write_offset(fp, line); /* * Add data. */ byte_count = TrimmedLength(line->data); for (byte_num = 0; byte_num < byte_count; byte_num++) { int byte_offset = byte_num + line->offset; int ch = UCH(line->data[byte_num]); int c2; while (cur != 0 && FieldLast(cur) < byte_offset) { CTRACE2(TRACE_GRIDTEXT, (tfp, "skip field since last %d < %d\n", FieldLast(cur), byte_offset)); cur = cur->next; in_field = -1; } if (cur != 0 && in_field >= 0) { CTRACE2(TRACE_GRIDTEXT, (tfp, "compare %d to [%d..%d]\n", byte_offset, FieldFirst(cur), FieldLast(cur) - 1)); } if (cur != 0 && FieldFirst(cur) <= byte_offset && FieldLast(cur) > byte_offset) { int off2 = ((in_field > 0) ? in_field : (byte_offset - FieldFirst(cur))); /* * On the first time (for each line that the field appears on), * check if this field wraps. If it does, save the offset into * the field which will be used to adjust the beginning of the * continuation line. */ if (byte_offset == FieldFirst(cur)) { next_wrap = 0; if (cur->size - this_wrap + byte_num > byte_count) { CTRACE((tfp, "size %d, offset %d, length %d\n", cur->size, cur->offset, cur->length)); CTRACE((tfp, "byte_count %d, byte_num %d\n", byte_count, byte_num)); next_wrap = byte_count - byte_num; CTRACE2(TRACE_GRIDTEXT, (tfp, "field will wrap: %d\n", next_wrap)); } } c2 = ((off2 < cur->length) ? cur->value[off2] : cur->filler); if (ch != c2) { CTRACE2(TRACE_GRIDTEXT, (tfp, "line %d %d/%d [%d..%d] map %d %c->%c\n", line_num, off2, cur->length, FieldFirst(cur), FieldLast(cur) - 1, byte_offset, ch, c2)); ch = c2; } ++off2; if ((off2 >= cur->size) && (off2 >= cur->length || F_TEXTLIKE(cur->type))) { in_field = -1; this_wrap = 0; next_wrap = 0; } else { in_field = off2; } } if (!IsSpecialAttrChar(ch)) { #ifndef NO_DUMP_WITH_BACKSPACES if (in_b) { fputc(ch, fp); fputc('\b', fp); fputc(ch, fp); } else if (in_u) { fputc('_', fp); fputc('\b', fp); fputc(ch, fp); } else #endif fputc(ch, fp); } else if (ch == LY_SOFT_HYPHEN && (byte_num + 1) >= byte_count) { write_hyphen(fp); } else if (dump_output_immediately && use_underscore) { switch (ch) { case LY_UNDERLINE_START_CHAR: case LY_UNDERLINE_END_CHAR: fputc('_', fp); break; case LY_BOLD_START_CHAR: case LY_BOLD_END_CHAR: break; } } #ifndef NO_DUMP_WITH_BACKSPACES else if (bs) { switch (ch) { case LY_UNDERLINE_START_CHAR: if (!in_b) in_u = TRUE; /*favor bold over underline */ break; case LY_UNDERLINE_END_CHAR: in_u = FALSE; break; case LY_BOLD_START_CHAR: if (in_u) in_u = FALSE; /* turn it off */ in_b = TRUE; break; case LY_BOLD_END_CHAR: in_b = FALSE; break; } } #endif } if (line == HTMainText->last_line) break; #ifdef VMS if (HadVMSInterrupt) break; #endif /* VMS */ } fputc('\n', fp); freeAnchorIndex(inx, inx_size); } /* * Print the contents of the file in HTMainText to * the file descriptor fp. * First output line is "thelink", ie, the URL for this file. */ void print_crawl_to_fd(FILE *fp, char *thelink, char *thetitle) { register int i; int first = TRUE; int limit; HTLine *line; if (!HTMainText) return; line = FirstHTLine(HTMainText); fprintf(fp, "THE_URL:%s\n", thelink); if (thetitle != NULL) { fprintf(fp, "THE_TITLE:%s\n", thetitle); } for (;; line = line->next) { if (!first && line->data[0] != LY_SOFT_NEWLINE) fputc('\n', fp); first = FALSE; write_offset(fp, line); /* * Add data. */ limit = TrimmedLength(line->data); for (i = 0; i < limit; i++) { int ch = UCH(line->data[i]); if (!IsSpecialAttrChar(ch)) { fputc(ch, fp); } else if (ch == LY_SOFT_HYPHEN && (i + 1) >= limit) { /* last char on line */ write_hyphen(fp); } } if (!HTMainText || (line == HTMainText->last_line)) { break; } } fputc('\n', fp); /* * Add the References list if appropriate */ if ((no_list == FALSE) && (dump_links_inline == FALSE) && links_are_numbered()) { printlist(fp, FALSE); } #ifdef VMS HadVMSInterrupt = FALSE; #endif /* VMS */ } static void adjust_search_result(DocInfo *doc, int tentative_result, int start_line) { if (tentative_result > 0) { int anch_line = -1; TextAnchor *a; int nl_closest = -1; int goal = SEARCH_GOAL_LINE; int max_offset; BOOL on_screen = (BOOL) (tentative_result > HTMainText->top_of_screen && tentative_result <= HTMainText->top_of_screen + display_lines); if (goal < 1) goal = 1; else if (goal > display_lines) goal = display_lines; max_offset = goal - 1; if (on_screen && nlinks > 0) { int i; for (i = 0; i < nlinks; i++) { if (doc->line + links[i].ly - 1 <= tentative_result) nl_closest = i; if (doc->line + links[i].ly - 1 >= tentative_result) break; } if (nl_closest >= 0 && doc->line + links[nl_closest].ly - 1 == tentative_result) { www_search_result = doc->line; doc->link = nl_closest; return; } } /* find last anchor before or on target line */ for (a = HTMainText->first_anchor; a && a->line_num <= tentative_result - 1; a = a->next) { anch_line = a->line_num + 1; } /* position such that the anchor found is on first screen line, if it is not too far above the target line; but also try to make sure we move forward. */ if (anch_line >= 0 && anch_line >= tentative_result - max_offset && (anch_line > start_line || tentative_result <= HTMainText->top_of_screen)) { www_search_result = anch_line; } else if (tentative_result - start_line > 0 && tentative_result - (start_line + 1) <= max_offset) { www_search_result = start_line + 1; } else if (tentative_result > HTMainText->top_of_screen && tentative_result <= start_line && /* have wrapped */ tentative_result <= HTMainText->top_of_screen + goal) { www_search_result = HTMainText->top_of_screen + 1; } else if (tentative_result <= goal) www_search_result = 1; else www_search_result = tentative_result - max_offset; if (www_search_result == doc->line) { if (nl_closest >= 0) { doc->link = nl_closest; return; } } } } /* * see also link_has_target */ static BOOL anchor_has_target(TextAnchor *a, char *target) { char *text = NULL; const char *last = "?"; int count; /* * Combine the parts of the link's text using the highlighting information, * and compare the target against that. */ for (count = 0; count < 10; ++count) { const char *part = LYGetHiTextStr(a, count); if (part == NULL || part == last) { if (text != NULL && LYno_attr_strstr(text, target)) { return TRUE; } break; } StrAllocCat(text, part); last = part; } return field_has_target(a->input_field, target); } static TextAnchor *line_num_to_anchor(int line_num) { TextAnchor *a; if (HTMainText != 0) { a = HTMainText->first_anchor; while (a != 0 && a->line_num < line_num) { a = a->next; } } else { a = 0; } return a; } static int line_num_in_text(HText *text, HTLine *line) { int result = 1; HTLine *temp = FirstHTLine(text); while (temp != line) { temp = temp->next; ++result; } return result; } /* Computes the 'prev' pointers on demand, and returns the one for the given * anchor. */ static TextAnchor *get_prev_anchor(TextAnchor *a) { TextAnchor *p, *q; if (a->prev == 0) { if ((p = HTMainText->first_anchor) != 0) { while ((q = p->next) != 0) { q->prev = p; p = q; } } } return a->prev; } static int www_search_forward(int start_line, DocInfo *doc, char *target, HTLine *line, int count) { int wrapped = 0; TextAnchor *a = line_num_to_anchor(count - 1); int tentative_result = -1; for (;;) { while ((a != NULL) && a->line_num == (count - 1)) { if (a->show_anchor && !(a->link_type == INPUT_ANCHOR && a->input_field->type == F_HIDDEN_TYPE)) { if (anchor_has_target(a, target)) { adjust_search_result(doc, count, start_line); return 1; } } a = a->next; } if (LYno_attr_strstr(line->data, target)) { tentative_result = count; break; } else if ((count == start_line && wrapped) || wrapped > 1) { HTUserMsg2(STRING_NOT_FOUND, target); return -1; } else if (line == HTMainText->last_line) { count = 0; wrapped++; a = HTMainText->first_anchor; } line = line->next; count++; } if (tentative_result > 0) { adjust_search_result(doc, tentative_result, start_line); } return 0; } static int www_search_backward(int start_line, DocInfo *doc, char *target, HTLine *line, int count) { int wrapped = 0; TextAnchor *a = line_num_to_anchor(count - 1); int tentative_result = -1; for (;;) { while ((a != NULL) && a->line_num == (count - 1)) { if (a->show_anchor && !(a->link_type == INPUT_ANCHOR && a->input_field->type == F_HIDDEN_TYPE)) { if (anchor_has_target(a, target)) { adjust_search_result(doc, count, start_line); return 1; } } a = get_prev_anchor(a); } if (LYno_attr_strstr(line->data, target)) { tentative_result = count; break; } else if ((count == start_line && wrapped) || wrapped > 1) { HTUserMsg2(STRING_NOT_FOUND, target); return -1; } else if (line == FirstHTLine(HTMainText)) { count = line_num_in_text(HTMainText, LastHTLine(HTMainText)) + 1; wrapped++; a = HTMainText->last_anchor; } line = line->prev; count--; } if (tentative_result > 0) { adjust_search_result(doc, tentative_result, start_line); } return 0; } void www_user_search(int start_line, DocInfo *doc, char *target, int direction) { HTLine *line; int count; if (!HTMainText) { return; } /* * Advance to the start line. */ line = FirstHTLine(HTMainText); if (start_line + direction > 0) { for (count = 1; count < start_line + direction; line = line->next, count++) { if (line == HTMainText->last_line) { line = FirstHTLine(HTMainText); count = 1; break; } } } else { line = HTMainText->last_line; count = line_num_in_text(HTMainText, line); } if (direction >= 0) www_search_forward(start_line, doc, target, line, count); else www_search_backward(start_line, doc, target, line, count); } void user_message(const char *message, const char *argument) { if (message == NULL) { mustshow = FALSE; } else { char *temp = NULL; HTSprintf0(&temp, message, NonNull(argument)); statusline(temp); FREE(temp); } } /* * HText_getOwner returns the owner of the * current document. */ const char *HText_getOwner(void) { return (HTMainText ? HTAnchor_owner(HTMainText->node_anchor) : 0); } /* * HText_setMainTextOwner sets the owner for the * current document. */ void HText_setMainTextOwner(const char *owner) { if (!HTMainText) return; HTAnchor_setOwner(HTMainText->node_anchor, owner); } /* * HText_getRevTitle returns the RevTitle element of the * current document, used as the subject for mailto comments * to the owner. */ const char *HText_getRevTitle(void) { return (HTMainText ? HTAnchor_RevTitle(HTMainText->node_anchor) : 0); } /* * HText_getContentBase returns the Content-Base header * of the current document. */ const char *HText_getContentBase(void) { return (HTMainText ? HTAnchor_content_base(HTMainText->node_anchor) : 0); } /* * HText_getContentLocation returns the Content-Location header * of the current document. */ const char *HText_getContentLocation(void) { return (HTMainText ? HTAnchor_content_location(HTMainText->node_anchor) : 0); } /* * HText_getMessageID returns the Message-ID of the * current document. */ const char *HText_getMessageID(void) { return (HTMainText ? HTAnchor_messageID(HTMainText->node_anchor) : NULL); } void HTuncache_current_document(void) { /* * Should remove current document from memory. */ if (HTMainText) { HTParentAnchor *htmain_anchor = HTMainText->node_anchor; if (htmain_anchor) { if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) { FREE(htmain_anchor->UCStages); } } CTRACE((tfp, "\nHTuncache.. freeing document for '%s'%s\n", ((htmain_anchor && htmain_anchor->address) ? htmain_anchor->address : "unknown anchor"), ((htmain_anchor && htmain_anchor->post_data) ? " with POST data" : ""))); HTList_removeObject(loaded_texts, HTMainText); HText_free(HTMainText); HTMainText = NULL; } else { CTRACE((tfp, "HTuncache.. HTMainText already is NULL!\n")); } } /* * This magic FREE(anchor->UCStages) call * stolen from HTuncache_current_document() above. */ static void magicUncache(void) { if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) { FREE(HTMainAnchor->UCStages); } /* avoid null-reference later */ if (!HTOutputFormat) HTOutputFormat = WWW_SOURCE; } #ifdef USE_SOURCE_CACHE /* dummy - kw */ static HTProtocol scm = { "source-cache-mem", 0, 0 }; static BOOLEAN useSourceCache(void) { BOOLEAN result = FALSE; if (LYCacheSource == SOURCE_CACHE_FILE) { result = (BOOLEAN) (HTMainAnchor->source_cache_file != 0); CTRACE((tfp, "SourceCache: file-cache%s found\n", result ? "" : " not")); } return result; } static BOOLEAN useMemoryCache(void) { BOOLEAN result = FALSE; if (LYCacheSource == SOURCE_CACHE_MEMORY) { result = (BOOLEAN) (HTMainAnchor->source_cache_chunk != 0); CTRACE((tfp, "SourceCache: memory-cache%s found\n", result ? "" : " not")); } return result; } BOOLEAN HTreparse_document(void) { BOOLEAN ok = FALSE; if (!HTMainAnchor || LYCacheSource == SOURCE_CACHE_NONE) { CTRACE((tfp, "HTreparse_document returns FALSE\n")); } else if (useSourceCache()) { FILE *fp; HTFormat format; int ret; CTRACE((tfp, "SourceCache: Reparsing file %s\n", HTMainAnchor->source_cache_file)); magicUncache(); /* * This is more or less copied out of HTLoadFile(), except we don't * get a content encoding. This may be overkill. -dsb */ if (HTMainAnchor->content_type) { format = HTAtom_for(HTMainAnchor->content_type); } else { format = HTFileFormat(HTMainAnchor->source_cache_file, NULL, NULL); format = HTCharsetFormat(format, HTMainAnchor, UCLYhndl_for_unspec); /* not UCLYhndl_HTFile_for_unspec - we are talking about remote * documents... */ } CTRACE((tfp, " Content type is \"%s\"\n", format->name)); fp = fopen(HTMainAnchor->source_cache_file, "r"); if (!fp) { CTRACE((tfp, " Cannot read file %s\n", HTMainAnchor->source_cache_file)); (void) LYRemoveTemp(HTMainAnchor->source_cache_file); FREE(HTMainAnchor->source_cache_file); } else { if (HText_HaveUserChangedForms(HTMainText)) { /* * Issue a warning. Will not restore changed forms, currently. */ HTAlert(RELOADING_FORM); } /* Set HTMainAnchor->protocol or HTMainAnchor->physical to convince * the SourceCacheWriter to not regenerate the cache file (which * would be an unnecessary "loop"). - kw */ HTAnchor_setProtocol(HTMainAnchor, &HTFile); ret = HTParseFile(format, HTOutputFormat, HTMainAnchor, fp, NULL); LYCloseInput(fp); if (ret == HT_PARTIAL_CONTENT) { HTInfoMsg(gettext("Loading incomplete.")); CTRACE((tfp, "SourceCache: `%s' has been accessed, partial content.\n", HTLoadedDocumentURL())); } ok = (BOOL) (ret == HT_LOADED || ret == HT_PARTIAL_CONTENT); CTRACE((tfp, "Reparse file %s\n", (ok ? "succeeded" : "failed"))); } } else if (useMemoryCache()) { HTFormat format = WWW_HTML; int ret; CTRACE((tfp, "SourceCache: Reparsing from memory chunk %p\n", (void *) HTMainAnchor->source_cache_chunk)); magicUncache(); if (HTMainAnchor->content_type) { format = HTAtom_for(HTMainAnchor->content_type); } else { /* * This is only done to make things aligned with SOURCE_CACHE_NONE * and SOURCE_CACHE_FILE when switching to source mode since the * original document's charset will be LYPushAssumed() and then * LYPopAssumed(). See LYK_SOURCE in mainloop if you change * something here. No user-visible benefits, seems just '=' Info * Page will show source's effective charset as "(assumed)". */ format = HTCharsetFormat(format, HTMainAnchor, UCLYhndl_for_unspec); } /* not UCLYhndl_HTFile_for_unspec - we are talking about remote documents... */ if (HText_HaveUserChangedForms(HTMainText)) { /* * Issue a warning. Will not restore changed forms, currently. */ HTAlert(RELOADING_FORM); } /* Set HTMainAnchor->protocol or HTMainAnchor->physical to convince * the SourceCacheWriter to not regenerate the cache chunk (which * would be an unnecessary "loop"). - kw */ HTAnchor_setProtocol(HTMainAnchor, &scm); /* cheating - anything != &HTTP or &HTTPS would do - kw */ ret = HTParseMem(format, HTOutputFormat, HTMainAnchor, HTMainAnchor->source_cache_chunk, NULL); ok = (BOOL) (ret == HT_LOADED); CTRACE((tfp, "Reparse memory %s\n", (ok ? "succeeded" : "failed"))); } return ok; } BOOLEAN HTcan_reparse_document(void) { BOOLEAN result = FALSE; if (!HTMainAnchor || LYCacheSource == SOURCE_CACHE_NONE) { result = FALSE; } else if (useSourceCache()) { result = LYCanReadFile(HTMainAnchor->source_cache_file); } else if (useMemoryCache()) { result = TRUE; } CTRACE((tfp, "HTcan_reparse_document -> %d\n", result)); return result; } static void trace_setting_change(const char *name, int prev_setting, int new_setting) { if (prev_setting != new_setting) CTRACE((tfp, "HTdocument_settings_changed: %s setting has changed (was %d, now %d)\n", name, prev_setting, new_setting)); } BOOLEAN HTdocument_settings_changed(void) { /* * Annoying Hack(TM): If we don't have a source cache, we can't * reparse anyway, so pretend the settings haven't changed. */ if (!HTMainText || !HTcan_reparse_document()) return FALSE; if (TRACE) { /* * If we're tracing, note everying that has changed. */ trace_setting_change("CLICKABLE_IMAGES", HTMainText->clickable_images, clickable_images); trace_setting_change("PSEUDO_INLINE_ALTS", HTMainText->pseudo_inline_alts, pseudo_inline_alts); trace_setting_change("VERBOSE_IMG", HTMainText->verbose_img, verbose_img); trace_setting_change("RAW_MODE", HTMainText->raw_mode, LYUseDefaultRawMode); trace_setting_change("HISTORICAL_COMMENTS", HTMainText->historical_comments, historical_comments); trace_setting_change("MINIMAL_COMMENTS", HTMainText->minimal_comments, minimal_comments); trace_setting_change("SOFT_DQUOTES", HTMainText->soft_dquotes, soft_dquotes); trace_setting_change("OLD_DTD", HTMainText->old_dtd, Old_DTD); trace_setting_change("KEYPAD_MODE", HTMainText->keypad_mode, keypad_mode); if (HTMainText->disp_lines != LYlines || HTMainText->disp_cols != DISPLAY_COLS) CTRACE((tfp, "HTdocument_settings_changed: Screen size has changed (was %dx%d, now %dx%d)\n", HTMainText->disp_cols, HTMainText->disp_lines, DISPLAY_COLS, LYlines)); } return (BOOLEAN) (HTMainText->clickable_images != clickable_images || HTMainText->pseudo_inline_alts != pseudo_inline_alts || HTMainText->verbose_img != verbose_img || HTMainText->raw_mode != LYUseDefaultRawMode || HTMainText->historical_comments != historical_comments || (HTMainText->minimal_comments != minimal_comments && !historical_comments) || HTMainText->soft_dquotes != soft_dquotes || HTMainText->old_dtd != Old_DTD || HTMainText->keypad_mode != keypad_mode || HTMainText->disp_cols != DISPLAY_COLS); } #endif int HTisDocumentSource(void) { return (HTMainText != 0) ? HTMainText->source : FALSE; } const char *HTLoadedDocumentURL(void) { if (!HTMainText) return (""); if (HTMainText->node_anchor && HTMainText->node_anchor->address) return (HTMainText->node_anchor->address); else return (""); } bstring *HTLoadedDocumentPost_data(void) { if (HTMainText && HTMainText->node_anchor && HTMainText->node_anchor->post_data) return (HTMainText->node_anchor->post_data); else return (0); } const char *HTLoadedDocumentTitle(void) { if (!HTMainText) return (""); if (HTMainText->node_anchor && HTMainText->node_anchor->title) return (HTMainText->node_anchor->title); else return (""); } BOOLEAN HTLoadedDocumentIsHEAD(void) { if (!HTMainText) return (FALSE); if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD) return (HTMainText->node_anchor->isHEAD); else return (FALSE); } BOOLEAN HTLoadedDocumentIsSafe(void) { if (!HTMainText) return (FALSE); if (HTMainText->node_anchor && HTMainText->node_anchor->safe) return (HTMainText->node_anchor->safe); else return (FALSE); } const char *HTLoadedDocumentCharset(void) { const char *result = NULL; if (HTMainText && HTMainText->node_anchor) { result = HTMainText->node_anchor->charset; } return result; } BOOL HTLoadedDocumentEightbit(void) { if (!HTMainText) return (NO); else return (HTMainText->have_8bit_chars); } void HText_setNodeAnchorBookmark(const char *bookmark) { if (!HTMainText) return; if (HTMainText->node_anchor) HTAnchor_setBookmark(HTMainText->node_anchor, bookmark); } const char *HTLoadedDocumentBookmark(void) { if (!HTMainText) return (NULL); if (HTMainText->node_anchor && HTMainText->node_anchor->bookmark) return (HTMainText->node_anchor->bookmark); else return (NULL); } int HText_LastLineSize(HText *text, int IgnoreSpaces) { if (!text || !text->last_line || !text->last_line->size) return 0; return HText_TrueLineSize(text->last_line, text, IgnoreSpaces); } BOOL HText_LastLineEmpty(HText *text, int IgnoreSpaces) { if (!text || !text->last_line || !text->last_line->size) return TRUE; return HText_TrueEmptyLine(text->last_line, text, IgnoreSpaces); } int HText_LastLineOffset(HText *text) { if (!text || !text->last_line) return 0; return text->last_line->offset; } int HText_PreviousLineSize(HText *text, int IgnoreSpaces) { HTLine *line; if (!text || !text->last_line) return 0; if (!(line = text->last_line->prev)) return 0; return HText_TrueLineSize(line, text, IgnoreSpaces); } BOOL HText_PreviousLineEmpty(HText *text, int IgnoreSpaces) { HTLine *line; if (!text || !text->last_line) return TRUE; if (!(line = text->last_line->prev)) return TRUE; return HText_TrueEmptyLine(line, text, IgnoreSpaces); } /* * Compute the "true" line size. */ static int HText_TrueLineSize(HTLine *line, HText *text, int IgnoreSpaces) { size_t i; int true_size = 0; if (!(line && line->size)) return 0; if (IgnoreSpaces) { for (i = 0; i < line->size; i++) { if (!IsSpecialAttrChar(UCH(line->data[i])) && IS_UTF8_EXTRA(line->data[i]) && !isspace(UCH(line->data[i])) && UCH(line->data[i]) != HT_NON_BREAK_SPACE && UCH(line->data[i]) != HT_EN_SPACE) { true_size++; } } } else { for (i = 0; i < line->size; i++) { if (!IsSpecialAttrChar(line->data[i]) && IS_UTF8_EXTRA(line->data[i])) { true_size++; } } } return true_size; } /* * Tell if the line is really empty. This is invoked much more often than * HText_TrueLineSize(), and most lines are not empty. So it is faster to * do this check than to check if the line size happens to be zero. */ static BOOL HText_TrueEmptyLine(HTLine *line, HText *text, int IgnoreSpaces) { size_t i; if (!(line && line->size)) return TRUE; if (IgnoreSpaces) { for (i = 0; i < line->size; i++) { if (!IsSpecialAttrChar(UCH(line->data[i])) && IS_UTF8_EXTRA(line->data[i]) && !isspace(UCH(line->data[i])) && UCH(line->data[i]) != HT_NON_BREAK_SPACE && UCH(line->data[i]) != HT_EN_SPACE) { return FALSE; } } } else { for (i = 0; i < line->size; i++) { if (!IsSpecialAttrChar(line->data[i]) && IS_UTF8_EXTRA(line->data[i])) { return FALSE; } } } return TRUE; } void HText_NegateLineOne(HText *text) { if (text) { text->in_line_1 = NO; } return; } BOOL HText_inLineOne(HText *text) { if (text) { return text->in_line_1; } return YES; } /* * This function is for removing the first of two * successive blank lines. It should be called after * checking the situation with HText_LastLineSize() * and HText_PreviousLineSize(). Any characters in * the removed line (i.e., control characters, or it * wouldn't have tested blank) should have been * reiterated by split_line() in the retained blank * line. -FM */ void HText_RemovePreviousLine(HText *text) { HTLine *line, *previous; if (!(text && text->Lines > 1)) return; line = text->last_line->prev; previous = line->prev; previous->next = text->last_line; text->last_line->prev = previous; text->Lines--; freeHTLine(text, line); } /* * NOTE: This function presently is correct only if the * alignment is HT_LEFT. The offset is still zero, * because that's not determined for HT_CENTER or * HT_RIGHT until subsequent characters are received * and split_line() is called. -FM */ int HText_getCurrentColumn(HText *text) { int column = 0; BOOL IgnoreSpaces = FALSE; if (text) { column = ((text->in_line_1 ? (int) text->style->indent1st : (int) text->style->leftIndent) + (int) text->last_line->offset + HText_LastLineSize(text, IgnoreSpaces)); } return column; } int HText_getMaximumColumn(HText *text) { int column = DISPLAY_COLS; if (text) { column -= (int) text->style->rightIndent; } return column; } /* * NOTE: This function uses HText_getCurrentColumn() which * presently is correct only if the alignment is * HT_LEFT. -FM */ void HText_setTabID(HText *text, const char *name) { HTTabID *Tab = NULL; HTList *cur = text->tabs; HTList *last = NULL; if (!text || isEmpty(name)) return; if (!cur) { cur = text->tabs = HTList_new(); } else { while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) { if (Tab->name && !strcmp(Tab->name, name)) return; /* Already set. Keep the first value. */ last = cur; } if (last) cur = last; } if (!Tab) { /* New name. Create a new node */ Tab = typecalloc(HTTabID); if (Tab == NULL) outofmem(__FILE__, "HText_setTabID"); HTList_addObject(cur, Tab); StrAllocCopy(Tab->name, name); } assert(Tab != NULL); Tab->column = HText_getCurrentColumn(text); return; } int HText_getTabIDColumn(HText *text, const char *name) { int column = 0; HTTabID *Tab; HTList *cur = text->tabs; if (text && non_empty(name) && cur) { while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) { if (Tab->name && !strcmp(Tab->name, name)) break; } if (Tab) column = Tab->column; } return column; } /* * This function is for saving the address of a link * which had an attribute in the markup that resolved * to a URL (i.e., not just a NAME or ID attribute), * but was found in HText_endAnchor() to have no visible * content for use as a link name. It loads the address * into text->hidden_links, whose count can be determined * via HText_HiddenLinks(), below. The addresses can be * retrieved via HText_HiddenLinkAt(), below, based on * count. -FM */ static void HText_AddHiddenLink(HText *text, TextAnchor *textanchor) { HTAnchor *dest; /* * Make sure we have an HText structure and anchor. -FM */ if (!(text && textanchor && textanchor->anchor)) return; /* * Create the hidden links list * if it hasn't been already. -FM */ if (text->hidden_links == NULL) text->hidden_links = HTList_new(); /* * Store the address, in reverse list order * so that first in will be first out on * retrievals. -FM */ if ((dest = HTAnchor_followLink(textanchor->anchor)) && (text->hiddenlinkflag != HIDDENLINKS_IGNORE || HTList_isEmpty(text->hidden_links))) { char *value = HTAnchor_address(dest); BOOL ignore = FALSE; if (unique_urls) { int cnt; char *check; for (cnt = 0;; ++cnt) { check = (char *) HTList_objectAt(text->hidden_links, cnt); if (check == 0) break; if (!strcmp(check, value)) { ignore = TRUE; break; } } } if (ignore) { FREE(value); } else { HTList_appendObject(text->hidden_links, value); } } return; } /* * This function returns the number of addresses * that are loaded in text->hidden_links. -FM */ int HText_HiddenLinkCount(HText *text) { int count = 0; if (text && text->hidden_links) count = HTList_count((HTList *) text->hidden_links); return (count); } /* * This function returns the address, corresponding to * a hidden link, at the position (zero-based) in the * text->hidden_links list of the number argument. -FM */ const char *HText_HiddenLinkAt(HText *text, int number) { char *href = NULL; if (text && text->hidden_links && number >= 0) href = (char *) HTList_objectAt((HTList *) text->hidden_links, number); return (href); } /* * Form methods * These routines are used to build forms consisting * of input fields */ static BOOLEAN HTFormDisabled = FALSE; static PerFormInfo *HTCurrentForm; static BOOLEAN addFormAction(FormInfo * f) { BOOLEAN result = FALSE; if (HTCurrentForm != NULL) { result = TRUE; f->submit_action = NULL; StrAllocCopy(f->submit_action, HTCurrentForm->data.submit_action); if (HTCurrentForm->data.submit_enctype != NULL) StrAllocCopy(f->submit_enctype, HTCurrentForm->data.submit_enctype); if (HTCurrentForm->data.submit_title != NULL) StrAllocCopy(f->submit_title, HTCurrentForm->data.submit_title); f->submit_method = HTCurrentForm->data.submit_method; } return result; } void HText_beginForm(char *action, char *method, char *enctype, char *title, const char *accept_cs) { PerFormInfo *newform; int HTFormMethod = URL_GET_METHOD; char *HTFormAction = NULL; char *HTFormEnctype = NULL; char *HTFormTitle = NULL; char *HTFormAcceptCharset = NULL; HTFormNumber++; HTFormFields = 0; HTFormDisabled = FALSE; /* * Check the ACTION. -FM */ if (action != NULL) { if (isMAILTO_URL(action)) { HTFormMethod = URL_MAIL_METHOD; } StrAllocCopy(HTFormAction, action); } else StrAllocCopy(HTFormAction, HTLoadedDocumentURL()); /* * Check the METHOD. -FM */ if (method != NULL && HTFormMethod != URL_MAIL_METHOD) if (!strcasecomp(method, "post") || !strcasecomp(method, "pget")) HTFormMethod = URL_POST_METHOD; /* * Check the ENCTYPE. -FM */ if (non_empty(enctype)) { StrAllocCopy(HTFormEnctype, enctype); if (HTFormMethod != URL_MAIL_METHOD && !strncasecomp(enctype, "multipart/form-data", 19)) HTFormMethod = URL_POST_METHOD; } else { FREE(HTFormEnctype); } /* * Check the TITLE. -FM */ if (non_empty(title)) StrAllocCopy(HTFormTitle, title); else FREE(HTFormTitle); /* * Check for an ACCEPT_CHARSET. If present, store it and * convert to lowercase and collapse spaces. - kw */ if (accept_cs != NULL) { StrAllocCopy(HTFormAcceptCharset, accept_cs); LYRemoveBlanks(HTFormAcceptCharset); LYLowerCase(HTFormAcceptCharset); } /* * Create a new "PerFormInfo" structure to hold info on the current form. * This will be appended to the forms list kept by the HText object if and * when we reach a HText_endForm. */ newform = typecalloc(PerFormInfo); if (newform == NULL) outofmem(__FILE__, "HText_beginForm"); assert(newform != NULL); PerFormInfo_free(HTCurrentForm); /* shouldn't happen here - kw */ HTCurrentForm = newform; newform->number = HTFormNumber; newform->data.submit_action = HTFormAction; newform->data.submit_enctype = HTFormEnctype; newform->data.submit_method = HTFormMethod; newform->data.submit_title = HTFormTitle; newform->accept_cs = HTFormAcceptCharset; CTRACE((tfp, "BeginForm: action:%s Method:%d%s%s%s%s%s%s\n", HTFormAction, HTFormMethod, (HTFormTitle ? " Title:" : ""), NonNull(HTFormTitle), (HTFormEnctype ? " Enctype:" : ""), NonNull(HTFormEnctype), (HTFormAcceptCharset ? " Accept-charset:" : ""), NonNull(HTFormAcceptCharset))); } void HText_endForm(HText *text) { if (text != NULL) { if (HTFormFields == 1 && text->first_anchor) { /* * Support submission of a single text input field in * the form via instead of a submit button. -FM */ TextAnchor *a; /* * Go through list of anchors and get our input field. -FM */ for (a = text->first_anchor; a != NULL; a = a->next) { if (a->link_type == INPUT_ANCHOR && a->input_field->number == HTFormNumber && a->input_field->type != F_TEXTAREA_TYPE && F_TEXTLIKE(a->input_field->type)) { /* * Got it. Make it submitting. -FM */ if (addFormAction(a->input_field)) { a->input_field->type = F_TEXT_SUBMIT_TYPE; if (HTFormDisabled) a->input_field->disabled = TRUE; } break; } } } /* * Append info on the current form to the HText object's list of forms. * HText_beginInput call will have set some of the data in the * PerFormInfo structure (if there were any form fields at all). */ if (HTCurrentForm) { if (HTFormDisabled) HTCurrentForm->disabled = TRUE; if (!text->forms) text->forms = HTList_new(); HTList_appendObject(text->forms, HTCurrentForm); HTCurrentForm = NULL; } else { CTRACE((tfp, "endForm: HTCurrentForm is missing!\n")); } } else { CTRACE((tfp, "endForm: HText is missing!\n")); } FREE(HTCurSelectGroup); FREE(HTCurSelectGroupSize); FREE(HTCurSelectedOptionValue); HTFormFields = 0; HTFormDisabled = FALSE; } void HText_beginSelect(char *name, int name_cs, int multiple, char *size) { /* * Save the group name. */ StrAllocCopy(HTCurSelectGroup, name); HTCurSelectGroupCharset = name_cs; /* * If multiple then all options are actually checkboxes. */ if (multiple) HTCurSelectGroupType = F_CHECKBOX_TYPE; /* * If not multiple then all options are radio buttons. */ else HTCurSelectGroupType = F_RADIO_TYPE; /* * Length of an option list. */ StrAllocCopy(HTCurSelectGroupSize, size); CTRACE((tfp, "HText_beginSelect: name=%s type=%d size=%s\n", ((HTCurSelectGroup == NULL) ? "" : HTCurSelectGroup), HTCurSelectGroupType, ((HTCurSelectGroupSize == NULL) ? "" : HTCurSelectGroupSize))); CTRACE((tfp, "HText_beginSelect: name_cs=%d \"%s\"\n", HTCurSelectGroupCharset, (HTCurSelectGroupCharset >= 0 ? LYCharSet_UC[HTCurSelectGroupCharset].MIMEname : ""))); } /* * This function returns the number of the option whose * value currently is being accumulated for a select * block. - LE && FM */ int HText_getOptionNum(HText *text) { TextAnchor *a; OptionType *op; int n = 1; /* start count at 1 */ if (!(text && text->last_anchor)) return (0); a = text->last_anchor; if (!(a->link_type == INPUT_ANCHOR && a->input_field && a->input_field->type == F_OPTION_LIST_TYPE)) return (0); for (op = a->input_field->select_list; op; op = op->next) n++; CTRACE((tfp, "HText_getOptionNum: Got number '%d'.\n", n)); return (n); } /* * This function checks for a numbered option pattern * as the prefix for an option value. If present, and * we are in the correct keypad mode, it returns a * pointer to the actual value, following that prefix. * Otherwise, it returns the original pointer. */ static char *HText_skipOptionNumPrefix(char *opname) { /* * Check if we are in the correct keypad mode. */ if (fields_are_numbered()) { /* * Skip the option number embedded in the option name so the * extra chars won't mess up cgi scripts processing the value. * The format is (nnn)__ where nnn is a number and there is a * minimum of 5 chars (no underscores if (nnn) exceeds 5 chars). * See HTML.c. If the chars don't exactly match this format, * just use all of opname. - LE */ char *cp = opname; if ((non_empty(cp) && *cp++ == '(') && *cp && isdigit(UCH(*cp++))) { while (*cp && isdigit(UCH(*cp))) ++cp; if (*cp && *cp++ == ')') { int i = (int) (cp - opname); while (i < 5) { if (*cp != '_') break; i++; cp++; } if (i < 5) { cp = opname; } } else { cp = opname; } } else { cp = opname; } return (cp); } return (opname); } /* * We couldn't set the value field for the previous option tag so we have to do * it now. Assume that the last anchor was the previous options' tag. */ char *HText_setLastOptionValue(HText *text, char *value, char *submit_value, int order, int checked, int val_cs, int submit_val_cs) { char *cp, *cp1; char *ret_Value = NULL; unsigned char *tmp = NULL; int number = 0, i, j; if (!(value && text && text->last_anchor && text->last_anchor->input_field && text->last_anchor->link_type == INPUT_ANCHOR)) { CTRACE((tfp, "HText_setLastOptionValue: invalid call! value:%s!\n", (value ? value : ""))); return NULL; } CTRACE((tfp, "Entering HText_setLastOptionValue: value:\"%s\", checked:%s\n", value, (checked ? "on" : "off"))); /* * Strip end spaces, newline is also whitespace. */ if (*value) { cp = &value[strlen(value) - 1]; while ((cp >= value) && (isspace(UCH(*cp)) || IsSpecialAttrChar(UCH(*cp)))) cp--; *(cp + 1) = '\0'; } /* * Find first non space */ cp = value; while (isspace(UCH(*cp)) || IsSpecialAttrChar(UCH(*cp))) cp++; if (HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups && fields_are_numbered()) { /* * Collapse any space between the popup option * prefix and actual value. -FM */ if ((cp1 = HText_skipOptionNumPrefix(cp)) > cp) { i = 0, j = (int) (cp1 - cp); while (isspace(UCH(cp1[i])) || IsSpecialAttrChar(UCH(cp1[i]))) { i++; } if (i > 0) { while (cp1[i] != '\0') cp[j++] = cp1[i++]; cp[j] = '\0'; } } } if (HTCurSelectGroupType == F_CHECKBOX_TYPE) { StrAllocCopy(text->last_anchor->input_field->value, cp); text->last_anchor->input_field->value_cs = val_cs; /* * Put the text on the screen as well. */ HText_appendText(text, cp); } else if (LYSelectPopups == FALSE) { StrAllocCopy(text->last_anchor->input_field->value, (submit_value ? submit_value : cp)); text->last_anchor->input_field->value_cs = (submit_value ? submit_val_cs : val_cs); /* * Put the text on the screen as well. */ HText_appendText(text, cp); } else { /* * Create a linked list of option values. */ OptionType *op_ptr = text->last_anchor->input_field->select_list; OptionType *new_ptr = NULL; BOOLEAN first_option = FALSE; /* * Deal with newlines or tabs. */ LYReduceBlanks(value); if (!op_ptr) { /* * No option items yet. */ if (text->last_anchor->input_field->type != F_OPTION_LIST_TYPE) { CTRACE((tfp, "HText_setLastOptionValue: last input_field not F_OPTION_LIST_TYPE (%d)\n", F_OPTION_LIST_TYPE)); CTRACE((tfp, " but %d, ignoring!\n", text->last_anchor->input_field->type)); return NULL; } new_ptr = typecalloc(OptionType); if (new_ptr == NULL) outofmem(__FILE__, "HText_setLastOptionValue"); text->last_anchor->input_field->select_list = new_ptr; first_option = TRUE; } else { while (op_ptr->next) { number++; op_ptr = op_ptr->next; } number++; /* add one more */ op_ptr->next = new_ptr = typecalloc(OptionType); if (new_ptr == NULL) outofmem(__FILE__, "HText_setLastOptionValue"); } assert(new_ptr != NULL); new_ptr->name = NULL; new_ptr->cp_submit_value = NULL; new_ptr->next = NULL; /* * Find first non-space again, convert_to_spaces above may have * changed the string. - kw */ cp = value; while (isspace(UCH(*cp)) || IsSpecialAttrChar(UCH(*cp))) cp++; for (i = 0, j = 0; cp[i]; i++) { if (cp[i] == HT_NON_BREAK_SPACE || cp[i] == HT_EN_SPACE) { cp[j++] = ' '; } else if (cp[i] != LY_SOFT_HYPHEN && !IsSpecialAttrChar(UCH(cp[i]))) { cp[j++] = cp[i]; } } cp[j] = '\0'; if (IS_CJK_TTY) { if ((tmp = typecallocn(unsigned char, strlen(cp) * 2 + 1)) != 0) { if (kanji_code == EUC) { TO_EUC((unsigned char *) cp, tmp); val_cs = current_char_set; } else if (kanji_code == SJIS) { TO_SJIS((unsigned char *) cp, tmp); val_cs = current_char_set; } else { for (i = 0, j = 0; cp[i]; i++) { if (cp[i] != CH_ESC) { /* S/390 -- gil -- 1604 */ tmp[j++] = UCH(cp[i]); } } } StrAllocCopy(new_ptr->name, (const char *) tmp); FREE(tmp); } else { outofmem(__FILE__, "HText_setLastOptionValue"); } } else { StrAllocCopy(new_ptr->name, cp); } StrAllocCopy(new_ptr->cp_submit_value, (submit_value ? submit_value : HText_skipOptionNumPrefix(new_ptr->name))); new_ptr->value_cs = (submit_value ? submit_val_cs : val_cs); if (first_option) { FormInfo *last_input = text->last_anchor->input_field; StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name); last_input->num_value = 0; /* * If this is the first option in a popup select list, * HText_beginInput may have allocated the value and * cp_submit_value fields, so free them now to avoid * a memory leak. - kw */ FREE(last_input->value); FREE(last_input->cp_submit_value); last_input->value = last_input->select_list->name; last_input->orig_value = last_input->select_list->name; last_input->cp_submit_value = last_input->select_list->cp_submit_value; last_input->orig_submit_value = last_input->select_list->cp_submit_value; last_input->value_cs = new_ptr->value_cs; } else { int newlen = (int) strlen(new_ptr->name); int curlen = (int) (HTCurSelectedOptionValue ? strlen(HTCurSelectedOptionValue) : 0); /* * Make the selected Option Value as long as * the longest option. */ if (newlen > curlen) StrAllocCat(HTCurSelectedOptionValue, UNDERSCORES(newlen - curlen)); } if (checked) { int curlen = (int) strlen(new_ptr->name); int newlen = (HTCurSelectedOptionValue ? (int) strlen(HTCurSelectedOptionValue) : 0); FormInfo *last_input = text->last_anchor->input_field; /* * Set the default option as this one. */ last_input->num_value = number; last_input->value = new_ptr->name; last_input->orig_value = new_ptr->name; last_input->cp_submit_value = new_ptr->cp_submit_value; last_input->orig_submit_value = new_ptr->cp_submit_value; last_input->value_cs = new_ptr->value_cs; StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name); if (newlen > curlen) StrAllocCat(HTCurSelectedOptionValue, UNDERSCORES(newlen - curlen)); } /* * Return the selected Option value to be sent to the screen. */ if (order == LAST_ORDER) { /* * Change the value. */ if (HTCurSelectedOptionValue == 0) StrAllocCopy(HTCurSelectedOptionValue, ""); text->last_anchor->input_field->size = (int) strlen(HTCurSelectedOptionValue); ret_Value = HTCurSelectedOptionValue; } } if (TRACE) { CTRACE((tfp, "HText_setLastOptionValue:%s value=\"%s\"\n", (order == LAST_ORDER) ? " LAST_ORDER" : "", value)); CTRACE((tfp, " val_cs=%d \"%s\"", val_cs, (val_cs >= 0 ? LYCharSet_UC[val_cs].MIMEname : ""))); if (submit_value) { CTRACE((tfp, " (submit_val_cs %d \"%s\") submit_value%s=\"%s\"\n", submit_val_cs, (submit_val_cs >= 0 ? LYCharSet_UC[submit_val_cs].MIMEname : ""), (HTCurSelectGroupType == F_CHECKBOX_TYPE) ? "(ignored)" : "", submit_value)); } else { CTRACE((tfp, "\n")); } } return (ret_Value); } /* * Assign a form input anchor. * Returns the number of characters to leave * blank so that the input field can fit. */ int HText_beginInput(HText *text, int underline, InputFieldData * I) { TextAnchor *a; FormInfo *f; const char *cp_option = NULL; char *IValue = NULL; unsigned char *tmp = NULL; int i, j; int adjust_marker = 0; int MaximumSize; char marker[16]; CTRACE((tfp, "GridText: Entering HText_beginInput type=%s\n", NonNull(I->type))); POOLtypecalloc(TextAnchor, a); POOLtypecalloc(FormInfo, f); if (a == NULL || f == NULL) outofmem(__FILE__, "HText_beginInput"); assert(a != NULL); assert(f != NULL); a->sgml_offset = SGML_offset(); a->inUnderline = (BOOLEAN) underline; a->line_num = text->Lines; a->line_pos = (short) text->last_line->size; /* * If this is a radio button, or an OPTION we're converting * to a radio button, and it's the first with this name, make * sure it's checked by default. Otherwise, if it's checked, * uncheck the default or any preceding radio button with this * name that was checked. -FM */ if (I->type != NULL && !strcmp(I->type, "OPTION") && HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups == FALSE) { I->type = "RADIO"; I->name = HTCurSelectGroup; I->name_cs = HTCurSelectGroupCharset; } if (I->name && I->type && !strcasecomp(I->type, "radio")) { if (!text->last_anchor) { I->checked = TRUE; } else { TextAnchor *b; int i2 = 0; for (b = text->first_anchor; b != NULL; b = b->next) { if (b->link_type == INPUT_ANCHOR && b->input_field->type == F_RADIO_TYPE && b->input_field->number == HTFormNumber) { if (!strcmp(b->input_field->name, I->name)) { if (I->checked && b->input_field->num_value) { b->input_field->num_value = 0; StrAllocCopy(b->input_field->orig_value, "0"); break; } i2++; } } } if (i2 == 0) I->checked = TRUE; } } a->next = 0; a->anchor = NULL; a->link_type = INPUT_ANCHOR; a->show_anchor = YES; LYClearHiText(a); a->extent = 2; a->input_field = f; f->select_list = 0; f->number = HTFormNumber; f->disabled = HTFormDisabled || I->disabled; f->readonly = I->readonly; f->no_cache = NO; HTFormFields++; /* * Set up VALUE. */ if (I->value) StrAllocCopy(IValue, I->value); if (IValue && IS_CJK_TTY && ((I->type == NULL) || strcasecomp(I->type, "hidden"))) { if ((tmp = typecallocn(unsigned char, strlen(IValue) * 2 + 1)) != 0) { if (kanji_code == EUC) { TO_EUC((unsigned char *) IValue, tmp); I->value_cs = current_char_set; } else if (kanji_code == SJIS) { TO_SJIS((unsigned char *) IValue, tmp); I->value_cs = current_char_set; } else { for (i = 0, j = 0; IValue[i]; i++) { if (IValue[i] != CH_ESC) { /* S/390 -- gil -- 1621 */ tmp[j++] = UCH(IValue[i]); } } } StrAllocCopy(IValue, (const char *) tmp); FREE(tmp); } } /* * Special case of OPTION. * Is handled above if radio type and LYSelectPopups is FALSE. */ /* set the values and let the parsing below do the work */ if (I->type != NULL && !strcmp(I->type, "OPTION")) { cp_option = I->type; if (HTCurSelectGroupType == F_RADIO_TYPE) I->type = "OPTION_LIST"; else I->type = "CHECKBOX"; I->name = HTCurSelectGroup; I->name_cs = HTCurSelectGroupCharset; /* * The option's size parameter actually gives the length and not * the width of the list. Perform the conversion here * and get rid of the allocated HTCurSelect.... * 0 is ok as it means any length (arbitrary decision). */ if (HTCurSelectGroupSize != NULL) { f->size_l = atoi(HTCurSelectGroupSize); FREE(HTCurSelectGroupSize); } } /* * Set SIZE. */ if (I->size != 0) { f->size = I->size; /* * Leave at zero for option lists. */ if (f->size == 0 && cp_option == NULL) { f->size = 20; /* default */ } } else { f->size = 20; /* default */ } /* * Set MAXLENGTH. */ if (I->maxlength != NULL) { f->maxlength = (unsigned) atoi(I->maxlength); } else { f->maxlength = 0; /* 0 means infinite */ } /* * Set CHECKED * (num_value is only relevant to check and radio types). */ if (I->checked == TRUE) f->num_value = 1; else f->num_value = 0; /* * Set TYPE. */ if (I->type != NULL) { if (!strcasecomp(I->type, "password")) { f->type = F_PASSWORD_TYPE; } else if (!strcasecomp(I->type, "checkbox")) { f->type = F_CHECKBOX_TYPE; } else if (!strcasecomp(I->type, "radio")) { f->type = F_RADIO_TYPE; } else if (!strcasecomp(I->type, "submit")) { f->type = F_SUBMIT_TYPE; } else if (!strcasecomp(I->type, "image")) { f->type = F_IMAGE_SUBMIT_TYPE; } else if (!strcasecomp(I->type, "reset")) { f->type = F_RESET_TYPE; } else if (!strcasecomp(I->type, "OPTION_LIST")) { f->type = F_OPTION_LIST_TYPE; } else if (!strcasecomp(I->type, "hidden")) { f->type = F_HIDDEN_TYPE; HTFormFields--; f->size = 0; } else if (!strcasecomp(I->type, "textarea")) { f->type = F_TEXTAREA_TYPE; } else if (!strcasecomp(I->type, "range")) { f->type = F_RANGE_TYPE; } else if (!strcasecomp(I->type, "file")) { f->type = F_FILE_TYPE; CTRACE((tfp, "ok, got a file uploader\n")); } else if (!strcasecomp(I->type, "keygen")) { f->type = F_KEYGEN_TYPE; } else if (!strcasecomp(I->type, "button")) { f->type = F_BUTTON_TYPE; } else { /* * Note that TYPE="scribble" defaults to TYPE="text". -FM */ f->type = F_TEXT_TYPE; /* default */ } } else { f->type = F_TEXT_TYPE; } /* * Set NAME. */ if (I->name != NULL) { StrAllocCopy(f->name, I->name); f->name_cs = I->name_cs; } else { if (f->type == F_RESET_TYPE || f->type == F_SUBMIT_TYPE || f->type == F_IMAGE_SUBMIT_TYPE) { /* * Set name to empty string. */ StrAllocCopy(f->name, ""); } else { /* * Error! NAME must be present. */ CTRACE((tfp, "GridText: No name present in input field; not displaying\n")); FREE(IValue); return (0); } } /* * Add this anchor to the anchor list */ if (text->last_anchor) { text->last_anchor->next = a; } else { text->first_anchor = a; } /* * Set VALUE, if it exists. Otherwise, if it's not * an option list make it a zero-length string. -FM */ if (IValue != NULL) { /* * OPTION VALUE is not actually the value to be seen but is to * be sent.... */ if (f->type == F_OPTION_LIST_TYPE || f->type == F_CHECKBOX_TYPE) { /* * Fill both with the value. The f->value may be * overwritten in HText_setLastOptionValue.... */ StrAllocCopy(f->value, IValue); StrAllocCopy(f->cp_submit_value, IValue); } else { StrAllocCopy(f->value, IValue); } f->value_cs = I->value_cs; } else if (f->type != F_OPTION_LIST_TYPE) { StrAllocCopy(f->value, ""); /* * May be an empty INPUT field. The text entered will then * probably be in the current display character set. - kw */ f->value_cs = current_char_set; } /* * Run checks and fill in necessary values. */ if (f->type == F_RESET_TYPE) { if (non_empty(f->value)) { f->size = (int) strlen(f->value); } else { StrAllocCopy(f->value, "Reset"); f->size = 5; } } else if (f->type == F_BUTTON_TYPE) { if (non_empty(f->value)) { f->size = (int) strlen(f->value); } else { StrAllocCopy(f->value, "BUTTON"); f->size = 5; } } else if (f->type == F_IMAGE_SUBMIT_TYPE || f->type == F_SUBMIT_TYPE) { if (non_empty(f->value)) { f->size = (int) strlen(f->value); } else if (f->type == F_IMAGE_SUBMIT_TYPE) { StrAllocCopy(f->value, "[IMAGE]-Submit"); f->size = 14; } else { StrAllocCopy(f->value, "Submit"); f->size = 6; } addFormAction(f); } else if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE) { f->size = 3; if (IValue == NULL) StrAllocCopy(f->value, (f->type == F_CHECKBOX_TYPE ? "on" : "")); } FREE(IValue); /* * Set original values. */ if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE) { if (f->num_value) StrAllocCopy(f->orig_value, "1"); else StrAllocCopy(f->orig_value, "0"); } else if (f->type == F_OPTION_LIST_TYPE) { f->orig_value = NULL; } else { StrAllocCopy(f->orig_value, f->value); } /* * Store accept-charset if present, converting to lowercase * and collapsing spaces. - kw */ if (I->accept_cs) { StrAllocCopy(f->accept_cs, I->accept_cs); LYRemoveBlanks(f->accept_cs); LYLowerCase(f->accept_cs); } /* * Add numbers to form fields if needed. - LE & FM */ switch (f->type) { /* * Do not supply number for hidden fields, nor * for types that are not yet implemented. */ case F_HIDDEN_TYPE: #ifndef USE_FILE_UPLOAD case F_FILE_TYPE: #endif case F_RANGE_TYPE: case F_KEYGEN_TYPE: case F_BUTTON_TYPE: a->number = 0; break; default: if (fields_are_numbered()) a->number = ++(text->last_anchor_number); else a->number = 0; break; } if (fields_are_numbered() && (a->number > 0)) { if (HTMainText != 0) { HText_findAnchorNumber(a); } else { a->show_number = a->number; } sprintf(marker, "[%d]", a->show_number); adjust_marker = (int) strlen(marker); if (number_fields_on_left) { BOOL had_bracket = (BOOL) (f->type == F_OPTION_LIST_TYPE); HText_appendText(text, had_bracket ? (marker + 1) : marker); if (had_bracket) HText_appendCharacter(text, '['); } a->line_num = text->Lines; a->line_pos = (short) text->last_line->size; } else { *marker = '\0'; } /* * Restrict SIZE to maximum allowable size. */ MaximumSize = WRAP_COLS(text) + 1 - adjust_marker; switch (f->type) { case F_SUBMIT_TYPE: case F_IMAGE_SUBMIT_TYPE: case F_RESET_TYPE: case F_TEXT_TYPE: case F_TEXTAREA_TYPE: /* * For submit and reset buttons, and for text entry * fields and areas, we limit the size element to that * of one line for the current style because that's * the most we could highlight on overwrites, and/or * handle in the line editor. The actual values for * text entry lines can be long, and will be scrolled * horizontally within the editing window. -FM */ MaximumSize -= (1 + (int) text->style->leftIndent + (int) text->style->rightIndent); /* If we are numbering form links, place is taken by [nn] */ if (fields_are_numbered()) { if (!number_fields_on_left && f->type == F_TEXT_TYPE && MaximumSize > a->line_pos + 10) MaximumSize -= a->line_pos; else MaximumSize -= (int) strlen(marker); } /* * Save value for submit/reset buttons so they * will be visible when printing the page. - LE */ I->value = f->value; break; default: /* * For all other fields we limit the size element to * 10 less than the screen width, because either they * are types with small placeholders, and/or are a * type which is handled via a popup window. -FM */ MaximumSize -= 10; break; } if (MaximumSize < 1) MaximumSize = 1; if (f->size > MaximumSize) f->size = MaximumSize; /* * Add this anchor to the anchor list */ text->last_anchor = a; if (HTCurrentForm) { /* should always apply! - kw */ if (!HTCurrentForm->first_field) { HTCurrentForm->first_field = f; } HTCurrentForm->last_field = f; HTCurrentForm->nfields++; /* will count hidden fields - kw */ /* * Set the no_cache flag if the METHOD is POST. -FM */ if (HTCurrentForm->data.submit_method == URL_POST_METHOD) f->no_cache = TRUE; /* * Propagate form field's accept-charset attribute to enclosing * form if the form itself didn't have an accept-charset - kw */ if (f->accept_cs && !HTCurrentForm->accept_cs) { StrAllocCopy(HTCurrentForm->accept_cs, f->accept_cs); } if (!text->forms) { text->forms = HTList_new(); } } else { CTRACE((tfp, "beginInput: HTCurrentForm is missing!\n")); } CTRACE((tfp, "Input link: name=%s\nvalue=%s\nsize=%d\n", f->name, NonNull(f->value), f->size)); CTRACE((tfp, "Input link: name_cs=%d \"%s\" (from %d \"%s\")\n", f->name_cs, (f->name_cs >= 0 ? LYCharSet_UC[f->name_cs].MIMEname : ""), I->name_cs, (I->name_cs >= 0 ? LYCharSet_UC[I->name_cs].MIMEname : ""))); CTRACE((tfp, " value_cs=%d \"%s\" (from %d \"%s\")\n", f->value_cs, (f->value_cs >= 0 ? LYCharSet_UC[f->value_cs].MIMEname : ""), I->value_cs, (I->value_cs >= 0 ? LYCharSet_UC[I->value_cs].MIMEname : ""))); /* * Return the SIZE of the input field. */ if (I->size && f->size > adjust_marker) { f->size -= adjust_marker; } return (f->size); } /* * If we're numbering fields on the right, do it. Note that some fields may * be too long for the line - we'll lose the marker in that case rather than * truncate the field. */ void HText_endInput(HText *text) { if (fields_are_numbered() && !number_fields_on_left && text != NULL && text->last_anchor != NULL && text->last_anchor->number > 0) { char marker[20]; sprintf(marker, "[%d]", text->last_anchor->show_number); HText_appendText(text, marker); } } /* * Get a translation (properly: transcoding) quality, factoring in * our ability to translate (an UCTQ_t) and a possible q parameter * on the given charset string, for cs_from -> givenmime. * The parsed input string will be mutilated on exit(!). * Note that results are not normalised to 1.0, but results from * different calls of this function can be compared. - kw * * Obsolete, it was planned to use here a quality parametr UCTQ_t, * which is boolean now. */ static double get_trans_q(int cs_from, char *givenmime) { double df = 1.0; BOOL tq; char *p; if (!givenmime || !(*givenmime)) return 0.0; if ((p = StrChr(givenmime, ';')) != NULL) { *p++ = '\0'; } if (!strcmp(givenmime, "*")) tq = UCCanTranslateFromTo(cs_from, UCGetLYhndl_byMIME("utf-8")); else tq = UCCanTranslateFromTo(cs_from, UCGetLYhndl_byMIME(givenmime)); if (!tq) return 0.0; if (non_empty(p)) { char *pair, *field = p, *pval, *ptok; /* Get all the parameters to the Charset */ while ((pair = HTNextTok(&field, ";", "\"", NULL)) != NULL) { if ((ptok = HTNextTok(&pair, "= ", NULL, NULL)) != NULL && (pval = HTNextField(&pair)) != NULL) { if (0 == strcasecomp(ptok, "q")) { df = strtod(pval, NULL); break; } } } return (df * tq); } else { return tq; } } /* * Find the best charset for submission, if we have an ACCEPT_CHARSET * list. It factors in how well we can translate (just as guess, and * not a very good one..) and possible ";q=" factors. Yes this is * more general than it needs to be here. * * Input is cs_in and acceptstring. * * Will return charset handle as int. * best_csname will point to a newly allocated MIME string for the * charset corresponding to the return value if return value >= 0. * - kw */ static int find_best_target_cs(char **best_csname, int cs_from, const char *acceptstring) { char *paccept = NULL; double bestq = -1.0; char *bestmime = NULL; char *field, *nextfield; StrAllocCopy(paccept, acceptstring); nextfield = paccept; while ((field = HTNextTok(&nextfield, ",", "\"", NULL)) != NULL) { double q; if (*field != '\0') { /* Get the Charset */ q = get_trans_q(cs_from, field); if (q > bestq) { bestq = q; bestmime = field; } } } if (bestmime) { if (!strcmp(bestmime, "*")) /* non-standard for HTML attribute.. */ StrAllocCopy(*best_csname, "utf-8"); else StrAllocCopy(*best_csname, bestmime); FREE(paccept); if (bestq > 0) return (UCGetLYhndl_byMIME(*best_csname)); else return (-1); } FREE(paccept); return (-1); } #ifdef USE_FILE_UPLOAD static void load_a_file(const char *val_used, bstring **result) { FILE *fd; size_t bytes; char buffer[BUFSIZ + 1]; CTRACE((tfp, "Ok, about to convert \"%s\" to mime/thingy\n", val_used)); if (*val_used) { /* ignore empty form field */ if ((fd = fopen(val_used, BIN_R)) == 0) { HTAlert(gettext("Can't open file for uploading")); } else { while ((bytes = fread(buffer, sizeof(char), BUFSIZ, fd)) != 0) { HTSABCat(result, buffer, (int) bytes); } LYCloseInput(fd); } } } static const char *guess_content_type(const char *filename) { HTAtom *encoding; const char *desc; HTFormat format = HTFileFormat(filename, &encoding, &desc); return (format != 0 && non_empty(format->name)) ? format->name : "text/plain"; } #endif /* USE_FILE_UPLOAD */ static void cannot_transcode(BOOL *had_warning, const char *target_csname) { if (*had_warning == NO) { *had_warning = YES; _user_message(CANNOT_TRANSCODE_FORM, target_csname ? target_csname : "UNKNOWN"); LYSleepAlert(); } } #define SPECIAL_8BIT 1 #define SPECIAL_FORM 2 static unsigned check_form_specialchars(const char *value) { unsigned result = 0; const char *p; for (p = value; non_empty(p) && (result != (SPECIAL_8BIT | SPECIAL_FORM)); p++) { if ((*p == HT_NON_BREAK_SPACE) || (*p == HT_EN_SPACE) || (*p == LY_SOFT_HYPHEN)) { result |= SPECIAL_FORM; } else if ((*p & 0x80) != 0) { result |= SPECIAL_8BIT; } } return result; } /* * Scan the given data, adding characters to the MIME-boundary to keep it from * matching any part of the data. */ static void UpdateBoundary(char **Boundary, bstring *data) { size_t j; size_t have = strlen(*Boundary); size_t last = (size_t) BStrLen(data); char *text = BStrData(data); char *want = *Boundary; assert(want != NULL); assert(text != NULL); for (j = 0; (long) j <= (long) (last - have); ++j) { if (want[0] == text[j] && !memcmp(want, text + j, have)) { char temp[2]; temp[0] = (char) (isdigit(UCH(text[have + j])) ? 'a' : '0'); temp[1] = '\0'; StrAllocCat(want, temp); ++have; } } *Boundary = want; } /* * Convert a string to base64 */ static char *convert_to_base64(const char *src, size_t len) { #define B64_LINE 76 static const char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char *dest; size_t rlen; /* length of result string */ unsigned char c1, c2, c3; const char *eol; char *r; const char *str; size_t eollen; int chunk; str = src; eol = "\n"; eollen = 1; /* calculate the length of the result */ rlen = (len + 2) / 3 * 4; /* encoded bytes */ if (rlen) { /* add space for EOL */ rlen += ((rlen - 1) / B64_LINE + 1) * eollen; } /* allocate a result buffer */ if ((dest = (char *) malloc(rlen + 1)) == NULL) { outofmem(__FILE__, "convert_to_base64"); } assert(dest != NULL); r = dest; /* encode */ for (chunk = 0; len > 0; len -= 3, chunk++) { if (chunk == (B64_LINE / 4)) { const char *c = eol; const char *e = eol + eollen; while (c < e) *r++ = *c++; chunk = 0; } c1 = UCH(*str++); c2 = UCH(*str++); *r++ = basis_64[c1 >> 2]; *r++ = basis_64[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)]; if (len > 2) { c3 = UCH(*str++); *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)]; *r++ = basis_64[c3 & 0x3F]; } else if (len == 2) { *r++ = basis_64[(c2 & 0xF) << 2]; *r++ = '='; } else { /* len == 1 */ *r++ = '='; *r++ = '='; } } if (rlen) { /* append eol to the result string */ const char *c = eol; const char *e = eol + eollen; while (c < e) *r++ = *c++; } *r = '\0'; return dest; } typedef enum { NO_QUOTE /* no quoting needed */ ,QUOTE_MULTI /* multipart */ ,QUOTE_BASE64 /* encode as base64 */ ,QUOTE_SPECIAL /* escape special characters only */ } QuoteData; typedef struct { int type; /* the type of this field */ BOOL first; /* true if this begins a submission part */ char *name; /* the name of this field */ char *value; /* the nominal value of this field */ bstring *data; /* its data, which is usually the same as the value */ QuoteData quote; /* how to quote/translate the data */ } PostData; static char *escape_or_quote_name(const char *name, QuoteData quoting, const char *MultipartContentType) { char *escaped1 = NULL; switch (quoting) { case NO_QUOTE: StrAllocCopy(escaped1, name); break; case QUOTE_MULTI: case QUOTE_BASE64: StrAllocCopy(escaped1, "Content-Disposition: form-data"); HTSprintf(&escaped1, "; name=\"%s\"", name); if (MultipartContentType) HTSprintf(&escaped1, MultipartContentType, "text/plain"); if (quoting == QUOTE_BASE64) StrAllocCat(escaped1, "\r\nContent-Transfer-Encoding: base64"); StrAllocCat(escaped1, "\r\n\r\n"); break; case QUOTE_SPECIAL: escaped1 = HTEscapeSP(name, URL_XALPHAS); break; } return escaped1; } static char *escape_or_quote_value(const char *value, QuoteData quoting) { char *escaped2 = NULL; switch (quoting) { case NO_QUOTE: case QUOTE_MULTI: StrAllocCopy(escaped2, NonNull(value)); break; case QUOTE_BASE64: /* FIXME: this is redundant */ escaped2 = convert_to_base64(value, strlen(value)); break; case QUOTE_SPECIAL: escaped2 = HTEscapeSP(value, URL_XALPHAS); break; } return escaped2; } /* * Check if we should encode the data in base64. We can, only if we're using * a multipart content type. We should, if we're sending mail and the data * contains long lines or nonprinting characters. */ static int check_if_base64_needed(int submit_method, bstring *data) { int width = 0; BOOL printable = TRUE; char *text = BStrData(data); if (text != 0) { int col = 0; int n; int length = BStrLen(data); for (n = 0; n < length; ++n) { int ch = UCH(text[n]); if (is8bits(ch) || ((ch < 32 && ch != '\n'))) { CTRACE((tfp, "nonprintable %d:%#x\n", n, ch)); printable = FALSE; } if (ch == '\n' || ch == '\r') { if (width < col) width = col; col = 0; } else { ++col; } } if (width < col) width = col; } return !printable && ((submit_method == URL_MAIL_METHOD) && (width > 72)); } PerFormInfo *HText_PerFormInfo(int number) { return (PerFormInfo *) HTList_objectAt(HTMainText->forms, number - 1); } /* * HText_SubmitForm - generate submit data from form fields. * For mailto forms, send the data. * For other methods, set fields in structure pointed to by doc * appropriately for next request. * Returns 1 if *doc set appropriately for next request, * 0 otherwise. - kw */ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc, const char *link_name, const char *link_value) { BOOL had_chartrans_warning = NO; BOOL have_accept_cs = NO; BOOL success; BOOLEAN PlainText = FALSE; BOOLEAN SemiColon = FALSE; BOOL skip_field = FALSE; const char *out_csname; const char *target_csname = NULL; PerFormInfo *thisform; PostData *my_data = NULL; TextAnchor *anchor_ptr; bstring *my_query = NULL; char *Boundary = NULL; char *MultipartContentType = NULL; char *content_type_out = NULL; char *copied_name_used = NULL; char *copied_val_used = NULL; char *escaped1 = NULL; char *escaped2 = NULL; char *last_textarea_name = NULL; const char *name_used = ""; char *previous_blanks = NULL; const char *val_used = ""; int anchor_count = 0; int anchor_limit = 0; int form_number = submit_item->number; int result = 0; int target_cs = -1; int textarea_lineno = 0; unsigned form_is_special = 0; CTRACE((tfp, "SubmitForm\n link_name=%s\n link_value=%s\n", link_name, link_value)); if (!HTMainText) return 0; thisform = HText_PerFormInfo(form_number); /* Sanity check */ if (!thisform) { CTRACE((tfp, "SubmitForm: form %d not in HTMainText's list!\n", form_number)); } else if (thisform->number != form_number) { CTRACE((tfp, "SubmitForm: failed sanity check, %d!=%d !\n", thisform->number, form_number)); thisform = NULL; } if (isEmpty(submit_item->submit_action)) { CTRACE((tfp, "SubmitForm: no action given\n")); return 0; } /* * If we're mailing, make sure it's a mailto ACTION. -FM */ if ((submit_item->submit_method == URL_MAIL_METHOD) && !isMAILTO_URL(submit_item->submit_action)) { HTAlert(BAD_FORM_MAILTO); return 0; } /* * Check the ENCTYPE and set up the appropriate variables. -FM */ if (submit_item->submit_enctype && !strncasecomp(submit_item->submit_enctype, "text/plain", 10)) { /* * Do not hex escape, and use physical newlines * to separate name=value pairs. -FM */ PlainText = TRUE; } else if (submit_item->submit_enctype && !strncasecomp(submit_item->submit_enctype, "application/sgml-form-urlencoded", 32)) { /* * Use semicolons instead of ampersands as the * separators for name=value pairs. -FM */ SemiColon = TRUE; } else if (submit_item->submit_enctype && !strncasecomp(submit_item->submit_enctype, "multipart/form-data", 19)) { /* * Use the multipart MIME format. Later we will ensure it does not * occur within the content. */ StrAllocCopy(Boundary, "xnyLAaB03X"); } /* * Determine in what character encoding (aka. charset) we should * submit. We call this target_cs and the MIME name for it * target_csname. * TODO: - actually use ACCEPT-CHARSET stuff from FORM * TODO: - deal with list in ACCEPT-CHARSET, find a "best" * charset to submit */ /* Look at ACCEPT-CHARSET on the submitting field if we have one. */ if (thisform && submit_item->accept_cs && strcasecomp(submit_item->accept_cs, "UNKNOWN")) { have_accept_cs = YES; target_cs = find_best_target_cs(&thisform->thisacceptcs, current_char_set, submit_item->accept_cs); } /* Look at ACCEPT-CHARSET on form as a whole if submitting field * didn't have one. */ if (thisform && !have_accept_cs && thisform->accept_cs && strcasecomp(thisform->accept_cs, "UNKNOWN")) { have_accept_cs = YES; target_cs = find_best_target_cs(&thisform->thisacceptcs, current_char_set, thisform->accept_cs); } if (have_accept_cs && (target_cs >= 0) && thisform->thisacceptcs) { target_csname = thisform->thisacceptcs; } if (target_cs < 0 && non_empty(HTMainText->node_anchor->charset)) { target_cs = UCGetLYhndl_byMIME(HTMainText->node_anchor->charset); if (target_cs >= 0) { target_csname = HTMainText->node_anchor->charset; } else { target_cs = UCLYhndl_for_unspec; /* always >= 0 */ target_csname = LYCharSet_UC[target_cs].MIMEname; } } if (target_cs < 0) { target_cs = UCLYhndl_for_unspec; /* always >= 0 */ } /* * Go through list of anchors and get a "max." charset parameter - kw */ for (anchor_ptr = HTMainText->first_anchor; anchor_ptr != NULL; anchor_ptr = anchor_ptr->next) { if (anchor_ptr->link_type != INPUT_ANCHOR) continue; if (anchor_ptr->input_field->number == form_number && !anchor_ptr->input_field->disabled) { FormInfo *form_ptr = anchor_ptr->input_field; char *val = (form_ptr->cp_submit_value != NULL ? form_ptr->cp_submit_value : form_ptr->value); unsigned field_is_special = check_form_specialchars(val); unsigned name_is_special = check_form_specialchars(form_ptr->name); form_is_special = (field_is_special | name_is_special); if (field_is_special == 0) { /* already ok */ } else if (target_cs < 0) { /* already confused */ } else if ((field_is_special & SPECIAL_8BIT) == 0 && (LYCharSet_UC[target_cs].enc == UCT_ENC_8859 || (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) { /* those specials will be trivial */ } else if (UCNeedNotTranslate(form_ptr->value_cs, target_cs)) { /* already ok */ } else if (UCCanTranslateFromTo(form_ptr->value_cs, target_cs)) { /* also ok */ } else if (UCCanTranslateFromTo(target_cs, form_ptr->value_cs)) { target_cs = form_ptr->value_cs; /* try this */ target_csname = NULL; /* will be set after loop */ } else { target_cs = -1; /* don't know what to do */ } /* Same for name */ if (name_is_special == 0) { /* already ok */ } else if (target_cs < 0) { /* already confused */ } else if ((name_is_special & SPECIAL_8BIT) == 0 && (LYCharSet_UC[target_cs].enc == UCT_ENC_8859 || (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) { /* those specials will be trivial */ } else if (UCNeedNotTranslate(form_ptr->name_cs, target_cs)) { /* already ok */ } else if (UCCanTranslateFromTo(form_ptr->name_cs, target_cs)) { /* also ok */ } else if (UCCanTranslateFromTo(target_cs, form_ptr->name_cs)) { target_cs = form_ptr->value_cs; /* try this */ target_csname = NULL; /* will be set after loop */ } else { target_cs = -1; /* don't know what to do */ } ++anchor_limit; } else if (anchor_ptr->input_field->number > form_number) { break; } } /* * If we have input fields (we expect this), make an array of them so we * can organize the data. */ if (anchor_limit != 0) { my_data = typecallocn(PostData, (size_t) anchor_limit); if (my_data == 0) outofmem(__FILE__, "HText_SubmitForm"); assert(my_data != NULL); } if (target_csname == NULL) { if (target_cs >= 0) { if ((form_is_special & SPECIAL_8BIT) != 0) { target_csname = LYCharSet_UC[target_cs].MIMEname; } else if ((form_is_special & SPECIAL_FORM) != 0) { target_csname = LYCharSet_UC[target_cs].MIMEname; } else { target_csname = "us-ascii"; } } else { target_csname = "us-ascii"; target_cs = UCLYhndl_for_unspec; /* always >= 0 */ } } else if (target_cs < 0) { target_cs = UCLYhndl_for_unspec; /* always >= 0 */ } if (submit_item->submit_method == URL_GET_METHOD && Boundary == NULL) { char *temp = NULL; StrAllocCopy(temp, submit_item->submit_action); /* * Method is GET. Clip out any anchor in the current URL. */ strtok(temp, "#"); /* * Clip out any old query in the current URL. */ strtok(temp, "?"); /* * Add the lead question mark for the new URL. */ StrAllocCat(temp, "?"); BStrCat0(my_query, temp); } else { /* * We are submitting POST content to a server, * so load content_type_out. This will be put in * the post_content_type element if all goes well. -FM, kw */ if (SemiColon == TRUE) { StrAllocCopy(content_type_out, "application/sgml-form-urlencoded"); } else if (PlainText == TRUE) { StrAllocCopy(content_type_out, "text/plain"); } else if (Boundary != NULL) { StrAllocCopy(content_type_out, "multipart/form-data"); } else { StrAllocCopy(content_type_out, "application/x-www-form-urlencoded"); } /* * If the ENCTYPE is not multipart/form-data, append the * charset we'll be sending to the post_content_type, IF * (1) there was an explicit accept-charset attribute, OR * (2) we have 8-bit or special chars, AND the document had * an explicit (recognized and accepted) charset parameter, * AND it or target_csname is different from iso-8859-1, * OR * (3) we have 8-bit or special chars, AND the document had * no explicit (recognized and accepted) charset parameter, * AND target_cs is different from the currently effective * assumed charset (which should have been set by the user * so that it reflects what the server is sending, if the * document is rendered correctly). * For multipart/form-data the equivalent will be done later, * separately for each form field. - kw */ if (have_accept_cs || ((form_is_special & SPECIAL_8BIT) != 0 || (form_is_special & SPECIAL_FORM) != 0)) { if (target_cs >= 0 && target_csname) { if (Boundary == NULL) { if ((HTMainText->node_anchor->charset && (strcmp(HTMainText->node_anchor->charset, "iso-8859-1") || strcmp(target_csname, "iso-8859-1"))) || (!HTMainText->node_anchor->charset && target_cs != UCLYhndl_for_unspec)) { HTSprintf(&content_type_out, "; charset=%s", target_csname); } } } else { cannot_transcode(&had_chartrans_warning, target_csname); } } } out_csname = target_csname; /* * Build up a list of the input fields and their associated values. */ for (anchor_ptr = HTMainText->first_anchor; anchor_ptr != NULL; anchor_ptr = anchor_ptr->next) { if (anchor_ptr->link_type != INPUT_ANCHOR) continue; if (anchor_ptr->input_field->number == form_number && !anchor_ptr->input_field->disabled) { FormInfo *form_ptr = anchor_ptr->input_field; int out_cs; QuoteData quoting = (PlainText ? NO_QUOTE : (Boundary ? QUOTE_MULTI : QUOTE_SPECIAL)); assert(my_data != NULL); if (form_ptr->type != F_TEXTAREA_TYPE) textarea_lineno = 0; CTRACE((tfp, "SubmitForm[%d/%d]: ", anchor_count + 1, anchor_limit)); name_used = NonNull(form_ptr->name); switch (form_ptr->type) { case F_RESET_TYPE: CTRACE((tfp, "reset\n")); break; #ifdef USE_FILE_UPLOAD case F_FILE_TYPE: val_used = NonNull(form_ptr->value); CTRACE((tfp, "I will submit \"%s\" (from %s)\n", val_used, name_used)); break; #endif case F_SUBMIT_TYPE: case F_TEXT_SUBMIT_TYPE: case F_IMAGE_SUBMIT_TYPE: if (!(non_empty(form_ptr->name) && !strcmp(form_ptr->name, link_name))) { CTRACE((tfp, "skipping submit field with ")); CTRACE((tfp, "name \"%s\" for link_name \"%s\", %s.\n", form_ptr->name ? form_ptr->name : "???", link_name ? link_name : "???", non_empty(form_ptr->name) ? "not current link" : "no field name")); break; } if (!(form_ptr->type == F_TEXT_SUBMIT_TYPE || (non_empty(form_ptr->value) && !strcmp(form_ptr->value, link_value)))) { CTRACE((tfp, "skipping submit field with ")); CTRACE((tfp, "name \"%s\" for link_name \"%s\", %s!\n", form_ptr->name ? form_ptr->name : "???", link_name ? link_name : "???", "values are different")); break; } /* FALLTHRU */ case F_RADIO_TYPE: case F_CHECKBOX_TYPE: case F_TEXTAREA_TYPE: case F_PASSWORD_TYPE: case F_TEXT_TYPE: case F_OPTION_LIST_TYPE: case F_HIDDEN_TYPE: /* * Be sure to actually look at the option submit value. */ if (form_ptr->cp_submit_value != NULL) { val_used = form_ptr->cp_submit_value; } else { val_used = form_ptr->value; } /* * Charset-translate value now, because we need to know the * charset parameter for multipart bodyparts. - kw */ if (check_form_specialchars(val_used) != 0) { /* We should translate back. */ StrAllocCopy(copied_val_used, val_used); success = FALSE; if (HTCJK == JAPANESE) { if ((0 <= target_cs) && !strcmp(LYCharSet_UC[target_cs].MIMEname, "euc-jp")) { TO_EUC((const unsigned char *) val_used, (unsigned char *) copied_val_used); success = YES; } else if ((0 <= target_cs) && !strcmp(LYCharSet_UC[target_cs].MIMEname, "shift_jis")) { TO_SJIS((const unsigned char *) val_used, (unsigned char *) copied_val_used); success = YES; } } if (!success) { success = LYUCTranslateBackFormData(&copied_val_used, form_ptr->value_cs, target_cs, PlainText); } CTRACE((tfp, "field \"%s\" %d %s -> %d %s %s\n", NonNull(form_ptr->name), form_ptr->value_cs, ((form_ptr->value_cs >= 0) ? LYCharSet_UC[form_ptr->value_cs].MIMEname : "???"), target_cs, target_csname ? target_csname : "???", success ? "OK" : "FAILED")); if (success) { val_used = copied_val_used; } } else { /* We can use the value directly. */ CTRACE((tfp, "field \"%s\" %d %s OK\n", NonNull(form_ptr->name), target_cs, target_csname ? target_csname : "???")); success = YES; } if (!success) { cannot_transcode(&had_chartrans_warning, target_csname); out_cs = form_ptr->value_cs; } else { out_cs = target_cs; } if (out_cs >= 0) out_csname = LYCharSet_UC[out_cs].MIMEname; if (Boundary) { StrAllocCopy(MultipartContentType, "\r\nContent-Type: %s"); if (!success && form_ptr->value_cs < 0) { /* This is weird. */ out_csname = "UNKNOWN-8BIT"; } else if (!success) { target_csname = NULL; } else { if (!target_csname) { target_csname = LYCharSet_UC[target_cs].MIMEname; } } if (strcmp(out_csname, "iso-8859-1")) HTSprintf(&MultipartContentType, "; charset=%s", out_csname); } /* * Charset-translate name now, because we need to know the * charset parameter for multipart bodyparts. - kw */ if (form_ptr->type == F_TEXTAREA_TYPE) { textarea_lineno++; if (textarea_lineno > 1 && last_textarea_name && form_ptr->name && !strcmp(last_textarea_name, form_ptr->name)) { break; } } if (check_form_specialchars(name_used) != 0) { /* We should translate back. */ StrAllocCopy(copied_name_used, name_used); success = LYUCTranslateBackFormData(&copied_name_used, form_ptr->name_cs, target_cs, PlainText); CTRACE((tfp, "name \"%s\" %d %s -> %d %s %s\n", NonNull(form_ptr->name), form_ptr->name_cs, ((form_ptr->name_cs >= 0) ? LYCharSet_UC[form_ptr->name_cs].MIMEname : "???"), target_cs, target_csname ? target_csname : "???", success ? "OK" : "FAILED")); if (success) { name_used = copied_name_used; } if (Boundary) { if (!success) { StrAllocCopy(MultipartContentType, ""); target_csname = NULL; } else { if (!target_csname) target_csname = LYCharSet_UC[target_cs].MIMEname; } } } else { /* We can use the name directly. */ CTRACE((tfp, "name \"%s\" %d %s OK\n", NonNull(form_ptr->name), target_cs, target_csname ? target_csname : "???")); success = YES; if (Boundary) { StrAllocCopy(copied_name_used, name_used); } } if (!success) { cannot_transcode(&had_chartrans_warning, target_csname); } if (Boundary) { /* * According to RFC 1867, Non-ASCII field names * "should be encoded according to the prescriptions * of RFC 1522 [...]. I don't think RFC 1522 actually * is meant to apply to parameters like this, and it * is unknown whether any server would make sense of * it, so for now just use some quoting/escaping and * otherwise leave 8-bit values as they are. * Non-ASCII characters in form field names submitted * as multipart/form-data can only occur if the form * provider specifically asked for it anyway. - kw */ HTMake822Word(&copied_name_used, FALSE); name_used = copied_name_used; } break; default: CTRACE((tfp, "What type is %d?\n", form_ptr->type)); break; } skip_field = FALSE; my_data[anchor_count].first = TRUE; my_data[anchor_count].type = form_ptr->type; /* * Using the values of 'name_used' and 'val_used' computed in the * previous case-statement, compute the 'first' and 'data' values * for the current input field. */ switch (form_ptr->type) { default: skip_field = TRUE; break; #ifdef USE_FILE_UPLOAD case F_FILE_TYPE: load_a_file(val_used, &(my_data[anchor_count].data)); break; #endif /* USE_FILE_UPLOAD */ case F_SUBMIT_TYPE: case F_TEXT_SUBMIT_TYPE: case F_IMAGE_SUBMIT_TYPE: if ((non_empty(form_ptr->name) && !strcmp(form_ptr->name, link_name)) && (form_ptr->type == F_TEXT_SUBMIT_TYPE || (non_empty(form_ptr->value) && !strcmp(form_ptr->value, link_value)))) { ; } else { skip_field = TRUE; } break; case F_RADIO_TYPE: case F_CHECKBOX_TYPE: /* * Only add if selected. */ if (form_ptr->num_value) { ; } else { skip_field = TRUE; } break; case F_TEXTAREA_TYPE: if (!last_textarea_name || strcmp(last_textarea_name, form_ptr->name)) { textarea_lineno = 1; last_textarea_name = form_ptr->name; } else { my_data[anchor_count].first = FALSE; } break; case F_PASSWORD_TYPE: case F_TEXT_TYPE: case F_OPTION_LIST_TYPE: case F_HIDDEN_TYPE: break; } /* * If we did not decide to skip the current field, populate the * values in the array for it. */ if (!skip_field) { StrAllocCopy(my_data[anchor_count].name, name_used); StrAllocCopy(my_data[anchor_count].value, val_used); if (my_data[anchor_count].data == 0) BStrCat0(my_data[anchor_count].data, val_used); my_data[anchor_count].quote = quoting; if (quoting == QUOTE_MULTI && check_if_base64_needed(submit_item->submit_method, my_data[anchor_count].data)) { CTRACE((tfp, "will encode as base64\n")); my_data[anchor_count].quote = QUOTE_BASE64; escaped2 = convert_to_base64(BStrData(my_data[anchor_count].data), (size_t) BStrLen(my_data[anchor_count].data)); BStrCopy0(my_data[anchor_count].data, escaped2); FREE(escaped2); } } ++anchor_count; FREE(copied_name_used); FREE(copied_val_used); } else if (anchor_ptr->input_field->number > form_number) { break; } } FREE(copied_name_used); if (my_data != 0) { BOOL first_one = TRUE; /* * If we're using a MIME-boundary, make it unique. */ if (content_type_out != 0 && Boundary != 0) { Boundary = 0; StrAllocCopy(Boundary, "LYNX"); for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) { if (my_data[anchor_count].data != 0) { UpdateBoundary(&Boundary, my_data[anchor_count].data); } } HTSprintf(&content_type_out, "; boundary=%s", Boundary); } for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) { if (my_data[anchor_count].name != 0 && my_data[anchor_count].value != 0) { CTRACE((tfp, "processing [%d:%d] name=%s(first:%d, value=%s, data=%p)\n", anchor_count + 1, anchor_limit, NonNull(my_data[anchor_count].name), my_data[anchor_count].first, NonNull(my_data[anchor_count].value), (void *) my_data[anchor_count].data)); if (my_data[anchor_count].first) { if (first_one) { if (Boundary) { HTBprintf(&my_query, "--%s\r\n", Boundary); } first_one = FALSE; } else { if (PlainText) { BStrCat0(my_query, "\n"); } else if (SemiColon) { BStrCat0(my_query, ";"); } else if (Boundary) { HTBprintf(&my_query, "\r\n--%s\r\n", Boundary); } else { BStrCat0(my_query, "&"); } } } /* append a null to the string */ HTSABCat(&(my_data[anchor_count].data), "", 1); name_used = my_data[anchor_count].name; val_used = my_data[anchor_count].value; } else { /* there is no data to send */ continue; } switch (my_data[anchor_count].type) { case F_TEXT_TYPE: case F_PASSWORD_TYPE: case F_OPTION_LIST_TYPE: case F_HIDDEN_TYPE: escaped1 = escape_or_quote_name(my_data[anchor_count].name, my_data[anchor_count].quote, MultipartContentType); escaped2 = escape_or_quote_value(val_used, my_data[anchor_count].quote); HTBprintf(&my_query, "%s%s%s%s%s", escaped1, (Boundary ? "" : "="), (PlainText ? "\n" : ""), escaped2, ((PlainText && *escaped2) ? "\n" : "")); break; case F_CHECKBOX_TYPE: case F_RADIO_TYPE: escaped1 = escape_or_quote_name(my_data[anchor_count].name, my_data[anchor_count].quote, MultipartContentType); escaped2 = escape_or_quote_value(val_used, my_data[anchor_count].quote); HTBprintf(&my_query, "%s%s%s%s%s", escaped1, (Boundary ? "" : "="), (PlainText ? "\n" : ""), escaped2, ((PlainText && *escaped2) ? "\n" : "")); break; case F_SUBMIT_TYPE: case F_TEXT_SUBMIT_TYPE: case F_IMAGE_SUBMIT_TYPE: /* * If it has a non-zero length name (e.g., because * its IMAGE_SUBMIT_TYPE is to be handled homologously * to an image map, or a SUBMIT_TYPE in a set of * multiple submit buttons, or a single type="text" * that's been converted to a TEXT_SUBMIT_TYPE), * include the name=value pair, or fake name.x=0 and * name.y=0 pairs for IMAGE_SUBMIT_TYPE. -FM */ escaped1 = escape_or_quote_name(my_data[anchor_count].name, my_data[anchor_count].quote, MultipartContentType); escaped2 = escape_or_quote_value(val_used, my_data[anchor_count].quote); if (my_data[anchor_count].type == F_IMAGE_SUBMIT_TYPE) { /* * It's a clickable image submit button. Fake a 0,0 * coordinate pair, which typically returns the image's * default. -FM */ if (Boundary) { *(StrChr(escaped1, '=') + 1) = '\0'; HTBprintf(&my_query, "%s\"%s.x\"\r\n\r\n0\r\n--%s\r\n%s\"%s.y\"\r\n\r\n0", escaped1, my_data[anchor_count].name, Boundary, escaped1, my_data[anchor_count].name); } else { HTBprintf(&my_query, "%s.x=0%s%s.y=0%s", escaped1, (PlainText ? "\n" : (SemiColon ? ";" : "&")), escaped1, ((PlainText && *escaped1) ? "\n" : "")); } } else { /* * It's a standard submit button. Use the name=value * pair. = FM */ HTBprintf(&my_query, "%s%s%s%s%s", escaped1, (Boundary ? "" : "="), (PlainText ? "\n" : ""), escaped2, ((PlainText && *escaped2) ? "\n" : "")); } break; case F_RESET_TYPE: /* ignore */ break; case F_TEXTAREA_TYPE: escaped2 = escape_or_quote_value(val_used, my_data[anchor_count].quote); if (my_data[anchor_count].first) { /* * Names are different so this is the first textarea or a * different one from any before it. */ if (PlainText) { FREE(previous_blanks); } else if (Boundary) { StrAllocCopy(previous_blanks, "\r\n"); } else { StrAllocCopy(previous_blanks, "%0d%0a"); } escaped1 = escape_or_quote_name(name_used, my_data[anchor_count].quote, MultipartContentType); HTBprintf(&my_query, "%s%s%s%s%s", escaped1, (Boundary ? "" : "="), (PlainText ? "\n" : ""), escaped2, ((PlainText && *escaped2) ? "\n" : "")); } else { const char *marker = (PlainText ? "\n" : (Boundary ? "\r\n" : "%0d%0a")); /* * This is a continuation of a previous textarea. */ if (escaped2[0] != '\0') { if (previous_blanks) { BStrCat0(my_query, previous_blanks); FREE(previous_blanks); } BStrCat0(my_query, escaped2); if (PlainText || Boundary) BStrCat0(my_query, marker); else StrAllocCopy(previous_blanks, marker); } else { StrAllocCat(previous_blanks, marker); } } break; case F_RANGE_TYPE: /* not implemented */ break; #ifdef USE_FILE_UPLOAD case F_FILE_TYPE: if (PlainText) { StrAllocCopy(escaped1, my_data[anchor_count].name); } else if (Boundary) { const char *t = guess_content_type(val_used); char *copied_fname = NULL; StrAllocCopy(escaped1, "Content-Disposition: form-data"); HTSprintf(&escaped1, "; name=\"%s\"", my_data[anchor_count].name); StrAllocCopy(copied_fname, val_used); HTMake822Word(&copied_fname, FALSE); HTSprintf(&escaped1, "; filename=\"%s\"", copied_fname); FREE(copied_fname); /* Should we take into account the encoding? */ HTSprintf(&escaped1, "\r\nContent-Type: %s", t); if (my_data[anchor_count].quote == QUOTE_BASE64) StrAllocCat(escaped1, "\r\nContent-Transfer-Encoding: base64"); StrAllocCat(escaped1, "\r\n\r\n"); } else { escaped1 = HTEscapeSP(my_data[anchor_count].name, URL_XALPHAS); } HTBprintf(&my_query, "%s%s%s", escaped1, (Boundary ? "" : "="), (PlainText ? "\n" : "")); /* * If we have anything more than the trailing null we added, * append the file-data to the query. */ if (BStrLen(my_data[anchor_count].data) > 1) { HTSABCat(&my_query, BStrData(my_data[anchor_count].data), BStrLen(my_data[anchor_count].data) - 1); if (PlainText) HTBprintf(&my_query, "\n"); } break; #endif /* USE_FILE_UPLOAD */ case F_KEYGEN_TYPE: case F_BUTTON_TYPE: /* not implemented */ break; } FREE(escaped1); FREE(escaped2); } if (Boundary) { HTBprintf(&my_query, "\r\n--%s--\r\n", Boundary); } /* * The data may contain a null - so we use fwrite(). */ if (TRACE) { CTRACE((tfp, "Query %d{", BStrLen(my_query))); trace_bstring(my_query); CTRACE((tfp, "}\n")); } } if (submit_item->submit_method == URL_MAIL_METHOD) { HTUserMsg2(gettext("Submitting %s"), submit_item->submit_action); HTSABCat(&my_query, "", 1); /* append null */ mailform((submit_item->submit_action + 7), (isEmpty(submit_item->submit_title) ? NonNull(HText_getTitle()) : submit_item->submit_title), BStrData(my_query), content_type_out); result = 0; BStrFree(my_query); FREE(content_type_out); } else { _statusline(SUBMITTING_FORM); if (submit_item->submit_method == URL_POST_METHOD || Boundary) { LYFreePostData(doc); doc->post_data = my_query; doc->post_content_type = content_type_out; /* don't free c_t_out */ CTRACE((tfp, "GridText - post_data set:\n%s\n", content_type_out)); StrAllocCopy(doc->address, submit_item->submit_action); } else { /* GET_METHOD */ HTSABCat(&my_query, "", 1); /* append null */ StrAllocCopy(doc->address, BStrData(my_query)); /* FIXME? */ LYFreePostData(doc); FREE(content_type_out); } result = 1; } FREE(MultipartContentType); FREE(previous_blanks); FREE(Boundary); if (my_data != 0) { for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) { FREE(my_data[anchor_count].name); FREE(my_data[anchor_count].value); BStrFree(my_data[anchor_count].data); } FREE(my_data); } return (result); } void HText_DisableCurrentForm(void) { TextAnchor *anchor_ptr; HTFormDisabled = TRUE; if (HTMainText != NULL) { /* * Go through list of anchors and set the disabled flag. */ for (anchor_ptr = HTMainText->first_anchor; anchor_ptr != NULL; anchor_ptr = anchor_ptr->next) { if (anchor_ptr->link_type == INPUT_ANCHOR && anchor_ptr->input_field->number == HTFormNumber) { anchor_ptr->input_field->disabled = TRUE; } } } return; } void HText_ResetForm(FormInfo * form) { TextAnchor *anchor_ptr; _statusline(RESETTING_FORM); if (HTMainText == 0) return; /* * Go through list of anchors and reset values. */ for (anchor_ptr = HTMainText->first_anchor; anchor_ptr != NULL; anchor_ptr = anchor_ptr->next) { if (anchor_ptr->link_type == INPUT_ANCHOR) { if (anchor_ptr->input_field->number == form->number) { if (anchor_ptr->input_field->type == F_RADIO_TYPE || anchor_ptr->input_field->type == F_CHECKBOX_TYPE) { if (anchor_ptr->input_field->orig_value[0] == '0') anchor_ptr->input_field->num_value = 0; else anchor_ptr->input_field->num_value = 1; } else if (anchor_ptr->input_field->type == F_OPTION_LIST_TYPE) { anchor_ptr->input_field->value = anchor_ptr->input_field->orig_value; anchor_ptr->input_field->cp_submit_value = anchor_ptr->input_field->orig_submit_value; } else { StrAllocCopy(anchor_ptr->input_field->value, anchor_ptr->input_field->orig_value); } } else if (anchor_ptr->input_field->number > form->number) { break; } } } } /* * This function is called before reloading/reparsing current document to find * whether any forms content was changed by user so any information will be * lost. */ BOOLEAN HText_HaveUserChangedForms(HText *text) { TextAnchor *anchor_ptr; if (text == 0) return FALSE; /* * Go through list of anchors to check if any value was changed. * This code based on HText_ResetForm() */ for (anchor_ptr = text->first_anchor; anchor_ptr != NULL; anchor_ptr = anchor_ptr->next) { if (anchor_ptr->link_type == INPUT_ANCHOR) { if (anchor_ptr->input_field->type == F_RADIO_TYPE || anchor_ptr->input_field->type == F_CHECKBOX_TYPE) { if ((anchor_ptr->input_field->orig_value[0] == '0' && anchor_ptr->input_field->num_value == 1) || (anchor_ptr->input_field->orig_value[0] != '0' && anchor_ptr->input_field->num_value == 0)) return TRUE; } else if (anchor_ptr->input_field->type == F_OPTION_LIST_TYPE) { if (strcmp(anchor_ptr->input_field->value, anchor_ptr->input_field->orig_value)) return TRUE; if (strcmp(anchor_ptr->input_field->cp_submit_value, anchor_ptr->input_field->orig_submit_value)) return TRUE; } else { if (strcmp(anchor_ptr->input_field->value, anchor_ptr->input_field->orig_value)) return TRUE; } } } return FALSE; } void HText_activateRadioButton(FormInfo * form) { TextAnchor *anchor_ptr; int form_number = form->number; if (!HTMainText) return; for (anchor_ptr = HTMainText->first_anchor; anchor_ptr != NULL; anchor_ptr = anchor_ptr->next) { if (anchor_ptr->link_type == INPUT_ANCHOR && anchor_ptr->input_field->type == F_RADIO_TYPE) { if (anchor_ptr->input_field->number == form_number) { /* if it has the same name and its on */ if (!strcmp(anchor_ptr->input_field->name, form->name) && anchor_ptr->input_field->num_value) { anchor_ptr->input_field->num_value = 0; break; } } else if (anchor_ptr->input_field->number > form_number) { break; } } } form->num_value = 1; } #ifdef LY_FIND_LEAKS /* * Purpose: Free all currently loaded HText objects in memory. * Arguments: void * Return Value: void * Remarks/Portability/Dependencies/Restrictions: * Usage of this function should really be limited to program * termination. * Revision History: * 05-27-94 created Lynx 2-3-1 Garrett Arch Blythe */ static void free_all_texts(void) { HText *cur = NULL; if (!loaded_texts) return; /* * Simply loop through the loaded texts list killing them off. */ while (loaded_texts && !HTList_isEmpty(loaded_texts)) { if ((cur = (HText *) HTList_removeLastObject(loaded_texts)) != NULL) { HText_free(cur); } } /* * Get rid of the text list. */ if (loaded_texts) { HTList_delete(loaded_texts); } /* * Insurance for bad HTML. */ FREE(HTCurSelectGroup); FREE(HTCurSelectGroupSize); FREE(HTCurSelectedOptionValue); PerFormInfo_free(HTCurrentForm); return; } #endif /* LY_FIND_LEAKS */ /* * stub_HTAnchor_address is like HTAnchor_address, but it returns the * parent address for child links. This is only useful for traversal's * where one does not want to index a text file N times, once for each * of N internal links. Since the parent link has already been taken, * it won't go again, hence the (incorrect) links won't cause problems. */ char *stub_HTAnchor_address(HTAnchor * me) { char *addr = NULL; if (me) StrAllocCopy(addr, me->parent->address); return addr; } void HText_setToolbar(HText *text) { if (text) text->toolbar = TRUE; return; } BOOL HText_hasToolbar(HText *text) { return (BOOL) ((text && text->toolbar) ? TRUE : FALSE); } void HText_setNoCache(HText *text) { if (text) text->no_cache = TRUE; return; } BOOL HText_hasNoCacheSet(HText *text) { return (BOOL) ((text && text->no_cache) ? TRUE : FALSE); } BOOL HText_hasUTF8OutputSet(HText *text) { return (BOOL) ((text && text->T.output_utf8) ? TRUE : FALSE); } /* * Check charset and set the kcode element. -FM * Info on the input charset may be passed in in two forms, * as a string (if given explicitly) and as a pointer to * a LYUCcharset (from chartrans mechanism); either can be NULL. * For Japanese the kcode will be reset at a space or explicit * line or paragraph break, so what we set here may not last for * long. It's potentially more important not to set HTCJK to * NOCJK unless we are sure. - kw */ void HText_setKcode(HText *text, const char *charset, LYUCcharset *p_in) { BOOL charset_explicit; if (!text) return; /* * Check whether we have some kind of info. - kw */ if (!charset && !p_in) { return; } charset_explicit = (BOOLEAN) (charset ? TRUE : FALSE); /* * If no explicit charset string, use the implied one. - kw */ if (isEmpty(charset)) { charset = p_in->MIMEname; } /* * Check whether we have a specified charset. -FM */ if (isEmpty(charset)) { return; } /* * We've included the charset, and not forced a download offer, * only if the currently selected character set can handle it, * so check the charset value and set the text->kcode element * appropriately. -FM */ /* If charset isn't specified explicitely nor assumed, * p_in->MIMEname would be set as display charset. * So text->kcode sholud be set as SJIS or EUC here only if charset * is specified explicitely, otherwise text->kcode would cause * mishandling Japanese strings. -- TH */ if (charset_explicit && (!strcmp(charset, "shift_jis") || !strcmp(charset, "x-sjis") || /* 1997/11/28 (Fri) 18:11:33 */ !strcmp(charset, "x-shift-jis"))) { text->kcode = SJIS; } else if (charset_explicit #ifdef EXP_JAPANESEUTF8_SUPPORT && strcmp(charset, "utf-8") #endif && ((p_in && (p_in->enc == UCT_ENC_CJK)) || !strcmp(charset, "x-euc") || /* 1997/11/28 (Fri) 18:11:24 */ !strcmp(charset, "euc-jp") || !StrNCmp(charset, "x-euc-", 6) || !strcmp(charset, "euc-kr") || !strcmp(charset, "iso-2022-kr") || !strcmp(charset, "big5") || !strcmp(charset, "cn-big5") || !strcmp(charset, "euc-cn") || !strcmp(charset, "gb2312") || !StrNCmp(charset, "cn-gb", 5) || !strcmp(charset, "iso-2022-cn"))) { text->kcode = EUC; } else { /* * If we get to here, it's not CJK, so disable that if * it is enabled. But only if we are quite sure. -FM & kw */ text->kcode = NOKANJI; if (IS_CJK_TTY) { if (!p_in || ((p_in->enc != UCT_ENC_CJK) #ifdef EXP_JAPANESEUTF8_SUPPORT && (p_in->enc != UCT_ENC_UTF8) #endif )) { HTCJK = NOCJK; } } } if (charset_explicit #ifdef EXP_JAPANESEUTF8_SUPPORT && strcmp(charset, "utf-8") #endif ) { text->specified_kcode = text->kcode; } else { if (UCAssume_MIMEcharset) { if (!strcmp(UCAssume_MIMEcharset, "euc-jp")) text->kcode = text->specified_kcode = EUC; else if (!strcmp(UCAssume_MIMEcharset, "shift_jis")) text->kcode = text->specified_kcode = SJIS; } } return; } /* * Set a permissible split at the current end of the last line. -FM */ void HText_setBreakPoint(HText *text) { if (!text) return; /* * Can split here. -FM */ text->permissible_split = text->last_line->size; return; } /* * This function determines whether a document which * would be sought via the a URL that has a fragment * directive appended is otherwise identical to the * currently loaded document, and if so, returns * FALSE, so that any no_cache directives can be * overridden "safely", on the grounds that we are * simply acting on the equivalent of a paging * command. Otherwise, it returns TRUE, i.e, that * the target document might differ from the current, * based on any caching directives or analyses which * claimed or suggested this. -FM */ BOOL HText_AreDifferent(HTParentAnchor *anchor, const char *full_address) { HTParentAnchor *MTanc; char *MTaddress; char *MTpound; /* * Do we have a loaded document and both * arguments for this function? */ if (!(HTMainText && anchor && full_address)) return TRUE; /* * Do we have both URLs? */ MTanc = HTMainText->node_anchor; if (!(MTanc->address && anchor->address)) return (TRUE); /* * Do we have a fragment associated with the target? */ if (findPoundSelector(full_address) == NULL) return (TRUE); /* * Always treat client-side image map menus * as potentially stale, so we'll create a * fresh menu from the LynxMaps HTList. */ if (isLYNXIMGMAP(anchor->address)) return (TRUE); /* * Do the docs differ in the type of request? */ if (MTanc->isHEAD != anchor->isHEAD) return (TRUE); /* * Are the actual URLs different, after factoring * out a "LYNXIMGMAP:" leader in the MainText URL * and its fragment, if present? */ MTaddress = (isLYNXIMGMAP(MTanc->address) ? MTanc->address + LEN_LYNXIMGMAP : MTanc->address); MTpound = trimPoundSelector(MTaddress); if (strcmp(MTaddress, anchor->address)) { restorePoundSelector(MTpound); return (TRUE); } restorePoundSelector(MTpound); /* * If the MainText is not an image map menu, * do the docs have different POST contents? */ if (MTaddress == MTanc->address) { if (MTanc->post_data) { if (anchor->post_data) { if (!BINEQ(MTanc->post_data, anchor->post_data)) { /* * Both have contents, and they differ. */ return (TRUE); } } else { /* * The loaded document has content, but the * target doesn't, so they're different. */ return (TRUE); } } else if (anchor->post_data) { /* * The loaded document does not have content, but * the target does, so they're different. */ return (TRUE); } } /* * We'll assume the target is a position in the currently * displayed document, and thus can ignore any header, META, * or other directives not to use a cached rendition. -FM */ return (FALSE); } #define CanTrimTextArea(c) \ (LYtrimInputFields ? isspace(c) : ((c) == '\r' || (c) == '\n')) /* * Re-render the text of a tagged ("[123]") HTLine (arg1), with the tag * number incremented by some value (arg5). The re-rendered string may * be allowed to expand in the event of a tag width change (eg, 99 -> 100) * as controlled by arg6 (CHOP or NOCHOP). Arg4 is either (the address * of) a value which must match, in order for the tag to be incremented, * or (the address of) a 0-value, which will match any value, and cause * any valid tag to be incremented. Arg2 is a pointer to the first/only * anchor that exists on the line; we may need to adjust their position(s) * on the line. Arg3 when non-0 indicates the number of new digits that * were added to the 2nd line in a line crossing pair. * * All tags fields in a line which individually match an expected new value, * are incremented. Line crossing [tags] are handled (PITA). * * Untagged or improperly tagged lines are not altered. * * Returns the number of chars added to the original string's length, if * any. * * --KED 02/03/99 */ static int increment_tagged_htline(HTLine *ht, TextAnchor *a, int *lx_val, int *old_val, int incr, int mode) { char buf[MAX_LINE]; char lxbuf[MAX_LINE * 2]; TextAnchor *st_anchor = a; TextAnchor *nxt_anchor; char *p = ht->data; char *s = buf; char *lx = lxbuf; char *t; BOOLEAN plx = FALSE; BOOLEAN valid; int val; int n; int new_n; int pre_n; int post_n; int fixup = 0; /* * Cleanup for the 2nd half of a line crosser, whose number of tag * digits grew by some number of places (usually 1 when it does * happen, though it *could* be more). The tag chars were already * rendered into the 2nd line of the pair, but the positioning and * other effects haven't been rippled through any other anchors on * the (2nd) line. So we do that here, as a special case, since * the part of the tag that's in the 2nd line of the pair, will not * be found by the tag string parsing code. Double PITA. * * [see comments below on line crosser caused problems] */ if (*lx_val != 0) { nxt_anchor = st_anchor; while ((nxt_anchor) && (nxt_anchor->line_num == a->line_num)) { nxt_anchor->line_pos = (short) (nxt_anchor->line_pos + *lx_val); nxt_anchor = nxt_anchor->next; } fixup = *lx_val; *lx_val = 0; if (st_anchor) st_anchor = st_anchor->next; } /* * Walk thru the line looking for tags (ie, "[nnn]" strings). */ while (*p != '\0') { if (*p != '[') { *s++ = *p++; continue; } else { *s++ = *p++; t = p; n = 0; valid = TRUE; /* p = t = byte after '[' */ /* * Make sure there are only digits between "[" and "]". */ while (*t != ']') { if (*t == '\0') { /* uhoh - we have a potential line crosser */ valid = FALSE; plx = TRUE; break; } if (isdigit(UCH(*t++))) { n++; continue; } else { valid = FALSE; break; } } /* * If the format is OK, we check to see if the value is what * we expect. If not, we have a random [nn] string in the text, * and leave it alone. * * [It is *possible* to have a false match here, *if* there are * two identical [nn] strings (including the numeric value of * nn), one of which is the [tag], and the other being part of * a document. In such a case, the 1st [nn] string will get * incremented; the 2nd one won't, which makes it a 50-50 chance * of being correct, if and when such an unlikely juxtaposition * of text ever occurs. Further validation tests of the [nnn] * string are probably not possible, since little of the actual * anchor-associated-text is retained in the TextAnchor or the * FormInfo structs. Fortunately, I think the current method is * more than adequate to weed out 99.999% of any possible false * matches, just as it stands. Caveat emptor.] */ if ((valid) && (n > 0)) { val = atoi(p); if ((val == *old_val) || (*old_val == 0)) { /* 0 matches all */ if (*old_val != 0) (*old_val)++; val += incr; sprintf(s, "%d", val); new_n = (int) strlen(s); s += new_n; p += n; /* * If the number of digits in an existing [tag] increased * (eg, [99] --> [100], etc), we need to "adjust" its * horizontal position, and that of all subsequent tags * that may be on the same line. PITA. * * [This seems to work as long as a tag isn't a line * crosser; when it is, the position of anchors on either * side of the split tag, seem to "float" and try to be * as "centered" as possible. Which means that simply * incrementing the line_pos by the fixed value of the * number of digits that got added to some tag in either * line doesn't work quite right, and the text for (say) * a button may get stomped on by another copy of itself, * but offset by a few chars, when it is selected (eg, * "Box Office" may end up looking like "BoBox Office" or * "Box Officece", etc. * * Dunno how to fix that behavior ATT, but at least the * tag numbers themselves are correct. -KED /\oo/\ ] */ if ((new_n -= n) != 0) { nxt_anchor = st_anchor; while ((nxt_anchor) && (nxt_anchor->line_num == a->line_num)) { nxt_anchor->line_pos = (short) (nxt_anchor->line_pos + new_n); nxt_anchor = nxt_anchor->next; } if (st_anchor) st_anchor = st_anchor->next; } } } /* * Unfortunately, valid [tag] strings *can* be split across two * lines. Perhaps it would be best to just prevent that from * happening, but a look into that code, makes me wonder. Anyway, * we can handle such tags without *too* much trouble in here [I * think], though since such animals are rather rare, it makes it * a bit difficult to test thoroughly (ie, Beyond here, there be * Dragons). * * We use lxbuf[] to deal with the two lines involved. */ pre_n = (int) strlen(p); /* count of 1st part chars in this line */ post_n = (int) strlen(ht->next->data); if (plx && (pre_n + post_n + 2 < (int) sizeof(lxbuf))) { strcpy(lx, p); /* <- 1st part of a possible lx'ing tag */ strcat(lx, ht->next->data); /* tack on NEXT line */ t = lx; n = 0; valid = TRUE; /* * Go hunting again for just digits, followed by tag end ']'. */ while (*t != ']') { if (isdigit(UCH(*t++))) { n++; continue; } else { valid = FALSE; break; } } /* * It *looks* like a line crosser; now we value test it to * find out for sure [but see the "false match" warning, * above], and if it matches, increment it into the buffer, * along with the 2nd line's text. */ if ((valid) && (n > 0) && (n + post_n + 2) < MAX_LINE) { val = atoi(lx); if ((val == *old_val) || (*old_val == 0)) { const char *r; if (*old_val != 0) (*old_val)++; val += incr; sprintf(lx, "%d", val); new_n = (int) strlen(lx); if ((r = StrChr(ht->next->data, ']')) == 0) { r = ""; } strcat(lx, r); /* * We keep the the same number of chars from the * adjusted tag number in the current line; any * extra chars due to a digits increase, will be * stuffed into the next line. * * Keep track of any digits added, for the next * pass through. */ s = StrNCpy(s, lx, pre_n) + pre_n; lx += pre_n; strcpy(ht->next->data, lx); *lx_val = new_n - n; } } break; /* had an lx'er, so we're done with this line */ } } } *s = '\0'; n = (int) strlen(ht->data); if (mode == CHOP) { *(buf + n) = '\0'; } else if (strlen(buf) > ht->size) { /* we didn't allocate enough space originally - increase it */ HTLine *temp; allocHTLine(temp, strlen(buf)); if (!temp) outofmem(__FILE__, "increment_tagged_htline"); assert(temp != NULL); memcpy(temp, ht, LINE_SIZE(0)); #if defined(USE_COLOR_STYLE) POOLallocstyles(temp->styles, ht->numstyles); if (!temp->styles) outofmem(__FILE__, "increment_tagged_htline"); assert(temp->styles != NULL); memcpy(temp->styles, ht->styles, sizeof(HTStyleChange) * ht->numstyles); #endif ht = temp; ht->prev->next = ht; /* Link in new line */ ht->next->prev = ht; /* Could be same node of course */ } strcpy(ht->data, buf); return ((int) strlen(buf) - n + fixup); } /* * Creates a new anchor and associated struct's appropriate for a form * TEXTAREA, and links them into the lists following the current anchor * position (as specified by arg1). * * Exits with arg1 now pointing at the new TextAnchor, and arg2 pointing * at the new, associated HTLine. * * --KED 02/13/99 */ static void insert_new_textarea_anchor(TextAnchor **curr_anchor, HTLine **exit_htline) { TextAnchor *anchor = *curr_anchor; HTLine *htline; TextAnchor *a = 0; FormInfo *f = 0; HTLine *l = 0; int curr_tag = 0; /* 0 ==> match any [tag] number */ int lx = 0; /* 0 ==> no line crossing [tag]; it's a new line */ int i; /* * Find line in the text that matches ending anchorline of * the TEXTAREA. * * [Yes, Virginia ... we *do* have to go thru this for each * anchor being added, since there is NOT a 1-to-1 mapping * between anchors and htlines. I suppose we could create * YAS (Yet Another Struct), but there are too many structs{} * floating around in here, as it is. IMNSHO.] */ for (htline = FirstHTLine(HTMainText), i = 0; anchor->line_num != i; i++) { htline = htline->next; if (htline == HTMainText->last_line) break; } /* * Clone and initialize the struct's needed to add a new TEXTAREA * anchor. */ allocHTLine(l, MAX_LINE); POOLtypecalloc(TextAnchor, a); POOLtypecalloc(FormInfo, f); if (a == NULL || l == NULL || f == NULL) outofmem(__FILE__, "insert_new_textarea_anchor"); assert(a != NULL); assert(f != NULL); assert(l != NULL); /* Init all the fields in the new TextAnchor. */ /* [anything "special" needed based on ->show_anchor value ?] */ a->next = anchor->next; a->number = anchor->number; a->line_pos = anchor->line_pos; a->extent = anchor->extent; a->sgml_offset = SGML_offset(); a->line_num = anchor->line_num + 1; LYCopyHiText(a, anchor); a->link_type = anchor->link_type; a->input_field = f; a->show_anchor = anchor->show_anchor; a->inUnderline = anchor->inUnderline; a->expansion_anch = TRUE; a->anchor = NULL; /* Just the (seemingly) relevant fields in the new FormInfo. */ /* [do we need to do anything "special" based on ->disabled] */ StrAllocCopy(f->name, anchor->input_field->name); f->number = anchor->input_field->number; f->type = anchor->input_field->type; StrAllocCopy(f->orig_value, ""); f->size = anchor->input_field->size; f->maxlength = anchor->input_field->maxlength; f->no_cache = anchor->input_field->no_cache; f->disabled = anchor->input_field->disabled; f->readonly = anchor->input_field->readonly; f->value_cs = current_char_set; /* use current setting - kw */ /* Init all the fields in the new HTLine (but see the #if). */ l->next = htline->next; l->prev = htline; l->offset = htline->offset; l->size = htline->size; #if defined(USE_COLOR_STYLE) /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */ l->numstyles = htline->numstyles; /*we fork the pointers! */ l->styles = htline->styles; #endif strcpy(l->data, htline->data); /* * Link in the new HTLine. */ htline->next->prev = l; htline->next = l; if (fields_are_numbered()) { a->number++; increment_tagged_htline(l, a, &lx, &curr_tag, 1, CHOP); } /* * If we're at the tail end of the TextAnchor or HTLine list(s), * the new node becomes the last node. */ if (anchor == HTMainText->last_anchor) HTMainText->last_anchor = a; if (htline == HTMainText->last_line) HTMainText->last_line = l; /* * Link in the new TextAnchor and point the entry anchor arg at it; * point the entry HTLine arg at it, too. */ anchor->next = a; *curr_anchor = a; *exit_htline = l->next; return; } /* * If new anchors were added to expand a TEXTAREA, we need to ripple the * new line numbers [and char counts ?] thru the subsequent anchors. * * If form lines are getting [nnn] tagged, we need to update the displayed * tag values to match (which means rerendering them ... sigh). * * Finally, we need to update various HTMainText and other counts, etc. * * [dunno if the char counts really *need* to be done, or if we're using * the exactly proper values/algorithms ... seems to be OK though ...] * * --KED 02/13/99 */ static void update_subsequent_anchors(int newlines, TextAnchor *start_anchor, HTLine *start_htline, int start_tag) { TextAnchor *anchor; HTLine *htline = start_htline; int line_adj = 0; int tag_adj = 0; int lx = 0; int hang = 0; /* for HANG detection of a nasty intermittent */ int hang_detect = 100000; /* ditto */ CTRACE((tfp, "GridText: adjusting struct's to add %d new line(s)\n", newlines)); /* * Update numeric fields of the rest of the anchors. * * [We bypass bumping ->number if it has a value of 0, which takes care * of the ->input_field->type == F_HIDDEN_TYPE (as well as any other * "hidden" anchors, if such things exist). Seems like the "right * thing" to do. I think.] */ anchor = start_anchor->next; /* begin updating with the NEXT anchor */ while (anchor) { if (fields_are_numbered() && (anchor->number != 0)) anchor->number += newlines; anchor->line_num += newlines; anchor = anchor->next; } /* * Update/rerender anchor [tags], if they are being numbered. * * [If a number tag (eg, "[177]") is itself broken across a line * boundary, this fixup only partially works. While the tag * numbering is done properly across the pair of lines, the * horizontal positioning on *either* side of the split, can get * out of sync by a char or two when it gets selected. See the * [comments] in increment_tagged_htline() for some more detail. * * I suppose THE fix is to prevent such tag-breaking in the first * place (dunno where yet, though). Ah well ... at least the tag * numbers themselves are correct from top to bottom now. * * All that said, about the only time this will be a problem in * *practice*, is when a page has near 1000 links or more (possibly * after a TEXTAREA expansion), and has line crossing tag(s), and * the tag numbers in a line crosser go from initially all 3 digit * numbers, to some mix of 3 and 4 digits (or all 4 digits) as a * result of the expansion process. Oh, you also need a "clump" of * anchors all on the same lines. * * Yes, it *can* happen, but in real life, it probably won't be * seen very much ...] * * [This may also be an artifact of bumping into the right hand * screen edge (or RHS margin), since we don't even *think* about * relocating an anchor to the following line, when [tag] digits * expansion pushes things too far in that direction.] */ if (fields_are_numbered()) { anchor = start_anchor->next; while (htline != FirstHTLine(HTMainText)) { while (anchor) { if ((anchor->number - newlines) == start_tag) break; /*** A HANG (infinite loop) *has* occurred here, with */ /*** the values of anchor and anchor->next being the */ /*** the same, OR with anchor->number "magically" and */ /*** suddenly taking on an anchor-pointer-like value. */ /*** */ /*** The same code and same doc have both passed and */ /*** failed at different times, which indicates some */ /*** sort of content/html dependency, or some kind of */ /*** a "race" condition, but I'll be damned if I can */ /*** find it after tons of CTRACE's, printf()'s, gdb */ /*** breakpoints and watchpoints, etc. */ /*** */ /*** I have added a hang detector (with error msg and */ /*** beep) here, to break the loop and warn the user, */ /*** until it can be isolated and fixed. */ /*** */ /*** [One UGLY intermittent .. gak ..! 02/22/99 KED] */ hang++; if ((anchor == anchor->next) || (hang >= hang_detect)) goto hang_detected; anchor = anchor->next; } if (anchor) { line_adj = increment_tagged_htline(htline, anchor, &lx, &start_tag, newlines, NOCHOP); htline->size = (unsigned short) (htline->size + line_adj); tag_adj += line_adj; } else { break; /* out of anchors ... we're done */ } htline = htline->next; } } finish: /* * Fixup various global variables. */ nlinks += newlines; HTMainText->Lines += newlines; HTMainText->last_anchor_number += newlines; more_text = HText_canScrollDown(); CTRACE((tfp, "GridText: TextAnchor and HTLine struct's adjusted\n")); return; hang_detected: /* ugliness has happened; inform user and do the best we can */ HTAlert(gettext("Hang Detect: TextAnchor struct corrupted - suggest aborting!")); goto finish; } /* * Check if the given anchor is a TEXTAREA belonging to the given form. * * KED's note - * [Finding the TEXTAREA we're actually *in* with these attributes isn't * foolproof. The form number isn't unique to a given TEXTAREA, and there * *could* be TEXTAREA's with the same "name". If that should ever be true, * we'll actually get the data from the *1st* TEXTAREA in the page that * matches. We should probably assign a unique id to each TEXTAREA in a page, * and match on that, to avoid this (potential) problem. * * Since the odds of "false matches" *actually* happening in real life seem * rather small though, we'll hold off doing this, for a rainy day ...] */ static BOOLEAN IsFormsTextarea(FormInfo * form, TextAnchor *anchor_ptr) { return (BOOLEAN) ((anchor_ptr->link_type == INPUT_ANCHOR) && (anchor_ptr->input_field->type == F_TEXTAREA_TYPE) && (anchor_ptr->input_field->number == form->number) && !strcmp(anchor_ptr->input_field->name, form->name)); } static char *readEditedFile(char *ed_temp) { struct stat stat_info; size_t size; FILE *fp; char *ebuf; CTRACE((tfp, "GridText: entered HText_EditTextArea()\n")); /* * Read back the edited temp file into our buffer. */ if ((stat(ed_temp, &stat_info) < 0) || !S_ISREG(stat_info.st_mode) || ((size = (size_t) stat_info.st_size) == 0)) { size = 0; ebuf = typecalloc(char); if (!ebuf) outofmem(__FILE__, "HText_EditTextArea"); assert(ebuf != NULL); } else { ebuf = typecallocn(char, size + 1); if (!ebuf) { /* * This could be huge - don't exit if we don't have enough * memory for it. With some luck, the user may be even able * to recover the file manually from the temp space while * the lynx session is not over. - kw */ HTAlwaysAlert(NULL, MEMORY_EXHAUSTED_FILE); return 0; } assert(ebuf != NULL); if ((fp = fopen(ed_temp, "r")) != 0) { size = fread(ebuf, (size_t) 1, size, fp); LYCloseInput(fp); ebuf[size] = '\0'; /* Terminate! - kw */ } else { size = 0; } } /* * Nuke any blank lines from the end of the edited data. */ while ((size != 0) && (CanTrimTextArea(UCH(ebuf[size - 1])) || (ebuf[size - 1] == '\0'))) ebuf[--size] = '\0'; return ebuf; } static int finish_ExtEditForm(LinkInfo * form_link, TextAnchor *start_anchor, char *ed_temp, int orig_cnt) { TextAnchor *anchor_ptr; TextAnchor *end_anchor = NULL; BOOLEAN wrapalert = FALSE; int entry_line = form_link->anchor_line_num; int exit_line = 0; int line_cnt = 1; HTLine *htline = NULL; char *ebuf; char *line; char *lp; char *cp; int match_tag = 0; int newlines = 0; int len, len0; int display_size; int wanted_fieldlen_wrap = -1; /* not yet asked; 0 means don't. */ char *skip_at = NULL; int skip_num = 0, i; size_t line_used = MAX_LINE; CTRACE((tfp, "GridText: entered HText_EditTextArea()\n")); if ((ebuf = readEditedFile(ed_temp)) == 0) { return 0; } /* * Copy each line from the temp file into the corresponding anchor * struct. Add new lines to the TEXTAREA if needed. (Always leave * the user with a blank line at the end of the TEXTAREA.) */ if ((line = typeMallocn(char, line_used)) == 0) outofmem(__FILE__, "HText_EditTextArea"); assert(line != NULL); anchor_ptr = start_anchor; display_size = anchor_ptr->input_field->size; if (display_size <= 4 || display_size >= MAX_LINE) wanted_fieldlen_wrap = 0; len = 0; lp = ebuf; while ((line_cnt <= orig_cnt) || (*lp) || ((len != 0) && (*lp == '\0'))) { if (skip_at) { len0 = (int) (skip_at - lp); LYStrNCpy(line, lp, len0); lp = skip_at + skip_num; skip_at = NULL; skip_num = 0; assert(lp != NULL); } else { len0 = 0; } line[len0] = '\0'; if ((cp = StrChr(lp, '\n')) != 0) len = (int) (cp - lp); else len = (int) strlen(lp); if (wanted_fieldlen_wrap < 0 && !wrapalert && len0 + len >= display_size && (cp = StrChr(lp, ' ')) != NULL && (cp - lp) < display_size - 1) { LYFixCursesOn("ask for confirmation:"); LYerase(); /* don't show previous state */ if (HTConfirmDefault(gettext("Wrap lines to fit displayed area?"), NO)) { wanted_fieldlen_wrap = display_size - 1; } else { wanted_fieldlen_wrap = 0; } } if (wanted_fieldlen_wrap > 0 && len0 + len > wanted_fieldlen_wrap) { for (i = wanted_fieldlen_wrap - len0; i + len0 >= wanted_fieldlen_wrap / 4; i--) { if (isspace(UCH(lp[i]))) { len = i + 1; cp = lp + i; if (cp[1] != '\n' && isspace(UCH(cp[1])) && !isspace(UCH(cp[2]))) { len++; cp++; } if (!isspace(UCH(cp[1]))) { while (*cp && *cp != '\r' && *cp != '\n' && (cp - lp) <= len + (3 * wanted_fieldlen_wrap / 4)) cp++; /* search for next line break */ if (*cp == '\r' && cp[1] == '\n') cp++; if (*cp == '\n' && (cp[1] == '\r' || cp[1] == '\n' || !isspace(UCH(cp[1])))) { *cp = ' '; while (isspace(UCH(*(cp - 1)))) { skip_num++; cp--; } skip_at = cp; } } break; } } } if (wanted_fieldlen_wrap > 0 && (len0 + len) > wanted_fieldlen_wrap) { i = len - 1; while (len0 + i + 1 > wanted_fieldlen_wrap && isspace(UCH(lp[i]))) i--; if (len0 + i + 1 > wanted_fieldlen_wrap) len = wanted_fieldlen_wrap - len0; } /* * Check if the new text will fit in the buffer. HTML does not define * a "maxlength" attribute for TEXTAREA; its data can grow as needed. * Lynx will not adjust the display to reflect larger amounts of text; * it relies on the rows/cols attributes as well as the initial content * of the text area for the layout. */ if ((size_t) (len0 + len) >= line_used) { line_used = (size_t) (3 * (len0 + len)) / 2; if ((line = typeRealloc(char, line, line_used)) == 0) outofmem(__FILE__, "HText_EditTextArea"); } strncat(line, lp, (size_t) len); *(line + len0 + len) = '\0'; /* * If there are more lines in the edit buffer than were in the * original TEXTAREA, we need to add a new line/anchor, continuing * on until the edit buffer is empty. */ if (line_cnt > orig_cnt) { insert_new_textarea_anchor(&end_anchor, &htline); assert(end_anchor != NULL); assert(end_anchor->input_field != NULL); anchor_ptr = end_anchor; /* make the new anchor current */ newlines++; } assert(anchor_ptr != NULL); /* * Finally copy the new line from the edit buffer into the anchor. */ StrAllocCopy(anchor_ptr->input_field->value, line); /* * Keep track of 1st blank line in any trailing blank lines, for * later cursor repositioning. */ if (len0 + len > 0) exit_line = 0; else if (exit_line == 0) exit_line = anchor_ptr->line_num; /* * And do the next line of edited text, for the next anchor ... */ lp += len; if (*lp && isspace(UCH(*lp))) lp++; end_anchor = anchor_ptr; anchor_ptr = anchor_ptr->next; if (anchor_ptr) match_tag = anchor_ptr->number; line_cnt++; } CTRACE((tfp, "GridText: edited text inserted into lynx struct's\n")); /* * If we've added any new lines/anchors, we need to adjust various * things in all anchor-bearing lines following the last newly added * line/anchor. The fun stuff starts here ... */ if (newlines > 0) update_subsequent_anchors(newlines, end_anchor, htline, match_tag); /* * Cleanup time. */ FREE(line); FREE(ebuf); /* * Return the offset needed to move the cursor from its current * (on entry) line number, to the 1st blank line of the trailing * (group of) blank line(s), which is where we want to be. Let * the caller deal with moving us there, however ... :-) ... */ return (exit_line - entry_line); } /* * Transfer the initial contents of a TEXTAREA to a temp file, invoke the * user's editor on that file, then transfer the contents of the resultant * edited file back into the TEXTAREA (expanding the size of the area, if * required). * * Returns the number of lines that the cursor should be moved so that it * will end up on the 1st blank line of whatever number of trailing blank * lines there are in the TEXTAREA (there will *always* be at least one). * * --KED 02/01/99 */ int HText_EditTextArea(LinkInfo * form_link) { char *ed_temp; FILE *fp; TextAnchor *anchor_ptr; TextAnchor *start_anchor = NULL; BOOLEAN firstanchor = TRUE; char ed_offset[10]; int start_line = 0; int entry_line = form_link->anchor_line_num; int orig_cnt = 0; int offset = 0; FormInfo *form = form_link->l_form; CTRACE((tfp, "GridText: entered HText_EditTextArea()\n")); if ((ed_temp = typeMallocn(char, LY_MAXPATH)) == 0) { outofmem(__FILE__, "HText_EditTextArea"); } else if ((fp = LYOpenTemp(ed_temp, "", "w")) != 0) { /* * Begin at the beginning, to find 1st anchor in the TEXTAREA, then * write all of its lines (anchors) out to the edit temp file. */ anchor_ptr = HTMainText->first_anchor; while (anchor_ptr) { if (IsFormsTextarea(form, anchor_ptr)) { if (firstanchor) { firstanchor = FALSE; start_anchor = anchor_ptr; start_line = anchor_ptr->line_num; } orig_cnt++; /* * Write the anchors' text to the temp edit file. */ fputs(anchor_ptr->input_field->value, fp); fputc('\n', fp); } else { if (!firstanchor) break; } anchor_ptr = anchor_ptr->next; } LYCloseTempFP(fp); if (start_anchor != 0) { CTRACE((tfp, "GridText: TEXTAREA name=|%s| dumped to tempfile\n", form->name)); CTRACE((tfp, "GridText: invoking editor (%s) on tempfile\n", editor)); /* * Go edit the TEXTAREA temp file, with the initial editor line * corresponding to the TEXTAREA line the cursor is on (if such * positioning is supported by the editor [as lynx knows it]). */ ed_offset[0] = 0; /* pre-ANSI compilers don't initialize aggregates - TD */ if (((entry_line - start_line) > 0) && editor_can_position()) sprintf(ed_offset, "%d", ((entry_line - start_line) + 1)); edit_temporary_file(ed_temp, ed_offset, NULL); CTRACE((tfp, "GridText: returned from editor (%s)\n", editor)); if (!form->disabled) offset = finish_ExtEditForm(form_link, start_anchor, ed_temp, orig_cnt); CTRACE((tfp, "GridText: exiting HText_EditTextArea()\n")); } (void) LYRemoveTemp(ed_temp); FREE(ed_temp); } /* * Return the offset needed to move the cursor from its current * (on entry) line number, to the 1st blank line of the trailing * (group of) blank line(s), which is where we want to be. Let * the caller deal with moving us there, however ... :-) ... */ return offset; } /* * Similar to HText_EditTextArea, but assume a single-line text field -TD */ void HText_EditTextField(LinkInfo * form_link) { char *ed_temp; FILE *fp; FormInfo *form = form_link->l_form; CTRACE((tfp, "GridText: entered HText_EditTextField()\n")); ed_temp = typeMallocn(char, LY_MAXPATH); if ((fp = LYOpenTemp(ed_temp, "", "w")) == 0) { FREE(ed_temp); return; } /* * Write the anchors' text to the temp edit file. */ fputs(form->value, fp); fputc('\n', fp); LYCloseTempFP(fp); CTRACE((tfp, "GridText: text field |%s| dumped to tempfile\n", form_link->lname)); CTRACE((tfp, "GridText: invoking editor (%s) on tempfile\n", editor)); edit_temporary_file(ed_temp, "", NULL); CTRACE((tfp, "GridText: returned from editor (%s)\n", editor)); if (!form->disabled) { char *ebuf; char *p; if ((ebuf = readEditedFile(ed_temp)) != 0) { /* * Only use the first line of the result. */ for (p = ebuf; *p != '\0'; ++p) { if (*p == '\n' || *p == '\r') { *p = '\0'; break; } } StrAllocCopy(form->value, ebuf); FREE(ebuf); } } (void) LYRemoveTemp(ed_temp); FREE(ed_temp); CTRACE((tfp, "GridText: exiting HText_EditTextField()\n")); } /* * Expand the size of a TEXTAREA by a fixed number of lines (as specified * by arg2). * * --KED 02/14/99 */ void HText_ExpandTextarea(LinkInfo * form_link, int newlines) { TextAnchor *anchor_ptr; TextAnchor *end_anchor = NULL; BOOLEAN firstanchor = TRUE; FormInfo *form = form_link->l_form; HTLine *htline = NULL; int match_tag = 0; int i; CTRACE((tfp, "GridText: entered HText_ExpandTextarea()\n")); if (newlines < 1) return; /* * Begin at the beginning, to find the TEXTAREA, then on to find * the last line (anchor) in it. */ anchor_ptr = HTMainText->first_anchor; while (anchor_ptr) { if (IsFormsTextarea(form, anchor_ptr)) { if (firstanchor) firstanchor = FALSE; end_anchor = anchor_ptr; } else { if (!firstanchor) break; } anchor_ptr = anchor_ptr->next; } for (i = 1; i <= newlines; i++) { insert_new_textarea_anchor(&end_anchor, &htline); /* * Make the new line blank. */ StrAllocCopy(end_anchor->input_field->value, ""); /* * And go add another line ... */ if (end_anchor->next) match_tag = end_anchor->next->number; } CTRACE((tfp, "GridText: %d blank line(s) added to TEXTAREA name=|%s|\n", newlines, form->name)); /* * We need to adjust various things in all anchor bearing lines * following the last newly added line/anchor. Fun stuff. */ update_subsequent_anchors(newlines, end_anchor, htline, match_tag); CTRACE((tfp, "GridText: exiting HText_ExpandTextarea()\n")); return; } /* * Insert the contents of a file into a TEXTAREA between the cursor line, * and the line preceding it. * * Returns the number of lines that the cursor should be moved so that it * will end up on the 1st line in the TEXTAREA following the inserted file * (if we decide to do that). * * --KED 02/21/99 */ int HText_InsertFile(LinkInfo * form_link) { struct stat stat_info; size_t size; FILE *fp; char *fn; TextAnchor *anchor_ptr; TextAnchor *prev_anchor = NULL; TextAnchor *end_anchor = NULL; BOOLEAN firstanchor = TRUE; BOOLEAN truncalert = FALSE; FormInfo *form = form_link->l_form; HTLine *htline = NULL; TextAnchor *a = 0; FormInfo *f = 0; HTLine *l = 0; char *fbuf = 0; char *line = 0; char *lp; char *cp; int entry_line = form_link->anchor_line_num; int file_cs; int match_tag = 0; int newlines = 0; int len; int i; CTRACE((tfp, "GridText: entered HText_InsertFile()\n")); /* * Get the filename of the insert file. */ if (!(fn = GetFileName())) { HTInfoMsg(FILE_INSERT_CANCELLED); CTRACE((tfp, "GridText: file insert cancelled - no filename provided\n")); return (0); } if (no_dotfiles || !show_dotfiles) { if (*LYPathLeaf(fn) == '.') { HTUserMsg(FILENAME_CANNOT_BE_DOT); return (0); } } /* * Read it into our buffer (abort on 0-length file). */ if ((stat(fn, &stat_info) < 0) || ((size = (size_t) stat_info.st_size) == 0)) { HTInfoMsg(FILE_INSERT_0_LENGTH); CTRACE((tfp, "GridText: file insert aborted - file=|%s|- was 0-length\n", fn)); FREE(fn); return (0); } else { if ((fbuf = typecallocn(char, size + 1)) == NULL) { /* * This could be huge - don't exit if we don't have enough * memory for it. - kw */ free(fn); HTAlert(MEMORY_EXHAUSTED_FILE); return 0; } /* Try to make the same assumption for the charset of the inserted * file as we would for normal loading of that file, i.e. taking * assume_local_charset and suffix mappings into account. * If there is a mismatch with the display character set, characters * may be displayed wrong, too bad; but the user has a chance to * correct this by editing the lines, which will update f->value_cs * again. - kw */ LYGetFileInfo(fn, 0, 0, 0, 0, 0, &file_cs); fp = fopen(fn, "r"); if (!fp) { free(fbuf); free(fn); HTAlert(FILE_CANNOT_OPEN_R); return 0; } size = fread(fbuf, (size_t) 1, size, fp); LYCloseInput(fp); FREE(fn); fbuf[size] = '\0'; /* Terminate! - kw */ } /* * Begin at the beginning, to find the TEXTAREA we're in, then * the current cursorline. */ anchor_ptr = HTMainText->first_anchor; while (anchor_ptr) { if (IsFormsTextarea(form, anchor_ptr)) { if (anchor_ptr->line_num == entry_line) break; } prev_anchor = anchor_ptr; anchor_ptr = anchor_ptr->next; } if (anchor_ptr == NULL) { CTRACE((tfp, "BUG: could not find anchor for TEXTAREA.\n")); FREE(line); FREE(fbuf); return 0; } /* * Clone a new TEXTAREA line/anchor using the cursorline anchor as * a template, but link it in BEFORE the cursorline anchor/htline. * * [We can probably combine this with insert_new_textarea_anchor() * along with a flag to indicate "insert before" as we do here, * or the "normal" mode of operation (add after "current" anchor/ * line). Beware of the differences ... some are a bit subtle to * notice.] */ for (htline = FirstHTLine(HTMainText), i = 0; anchor_ptr->line_num != i; i++) { htline = htline->next; if (htline == HTMainText->last_line) break; } allocHTLine(l, MAX_LINE); POOLtypecalloc(TextAnchor, a); POOLtypecalloc(FormInfo, f); if (a == NULL || l == NULL || f == NULL) outofmem(__FILE__, "HText_InsertFile"); assert(a != NULL); assert(f != NULL); assert(l != NULL); /* Init all the fields in the new TextAnchor. */ /* [anything "special" needed based on ->show_anchor value ?] */ /* *INDENT-EQLS* */ a->next = anchor_ptr; a->number = anchor_ptr->number; a->show_number = anchor_ptr->show_number; a->line_pos = anchor_ptr->line_pos; a->extent = anchor_ptr->extent; a->sgml_offset = SGML_offset(); a->line_num = anchor_ptr->line_num; LYCopyHiText(a, anchor_ptr); a->link_type = anchor_ptr->link_type; a->input_field = f; a->show_anchor = anchor_ptr->show_anchor; a->inUnderline = anchor_ptr->inUnderline; a->expansion_anch = TRUE; a->anchor = NULL; /* Just the (seemingly) relevant fields in the new FormInfo. */ /* [do we need to do anything "special" based on ->disabled] */ StrAllocCopy(f->name, anchor_ptr->input_field->name); f->number = anchor_ptr->input_field->number; f->type = anchor_ptr->input_field->type; StrAllocCopy(f->orig_value, ""); f->size = anchor_ptr->input_field->size; f->maxlength = anchor_ptr->input_field->maxlength; f->no_cache = anchor_ptr->input_field->no_cache; f->disabled = anchor_ptr->input_field->disabled; f->readonly = anchor_ptr->input_field->readonly; f->value_cs = (file_cs >= 0) ? file_cs : current_char_set; /* Init all the fields in the new HTLine (but see the #if). */ l->offset = htline->offset; l->size = htline->size; #if defined(USE_COLOR_STYLE) /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */ l->numstyles = htline->numstyles; /*we fork the pointers! */ l->styles = htline->styles; #endif strcpy(l->data, htline->data); /* * If we're at the head of the TextAnchor list, the new node becomes * the first node. */ if (anchor_ptr == HTMainText->first_anchor) HTMainText->first_anchor = a; /* * Link in the new TextAnchor, and corresponding HTLine. */ if (prev_anchor) prev_anchor->next = a; htline = htline->prev; l->next = htline->next; l->prev = htline; htline->next->prev = l; htline->next = l; /* * update_subsequent_anchors() expects htline to point to 1st potential * line needing fixup; we need to do this just in case the inserted file * was only a single line (yes, it's pathological ... ). */ htline = htline->next; /* ->new (current) htline, for 1st inserted line */ htline = htline->next; /* ->1st potential (following) [tag] fixup htline */ anchor_ptr = a; newlines++; /* * Copy each line from the insert file into the corresponding anchor * struct. * * Begin with the new line/anchor we just added (above the cursorline). */ if ((line = typeMallocn(char, MAX_LINE)) == 0) outofmem(__FILE__, "HText_InsertFile"); assert(line != NULL); match_tag = anchor_ptr->number; lp = fbuf; while (*lp) { if ((cp = StrChr(lp, '\n')) != 0) len = (int) (cp - lp); else len = (int) strlen(lp); if (len >= MAX_LINE) { if (!truncalert) { HTAlert(gettext("Very long lines have been truncated!")); truncalert = TRUE; } len = MAX_LINE - 1; if (lp[len]) lp[len + 1] = '\0'; /* prevent next iteration */ } LYStrNCpy(line, lp, len); /* * If not the first line from the insert file, we need to add * a new line/anchor, continuing on until the buffer is empty. */ if (!firstanchor) { insert_new_textarea_anchor(&end_anchor, &htline); anchor_ptr = end_anchor; /* make the new anchor current */ newlines++; } /* * Copy the new line from the buffer into the anchor. */ StrAllocCopy(anchor_ptr->input_field->value, line); /* * insert_new_textarea_anchor always uses current_char_set, * we may want something else, so fix it up. - kw */ if (file_cs >= 0) anchor_ptr->input_field->value_cs = file_cs; /* * And do the next line of insert text, for the next anchor ... */ lp += len; if (*lp) lp++; firstanchor = FALSE; end_anchor = anchor_ptr; anchor_ptr = anchor_ptr->next; } CTRACE((tfp, "GridText: file inserted into lynx struct's\n")); /* * Now adjust various things in all anchor-bearing lines following the * last newly added line/anchor. Some say this is the fun part ... */ update_subsequent_anchors(newlines, end_anchor, htline, match_tag); /* * Cleanup time. */ FREE(line); FREE(fbuf); CTRACE((tfp, "GridText: exiting HText_InsertFile()\n")); return (newlines); } #ifdef USE_COLOR_STYLE static int GetColumn(void) { int result; #ifdef USE_SLANG result = SLsmg_get_column(); #else int y, x; LYGetYX(y, x); result = x; (void) y; #endif return result; } static BOOL DidWrap(int y0, int x0) { BOOL result = NO; #ifndef USE_SLANG int y, x; LYGetYX(y, x); (void) x0; if (x >= DISPLAY_COLS || ((x == 0) && (y != y0))) result = YES; #endif return result; } #endif /* USE_COLOR_STYLE */ /* * This function draws the part of line 'line', pointed by 'str' (which can be * non terminated with null - i.e., is line->data+N) drawing 'len' bytes (not * characters) of it. It doesn't check whether the 'len' bytes crosses a * character boundary (if multibyte chars are in string). Assumes that the * cursor is positioned in the place where the 1st char of string should be * drawn. * * This code is based on display_line. This code was tested with ncurses only * (since no support for lss is availble for Slang) -HV. */ #ifdef USE_COLOR_STYLE static void redraw_part_of_line(HTLine *line, const char *str, int len, HText *text) { register int i; char buffer[7]; const char *data, *end_of_data; size_t utf_extra = 0; #ifdef USE_COLOR_STYLE int current_style = 0; int tcols, scols; #endif char LastDisplayChar = ' '; int YP, XP; LYGetYX(YP, XP); i = XP; /* Set up the multibyte character buffer */ buffer[0] = buffer[1] = buffer[2] = '\0'; data = str; end_of_data = data + len; i++; /* this assumes that the part of line to be drawn fits in the screen */ while (data < end_of_data) { buffer[0] = *data; data++; #if defined(USE_COLOR_STYLE) #define CStyle line->styles[current_style] tcols = GetColumn(); scols = StyleToCols(text, line, current_style); while (current_style < line->numstyles && tcols >= scols) { LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction); current_style++; scols = StyleToCols(text, line, current_style); } #endif switch (buffer[0]) { #ifndef USE_COLOR_STYLE case LY_UNDERLINE_START_CHAR: if (dump_output_immediately && use_underscore) { LYaddch('_'); i++; } else { lynx_start_underline(); } break; case LY_UNDERLINE_END_CHAR: if (dump_output_immediately && use_underscore) { LYaddch('_'); i++; } else { lynx_stop_underline(); } break; case LY_BOLD_START_CHAR: lynx_start_bold(); break; case LY_BOLD_END_CHAR: lynx_stop_bold(); break; #endif case LY_SOFT_NEWLINE: if (!dump_output_immediately) { LYaddch('+'); i++; } break; case LY_SOFT_HYPHEN: if (*data != '\0' || isspace(UCH(LastDisplayChar)) || LastDisplayChar == '-') { /* * Ignore the soft hyphen if it is not the last character in * the line. Also ignore it if it first character following * the margin, or if it is preceded by a white character (we * loaded 'M' into LastDisplayChar if it was a multibyte * character) or hyphen, though it should have been excluded by * HText_appendCharacter() or by split_line() in those cases. * -FM */ break; } else { /* * Make it a hard hyphen and fall through. -FM */ buffer[0] = '-'; } /* FALLTHRU */ default: if (text->T.output_utf8 && is8bits(buffer[0])) { utf_extra = utf8_length(text->T.output_utf8, data - 1); LastDisplayChar = 'M'; } if (utf_extra) { LYStrNCpy(&buffer[1], data, utf_extra); LYaddstr(buffer); buffer[1] = '\0'; data += utf_extra; utf_extra = 0; } else if (is_CJK2(buffer[0])) { /* * For CJK strings, by Masanobu Kimura. */ if (i <= DISPLAY_COLS) { buffer[1] = *data; buffer[2] = '\0'; data++; i++; LYaddstr(buffer); buffer[1] = '\0'; /* * For now, load 'M' into LastDisplayChar, but we should * check whether it's white and if so, use ' '. I don't * know if there actually are white CJK characters, and * we're loading ' ' for multibyte spacing characters in * this code set, but this will become an issue when the * development code set's multibyte character handling is * used. -FM */ LastDisplayChar = 'M'; } } else { LYaddstr(buffer); LastDisplayChar = buffer[0]; } if (DidWrap(YP, XP)) break; i++; } /* end of switch */ } /* end of while */ #ifndef USE_COLOR_STYLE lynx_stop_underline(); lynx_stop_bold(); #else while (current_style < line->numstyles) { LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction); current_style++; } #undef CStyle #endif return; } #endif /* USE_COLOR_STYLE */ #ifndef USE_COLOR_STYLE /* * Function move_to_glyph is called from LYMoveToLink and does all * the real work for it. * The pair LYMoveToLink()/move_to_glyph() is similar to the pair * redraw_lines_of_link()/redraw_part_of_line(), some key differences: * LYMoveToLink/move_to_glyph redraw_* * ----------------------------------------------------------------- * - used without color style - used with color style * - handles showing WHEREIS target - WHEREIS handled elsewhere * - handles only one line - handles first two lines for * hypertext anchors * - right columns position for UTF-8 * by redrawing as necessary * - currently used for highlight - currently used for highlight * ON and OFF OFF * * Eventually the two sets of function should be unified, and should handle * UTF-8 positioning, both lines of hypertext anchors, and WHEREIS in all * cases. If possible. The complex WHEREIS target logic in LYhighlight() * could then be completely removed. - kw */ static void move_to_glyph(int YP, int XP, int XP_draw_min, const char *data, int datasize, unsigned offset, const char *target, const char *hightext, int flags, int utf_flag) { char buffer[7]; const char *end_of_data; size_t utf_extra = 0; #if defined(SHOW_WHEREIS_TARGETS) const char *cp_tgt; int i_start_tgt = 0, i_after_tgt; int HitOffset, LenNeeded; #endif /* SHOW_WHEREIS_TARGETS */ BOOL intarget = NO; BOOL inunderline = NO; BOOL inbold = NO; BOOL drawing = NO; BOOL inU = NO; BOOL hadutf8 = NO; BOOL incurlink = NO; BOOL drawingtarget = NO; BOOL flag = NO; const char *sdata = data; char LastDisplayChar = ' '; int i = (int) offset; /* FIXME: should be columns, not offset? */ int last_i = DISPLAY_COLS; int XP_link = XP; /* column of link */ int XP_next = XP; /* column to move to when done drawing */ int linkvlen; int len; if (no_title) YP -= TITLE_LINES; if (flags & 1) flag = YES; if (flags & 2) inU = YES; /* Set up the multibyte character buffer */ buffer[0] = buffer[1] = buffer[2] = '\0'; /* * Add offset, making sure that we do not * go over the COLS limit on the display. */ if (hightext != 0) { #ifdef WIDEC_CURSES last_i = i + LYstrExtent2(data, datasize); #endif linkvlen = LYmbcsstrlen(hightext, utf_flag, YES); } else { linkvlen = 0; } if (i >= last_i) i = last_i - 1; /* * Scan through the data, making sure that we do not * go over the COLS limit on the display etc. */ len = datasize; end_of_data = data + len; #if defined(SHOW_WHEREIS_TARGETS) /* * If the target overlaps with the part of this line that * we are drawing, it will be emphasized. */ i_after_tgt = i; if (target) { cp_tgt = LYno_attr_mb_strstr(sdata, target, utf_flag, YES, &HitOffset, &LenNeeded); if (cp_tgt) { if ((int) offset + LenNeeded > last_i || ((int) offset + HitOffset >= XP + linkvlen)) { cp_tgt = NULL; } else { i_start_tgt = i + HitOffset; i_after_tgt = i + LenNeeded; } } } else { cp_tgt = NULL; } #endif /* SHOW_WHEREIS_TARGETS */ /* * Iterate through the line data from the start, keeping track of * the display ("glyph") position in i. Drawing will be turned * on when either the first UTF-8 sequence (that occurs after * XP_draw_min) is found, or when we reach the link itself (if * highlight is non-NULL). - kw */ while ((i <= last_i) && data < end_of_data && (*data != '\0')) { if (hightext && i >= XP && !incurlink) { /* * We reached the position of link itself, and hightext is * non-NULL. We switch data from being a pointer into the HTLine * to being a pointer into hightext. Normally (as long as this * routine is applied to normal hyperlink anchors) the text in * hightext will be identical to that part of the HTLine that * data was already pointing to, except that special attribute * chars LY_BOLD_START_CHAR etc., have been stripped out (see * HText_trimHightext). So the switching should not result in * any different display, but it ensures that it doesn't go * unnoticed if somehow hightext got messed up somewhere else. * This is also useful in preparation for using this function * for something else than normal hyperlink anchors, i.e., form * fields. * Turn on drawing here or make sure it gets turned on before the * next actual normal character is handled. - kw */ data = hightext; len = (int) strlen(hightext); end_of_data = hightext + len; last_i = i + len; XP_next += linkvlen; incurlink = YES; #ifdef SHOW_WHEREIS_TARGETS if (cp_tgt) { if (flag && i_after_tgt >= XP) i_after_tgt = XP - 1; } #endif /* * The logic of where to set in-target drawing target etc. * and when to react to it should be cleaned up (here and * further below). For now this seems to work but isn't * very clear. The complications arise from reproducing * the behavior (previously done in LYhighlight()) for target * strings that fall into or overlap a link: use target * emphasis for the target string, except for the first * and last character of the anchor text if the anchor is * highlighted as "current link". - kw */ if (!drawing) { #ifdef SHOW_WHEREIS_TARGETS if (intarget) { if (i_after_tgt > i) { LYmove(YP, i); if (flag) { drawing = YES; drawingtarget = NO; if (inunderline) inU = YES; lynx_start_link_color(flag, inU); } else { drawing = YES; drawingtarget = YES; LYstartTargetEmphasis(); } } } #endif /* SHOW_WHEREIS_TARGETS */ } else { #ifdef SHOW_WHEREIS_TARGETS if (intarget && i_after_tgt > i) { if (flag && (data == hightext)) { drawingtarget = NO; LYstopTargetEmphasis(); } } else if (!intarget) #endif /* SHOW_WHEREIS_TARGETS */ { if (inunderline) inU = YES; if (inunderline) lynx_stop_underline(); if (inbold) lynx_stop_bold(); lynx_start_link_color(flag, inU); } } } if (i >= last_i || data >= end_of_data) break; if ((buffer[0] = *data) == '\0') break; #if defined(SHOW_WHEREIS_TARGETS) /* * Look for a subsequent occurrence of the target string, * if we had a previous one and have now stepped past it. - kw */ if (cp_tgt && i >= i_after_tgt) { if (intarget) { if (incurlink && flag && i == last_i - 1) cp_tgt = NULL; else cp_tgt = LYno_attr_mb_strstr(sdata, target, utf_flag, YES, &HitOffset, &LenNeeded); if (cp_tgt) { i_start_tgt = i + HitOffset; i_after_tgt = i + LenNeeded; if (incurlink) { if (flag && i_start_tgt == XP_link) i_start_tgt++; if (flag && i_start_tgt == last_i - 1) i_start_tgt++; if (flag && i_after_tgt >= last_i) i_after_tgt = last_i - 1; if (flag && i_start_tgt >= last_i) cp_tgt = NULL; } else if (i_start_tgt == last_i) { if (flag) i_start_tgt++; } } if (!cp_tgt || i_start_tgt != i) { intarget = NO; if (drawing) { if (drawingtarget) { drawingtarget = NO; LYstopTargetEmphasis(); if (incurlink) { lynx_start_link_color(flag, inU); } } if (!incurlink) { if (inbold) lynx_start_bold(); if (inunderline) lynx_start_underline(); } } } } } #endif /* SHOW_WHEREIS_TARGETS */ /* * Advance data to point to the next input char (for the * next round). Advance sdata, used for searching for a * target string, so that they stay in synch. As long * as we are not within the highlight text, data and sdata * have identical values. After we have switched data to * point into hightext, sdata remains a pointer into the * HTLine (so that we don't miss a partial target match at * the end of the anchor text). So sdata has to sometimes * skip additional special attribute characters that are * not present in highlight in order to stay in synch. - kw */ data++; if (incurlink) { while (IsNormalChar(*sdata)) { ++sdata; } } switch (buffer[0]) { case LY_UNDERLINE_START_CHAR: if (!drawing || !incurlink) inunderline = YES; if (drawing && !intarget && !incurlink) lynx_start_underline(); break; case LY_UNDERLINE_END_CHAR: inunderline = NO; if (drawing && !intarget && !incurlink) lynx_stop_underline(); break; case LY_BOLD_START_CHAR: if (!drawing || !incurlink) inbold = YES; if (drawing && !intarget && !incurlink) lynx_start_bold(); break; case LY_BOLD_END_CHAR: inbold = NO; if (drawing && !intarget && !incurlink) lynx_stop_bold(); break; case LY_SOFT_NEWLINE: if (drawing) { LYaddch('+'); } i++; break; case LY_SOFT_HYPHEN: if (*data != '\0' || isspace(UCH(LastDisplayChar)) || LastDisplayChar == '-') { /* * Ignore the soft hyphen if it is not the last * character in the line. Also ignore it if it * first character following the margin, or if it * is preceded by a white character (we loaded 'M' * into LastDisplayChar if it was a multibyte * character) or hyphen, though it should have * been excluded by HText_appendCharacter() or by * split_line() in those cases. -FM */ break; } else { /* * Make it a hard hyphen and fall through. -FM */ buffer[0] = '-'; } /* FALLTHRU */ default: /* * We have got an actual normal displayable character, or * the start of one. Before proceeding check whether * drawing needs to be turned on now. - kw */ #if defined(SHOW_WHEREIS_TARGETS) if (incurlink && intarget && flag && i_after_tgt > i) { if (i == last_i - 1) { i_after_tgt = i; } else if (i == last_i - 2 && IS_CJK_TTY && is8bits(buffer[0])) { i_after_tgt = i; cp_tgt = NULL; if (drawing) { if (drawingtarget) { LYstopTargetEmphasis(); drawingtarget = NO; lynx_start_link_color(flag, inU); } } } } if (cp_tgt && i >= i_start_tgt && sdata > cp_tgt) { if (!intarget || (intarget && incurlink && !drawingtarget)) { if (incurlink && drawing && !(flag && (i == XP_link || i == last_i - 1))) { lynx_stop_link_color(flag, inU); } if (incurlink && !drawing) { LYmove(YP, i); if (inunderline) inU = YES; if (flag && (i == XP_link || i == last_i - 1)) { lynx_start_link_color(flag, inU); drawingtarget = NO; } else { LYstartTargetEmphasis(); drawingtarget = YES; } drawing = YES; } else if (incurlink && drawing && intarget && !drawingtarget && (flag && (i == XP_link))) { if (inunderline) inU = YES; lynx_start_link_color(flag, inU); } else if (drawing && !(flag && (i == XP_link || (incurlink && i == last_i - 1)))) { LYstartTargetEmphasis(); drawingtarget = YES; } intarget = YES; } } else #endif /* SHOW_WHEREIS_TARGETS */ if (incurlink) { if (!drawing) { LYmove(YP, i); if (inunderline) inU = YES; lynx_start_link_color(flag, inU); drawing = YES; } } i++; #ifndef WIDEC_CURSES if (utf_flag && is8bits(buffer[0])) { hadutf8 = YES; utf_extra = utf8_length(utf_flag, data - 1); LastDisplayChar = 'M'; } #endif if (utf_extra) { LYStrNCpy(&buffer[1], data, utf_extra); if (!drawing && i >= XP_draw_min) { LYmove(YP, i - 1); drawing = YES; #if defined(SHOW_WHEREIS_TARGETS) if (intarget) { drawingtarget = YES; LYstartTargetEmphasis(); } else #endif /* SHOW_WHEREIS_TARGETS */ { if (inbold) lynx_start_bold(); if (inunderline) lynx_start_underline(); } } LYaddstr(buffer); buffer[1] = '\0'; sdata += utf_extra; data += utf_extra; utf_extra = 0; } else if (IS_CJK_TTY && is8bits(buffer[0]) && (!conv_jisx0201kana && (kanji_code != SJIS))) { /* * For CJK strings, by Masanobu Kimura. */ if (drawing && (i <= last_i)) { buffer[1] = *data; LYaddstr(buffer); buffer[1] = '\0'; } i++; sdata++; data++; /* * For now, load 'M' into LastDisplayChar, but we should * check whether it's white and if so, use ' '. I don't * know if there actually are white CJK characters, and * we're loading ' ' for multibyte spacing characters in * this code set, but this will become an issue when the * development code set's multibyte character handling is * used. -FM */ LastDisplayChar = 'M'; } else { if (drawing) { LYaddstr(buffer); } LastDisplayChar = buffer[0]; } } /* end of switch */ } /* end of while */ if (!drawing) { LYmove(YP, XP_next); lynx_start_link_color(flag, inU); } else { #if defined(SHOW_WHEREIS_TARGETS) if (drawingtarget) { LYstopTargetEmphasis(); lynx_start_link_color(flag, inU); } #endif /* SHOW_WHEREIS_TARGETS */ if (hadutf8) { LYtouchline(YP); } } return; } #endif /* !USE_COLOR_STYLE */ #ifndef USE_COLOR_STYLE /* * Move cursor position to a link's place in the display. * The "moving to" is done by scanning through the line's * character data in the corresponding HTLine of HTMainText, * and starting to draw when a UTF-8 encoded non-ASCII character * is encountered before the link (with some protection against * overwriting form fields). This refreshing of preceding data is * necessary for preventing curses's or slang's display logic from * getting too clever; their logic counts character positions wrong * since they don't know about multi-byte characters that take up * only one screen position. So we have to make them forget their * idea of what's in a screen line drawn previously. * If hightext is non-NULL, it should be the anchor text for a normal * link as stored in a links[] element, and the anchor text will be * drawn too, with appropriate attributes. - kw */ void LYMoveToLink(int cur, const char *target, const char *hightext, int flag, int inU, int utf_flag) { #define pvtTITLE_HEIGHT 1 HTLine *todr; int i, n = 0; int XP_draw_min = 0; int flags = ((flag == TRUE) ? 1 : 0) | (inU ? 2 : 0); /* * We need to protect changed form text fields preceding this * link on the same line against overwriting. - kw */ for (i = cur - 1; i >= 0; i++) { if (links[i].ly < links[cur].ly) break; if (links[i].type == WWW_FORM_LINK_TYPE) { XP_draw_min = links[i].ly + links[i].l_form->size; break; } } /* Find the right HTLine. */ if (!HTMainText) { todr = NULL; } else if (HTMainText->stale) { todr = FirstHTLine(HTMainText); n = links[cur].ly - pvtTITLE_HEIGHT + HTMainText->top_of_screen; } else { todr = HTMainText->top_of_screen_line; n = links[cur].ly - pvtTITLE_HEIGHT; } for (i = 0; i < n && todr; i++) { todr = (todr == HTMainText->last_line) ? NULL : todr->next; } if (todr) { if (target && *target == '\0') target = NULL; move_to_glyph(links[cur].ly, links[cur].lx, XP_draw_min, todr->data, todr->size, todr->offset, target, hightext, flags, utf_flag); } else { /* This should not happen. */ move_to_glyph(links[cur].ly, links[cur].lx, XP_draw_min, "", 0, (unsigned) links[cur].lx, target, hightext, flags, utf_flag); } } #endif /* !USE_COLOR_STYLE */ /* * This is used only if compiled with lss support. It's called to redraw a * regular link when it's being unhighlighted in LYhighlight(). */ #ifdef USE_COLOR_STYLE void redraw_lines_of_link(int cur) { #define pvtTITLE_HEIGHT 1 HTLine *todr1; int lines_back; int row, col, count; const char *text; if (HTMainText->next_line == HTMainText->last_line) { /* we are at the last page - that is partially filled */ lines_back = HTMainText->Lines - (links[cur].ly - pvtTITLE_HEIGHT + HTMainText->top_of_screen); } else { lines_back = display_lines - (links[cur].ly - pvtTITLE_HEIGHT); } todr1 = HTMainText->next_line; while (lines_back-- > 0) todr1 = todr1->prev; row = links[cur].ly; if (no_title) row -= TITLE_LINES; for (count = 0; row <= display_lines && (text = LYGetHiliteStr(cur, count)) != NULL; ++count) { col = LYGetHilitePos(cur, count); if (col >= 0) { LYmove(row, col); redraw_part_of_line(todr1, text, (int) strlen(text), HTMainText); } todr1 = todr1->next; row++; } #undef pvtTITLE_HEIGHT return; } #endif #ifdef USE_PRETTYSRC void HTMark_asSource(void) { if (HTMainText) HTMainText->source = TRUE; } #endif HTkcode HText_getKcode(HText *text) { return text->kcode; } void HText_updateKcode(HText *text, HTkcode kcode) { text->kcode = kcode; } HTkcode HText_getSpecifiedKcode(HText *text) { return text->specified_kcode; } void HText_updateSpecifiedKcode(HText *text, HTkcode kcode) { text->specified_kcode = kcode; } int HTMainText_Get_UCLYhndl(void) { return (HTMainText ? HTAnchor_getUCLYhndl(HTMainText->node_anchor, UCT_STAGE_MIME) : -1); } #ifdef USE_CACHEJAR static int LYHandleCache(const char *arg, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *sink) { HTFormat format_in = WWW_HTML; HTStream *target = NULL; char c; char *buf = NULL; char *title = NULL; char *address = NULL; char *content_type = NULL; char *content_language = NULL; char *content_encoding = NULL; char *content_location = NULL; char *content_disposition = NULL; char *content_md5 = NULL; char *message_id = NULL; char *date = NULL; char *owner = NULL; char *subject = NULL; char *expires = NULL; char *ETag = NULL; char *server = NULL; char *FileCache = NULL; char *last_modified = NULL; char *cache_control = NULL; #ifdef USE_SOURCE_CACHE char *source_cache_file = NULL; #endif off_t Size = 0; int x = -1; /* * Check if there is something to do. */ if (HTList_count(loaded_texts) == 0) { HTProgress(CACHE_JAR_IS_EMPTY); LYSleepMsg(); HTNoDataOK = 1; return (HT_NO_DATA); } /* * If # of LYNXCACHE:/# is number ask user if he/she want to delete it. */ if (sscanf(arg, STR_LYNXCACHE "/%d", &x) == 1 && x > 0) { CTRACE((tfp, "LYNXCACHE number is %d\n", x)); _statusline(CACHE_D_OR_CANCEL); c = (char) LYgetch_single(); if (c == 'D') { HText *t = (HText *) HTList_objectAt(loaded_texts, x - 1); HTList_removeObjectAt(loaded_texts, x - 1); HText_free(t); } return (HT_NO_DATA); } /* * If we get to here, it was a LYNXCACHE:/ URL for creating and displaying * the Cache Jar Page. * Set up an HTML stream and return an updated Cache Jar Page. */ target = HTStreamStack(format_in, format_out, sink, anAnchor); if (!target || target == NULL) { HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O, HTAtom_name(format_in), HTAtom_name(format_out)); HTAlert(buf); FREE(buf); return (HT_NOT_LOADED); } /* * Load HTML strings into buf and pass buf to the target for parsing and * rendering. */ #define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf)) HTSprintf0(&buf, "\n\n%s\n\n\n", CACHE_JAR_TITLE); PUTS(buf); HTSprintf0(&buf, "

    %s (%s)%s%s

    \n", LYNX_NAME, LYNX_VERSION, HELP_ON_SEGMENT, helpfilepath, CACHE_JAR_HELP, CACHE_JAR_TITLE); PUTS(buf); /* * Max number of cached documents is always same as HTCacheSize. * We count them from oldest to newest. Currently cached document * is *never* listed, resulting in maximal entries of Cache Jar * to be HTCacheSize - 1 */ for (x = HTList_count(loaded_texts) - 1; x > 0; x--) { /* * The number of the document in the cache list, its title in a link, * and its address and memory allocated for each cached document. */ HText *cachedoc = (HText *) HTList_objectAt(loaded_texts, x); if (cachedoc != 0) { HTParentAnchor *docanchor = cachedoc->node_anchor; if (docanchor != 0) { #ifdef USE_SOURCE_CACHE source_cache_file = docanchor->source_cache_file; #endif Size = docanchor->content_length; StrAllocCopy(title, docanchor->title); StrAllocCopy(address, docanchor->address); content_type = docanchor->content_type; content_language = docanchor->content_language; content_encoding = docanchor->content_encoding; content_location = docanchor->content_location; content_disposition = docanchor->content_disposition; content_md5 = docanchor->content_md5; message_id = docanchor->message_id; owner = docanchor->owner; StrAllocCopy(subject, docanchor->subject); date = docanchor->date; expires = docanchor->expires; ETag = docanchor->ETag; StrAllocCopy(server, docanchor->server); FileCache = docanchor->FileCache; last_modified = docanchor->last_modified; cache_control = docanchor->cache_control; } } LYEntify(&address, TRUE); if (isEmpty(title)) StrAllocCopy(title, NO_TITLE); else LYEntify(&title, TRUE); HTSprintf0(&buf, "

    %d. Title: %s
    URL: %s
    ", x, STR_LYNXCACHE, x, title, address, address); PUTS(buf); if (Size > 0) { HTSprintf0(&buf, "Size: %" PRI_off_t " ", Size); PUTS(buf); } if (cachedoc != NULL && cachedoc->Lines > 0) { HTSprintf0(&buf, "Lines: %d ", cachedoc->Lines); PUTS(buf); } if (FileCache != NULL) { HTSprintf0(&buf, "File-Cache: %s ", FileCache, FileCache); PUTS(buf); } if (cache_control != NULL) { HTSprintf0(&buf, "Cache-Control: %s ", cache_control); PUTS(buf); } if (content_type != NULL) { HTSprintf0(&buf, "Content-Type: %s ", content_type); PUTS(buf); } if (content_language != NULL) { HTSprintf0(&buf, "Content-Language: %s ", content_language); PUTS(buf); } if (content_encoding != NULL) { HTSprintf0(&buf, "Content-Encoding: %s ", content_encoding); PUTS(buf); } if (content_location != NULL) { HTSprintf0(&buf, "Content-Location: %s ", content_location); PUTS(buf); } if (content_disposition != NULL) { HTSprintf0(&buf, "Content-Disposition: %s ", content_disposition); PUTS(buf); } if (content_md5 != NULL) { HTSprintf0(&buf, "Content-MD5: %s ", content_md5); PUTS(buf); } if (message_id != NULL) { HTSprintf0(&buf, "Message-ID: %s ", message_id); PUTS(buf); } if (subject != NULL) { LYEntify(&subject, TRUE); HTSprintf0(&buf, "Subject: %s ", subject); PUTS(buf); } if (owner != NULL) { HTSprintf0(&buf, "Owner: %s ", owner, owner); PUTS(buf); } if (date != NULL) { HTSprintf0(&buf, "Date: %s ", date); PUTS(buf); } if (expires != NULL) { HTSprintf0(&buf, "Expires: %s ", expires); PUTS(buf); } if (last_modified != NULL) { HTSprintf0(&buf, "Last-Modified: %s ", last_modified); PUTS(buf); } if (ETag != NULL) { HTSprintf0(&buf, "ETag: %s ", ETag); PUTS(buf); } if (server != NULL) { LYEntify(&server, TRUE); HTSprintf0(&buf, "Server: %s ", server); PUTS(buf); } #ifdef USE_SOURCE_CACHE if (source_cache_file != NULL) { HTSprintf0(&buf, "Source-Cache-File: %s", source_cache_file, source_cache_file); PUTS(buf); } #endif HTSprintf0(&buf, "
    "); PUTS(buf); } HTSprintf0(&buf, ""); PUTS(buf); FREE(subject); FREE(title); FREE(address); FREE(server); /* * Free the target to complete loading of the Cache Jar Page, and report a * successful load. */ (*target->isa->_free) (target); FREE(buf); return (HT_LOADED); } #ifdef GLOBALDEF_IS_MACRO #define _LYCACHE_C_GLOBALDEF_1_INIT { "LYNXCACHE",LYHandleCache,0} GLOBALDEF(HTProtocol, LYLynxCache, _LYCACHE_C_GLOBALDEF_1_INIT); #else GLOBALDEF HTProtocol LYLynxCache = {"LYNXCACHE", LYHandleCache, 0}; #endif /* GLOBALDEF_IS_MACRO */ #endif /* USE_CACHEJAR */ lynx2-8-8/src/GridText.h000644 023711 023712 00000023545 11716074571 015467 0ustar00dickeylynx000000 000000 /* * $LynxId: GridText.h,v 1.69 2012/02/12 23:25:38 tom Exp $ * * Specialities of GridText as subclass of HText */ #ifndef LYGRIDTEXT_H #define LYGRIDTEXT_H #include /* Superclass */ #ifndef HTFORMS_H #include #endif /* HTFORMS_H */ #include #include #ifdef __cplusplus extern "C" { #endif #define TABSTOP 8 #define SPACES " " /* must be at least TABSTOP spaces long */ #define SPLAT '.' #define NOCHOP 0 #define CHOP 1 /* just for information: US-ASCII control characters <32 which are not defined in Unicode standard =00 U+0000 NULL =01 U+0001 START OF HEADING =02 U+0002 START OF TEXT =03 U+0003 END OF TEXT =04 U+0004 END OF TRANSMISSION =05 U+0005 ENQUIRY =06 U+0006 ACKNOWLEDGE =07 U+0007 BELL =08 U+0008 BACKSPACE =09 U+0009 HORIZONTAL TABULATION =0A U+000A LINE FEED =0B U+000B VERTICAL TABULATION =0C U+000C FORM FEED =0D U+000D CARRIAGE RETURN =0E U+000E SHIFT OUT =0F U+000F SHIFT IN =10 U+0010 DATA LINK ESCAPE =11 U+0011 DEVICE CONTROL ONE =12 U+0012 DEVICE CONTROL TWO =13 U+0013 DEVICE CONTROL THREE =14 U+0014 DEVICE CONTROL FOUR =15 U+0015 NEGATIVE ACKNOWLEDGE =16 U+0016 SYNCHRONOUS IDLE =17 U+0017 END OF TRANSMISSION BLOCK =18 U+0018 CANCEL =19 U+0019 END OF MEDIUM =1A U+001A SUBSTITUTE =1B U+001B ESCAPE =1C U+001C FILE SEPARATOR =1D U+001D GROUP SEPARATOR =1E U+001E RECORD SEPARATOR =1F U+001F UNIT SEPARATOR =7F U+007F DELETE */ extern int HTCurSelectGroupType; extern char *HTCurSelectGroupSize; #if defined(VMS) && defined(VAXC) && !defined(__DECC) extern int HTVirtualMemorySize; #endif /* VMS && VAXC && !__DECC */ extern HTChildAnchor *HText_childNextNumber(int n, void **prev); extern int HText_findAnchorNumber(void *avoid); extern void HText_FormDescNumber(int n, const char **desc); /* Is there any file left? */ extern BOOL HText_canScrollUp(HText *text); extern BOOL HText_canScrollDown(void); /* Move display within window */ extern void HText_scrollUp(HText *text); /* One page */ extern void HText_scrollDown(HText *text); /* One page */ extern void HText_scrollTop(HText *text); extern void HText_scrollBottom(HText *text); extern void HText_pageDisplay(int line_num, char *target); extern BOOL HText_pageHasPrevTarget(void); extern int HText_LinksInLines(HText *text, int line_num, int Lines); extern int HText_getAbsLineNumber(HText *text, int anchor_number); extern int HText_closestAnchor(HText *text, int offset); extern int HText_locateAnchor(HText *text, int anchor_number); extern int HText_anchorRelativeTo(HText *text, int top_lineno, int anchor_num); extern void HText_setLastChar(HText *text, int ch); extern char HText_getLastChar(HText *text); extern int HText_sourceAnchors(HText *text); extern void HText_setStale(HText *text); extern void HText_refresh(HText *text); extern const char *HText_getTitle(void); extern const char *HText_getSugFname(void); extern void HTCheckFnameForCompression(char **fname, HTParentAnchor *anchor, int strip_ok); extern const char *HText_getLastModified(void); extern const char *HText_getDate(void); extern const char *HText_getHttpHeaders(void); extern const char *HText_getServer(void); extern const char *HText_getOwner(void); extern const char *HText_getContentBase(void); extern const char *HText_getContentLocation(void); extern const char *HText_getMessageID(void); extern const char *HText_getRevTitle(void); #ifdef USE_COLOR_STYLE extern const char *HText_getStyle(void); #endif extern void HText_setMainTextOwner(const char *owner); extern void print_wwwfile_to_fd(FILE *fp, int is_email, int is_reply); extern BOOL HText_select(HText *text); extern BOOL HText_POSTReplyLoaded(DocInfo *doc); extern BOOL HTFindPoundSelector(const char *selector); extern int HTGetRelLinkNum(int num, int rel, int cur); extern int HTGetLinkInfo(int number, int want_go, int *go_line, int *linknum, char **hightext, char **lname); extern BOOL HText_TAHasMoreLines(int curlink, int direction); extern int HTGetLinkOrFieldStart(int curlink, int *go_line, int *linknum, int direction, int ta_skip); extern BOOL HText_getFirstTargetInLine(HText *text, int line_num, int utf_flag, int *offset, int *tLen, char **data, const char *target); extern int HTisDocumentSource(void); extern void HTuncache_current_document(void); #ifdef USE_SOURCE_CACHE extern BOOLEAN HTreparse_document(void); extern BOOLEAN HTcan_reparse_document(void); extern BOOLEAN HTdocument_settings_changed(void); #endif extern BOOL HTLoadedDocumentEightbit(void); extern BOOL HText_LastLineEmpty(HText *me, int IgnoreSpaces); extern BOOL HText_PreviousLineEmpty(HText *me, int IgnoreSpaces); extern BOOL HText_inLineOne(HText *text); extern BOOLEAN HTLoadedDocumentIsHEAD(void); extern BOOLEAN HTLoadedDocumentIsSafe(void); extern bstring *HTLoadedDocumentPost_data(void); extern const char *HTLoadedDocumentBookmark(void); extern const char *HTLoadedDocumentCharset(void); extern const char *HTLoadedDocumentTitle(void); extern const char *HTLoadedDocumentURL(void); extern const char *HText_HiddenLinkAt(HText *text, int number); extern int HText_HiddenLinkCount(HText *text); extern int HText_LastLineOffset(HText *me); extern int HText_LastLineSize(HText *me, int IgnoreSpaces); extern int HText_PreviousLineSize(HText *me, int IgnoreSpaces); extern int HText_getCurrentColumn(HText *text); extern int HText_getLines(HText *text); extern int HText_getMaximumColumn(HText *text); extern int HText_getNumOfBytes(void); extern int HText_getNumOfLines(void); extern int HText_getPreferredTopLine(HText *text, int line_number); extern int HText_getTabIDColumn(HText *text, const char *name); extern int HText_getTopOfScreen(void); extern int do_www_search(DocInfo *doc); extern void HText_NegateLineOne(HText *text); extern void HText_RemovePreviousLine(HText *text); extern void HText_setNodeAnchorBookmark(const char *bookmark); extern void HText_setTabID(HText *text, const char *name); extern void *HText_pool_calloc(HText *text, unsigned size); /* "simple table" stuff */ extern BOOLEAN HText_endStblTABLE(HText *); extern int HText_trimCellLines(HText *text); extern void HText_cancelStbl(HText *); extern void HText_endStblCOLGROUP(HText *); extern void HText_endStblTD(HText *); extern void HText_endStblTR(HText *); extern void HText_startStblCOL(HText *, int, int, int); extern void HText_startStblRowGroup(HText *, int); extern void HText_startStblTABLE(HText *, int); extern void HText_startStblTD(HText *, int, int, int, int); extern void HText_startStblTR(HText *, int); /* forms stuff */ extern void HText_beginForm(char *action, char *method, char *enctype, char *title, const char *accept_cs); extern void HText_endForm(HText *text); extern void HText_beginSelect(char *name, int name_cs, int multiple, char *len); extern int HText_getOptionNum(HText *text); extern char *HText_setLastOptionValue(HText *text, char *value, char *submit_value, int order, int checked, int val_cs, int submit_val_cs); extern int HText_beginInput(HText *text, int underline, InputFieldData * I); extern void HText_endInput(HText *text); extern PerFormInfo *HText_PerFormInfo(int number); extern int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc, const char *link_name, const char *link_value); extern void HText_DisableCurrentForm(void); extern void HText_ResetForm(FormInfo * form); extern void HText_activateRadioButton(FormInfo * form); extern BOOLEAN HText_HaveUserChangedForms(HText *text); extern HTList *search_queries; /* Previous isindex and whereis queries */ extern void HTSearchQueries_free(void); extern void HTAddSearchQuery(char *query); extern void user_message(const char *message, const char *argument); #define _user_message(msg, arg) mustshow = TRUE, user_message(msg, arg) extern void www_user_search(int start_line, DocInfo *doc, char *target, int direction); extern void print_crawl_to_fd(FILE *fp, char *thelink, char *thetitle); extern char *stub_HTAnchor_address(HTAnchor * me); extern void HText_setToolbar(HText *text); extern BOOL HText_hasToolbar(HText *text); extern void HText_setNoCache(HText *text); extern BOOL HText_hasNoCacheSet(HText *text); extern BOOL HText_hasUTF8OutputSet(HText *text); extern void HText_setKcode(HText *text, const char *charset, LYUCcharset *p_in); extern void HText_setBreakPoint(HText *text); extern BOOL HText_AreDifferent(HTParentAnchor *anchor, const char *full_address); extern int HText_EditTextArea(LinkInfo * form_link); extern void HText_EditTextField(LinkInfo * form_link); extern void HText_ExpandTextarea(LinkInfo * form_link, int newlines); extern int HText_InsertFile(LinkInfo * form_link); extern void redraw_lines_of_link(int cur); extern void LYMoveToLink(int cur, const char *target, const char *hightext, int flag, int inU, int utf_flag); #ifdef USE_PRETTYSRC extern void HTMark_asSource(void); #endif extern int HTMainText_Get_UCLYhndl(void); #ifdef KANJI_CODE_OVERRIDE extern HTkcode last_kcode; #endif extern HTkcode HText_getKcode(HText *text); extern void HText_updateKcode(HText *text, HTkcode kcode); extern HTkcode HText_getSpecifiedKcode(HText *text); extern void HText_updateSpecifiedKcode(HText *text, HTkcode kcode); #ifdef __cplusplus } #endif #endif /* LYGRIDTEXT_H */ lynx2-8-8/src/HTAlert.c000644 023711 023712 00000072376 12245762550 015240 0ustar00dickeylynx000000 000000 /* * $LynxId: HTAlert.c,v 1.101 2013/11/28 11:17:04 tom Exp $ * * Displaying messages and getting input for Lynx Browser * ========================================================== * * REPLACE THIS MODULE with a GUI version in a GUI environment! * * History: * Jun 92 Created May 1992 By C.T. Barker * Feb 93 Simplified, portablised TBL * */ #include #include #include #include #include #include #include #include #include #include /* store statusline messages */ #include #include #undef timezone /* U/Win defines this in time.h, hides implementation detail */ #if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H) #include #endif /* * 'napms()' is preferable to 'sleep()' in any case because it does not * interfere with output, but also because it can be less than a second. */ #ifdef HAVE_NAPMS #define LYSleep(n) napms(n) #else #define LYSleep(n) sleep((unsigned)n) #endif /* Issue a message about a problem. HTAlert() * -------------------------------- */ void HTAlert(const char *Msg) { CTRACE((tfp, "\nAlert!: %s\n\n", Msg)); CTRACE_FLUSH(tfp); _user_message(ALERT_FORMAT, Msg); LYstore_message2(ALERT_FORMAT, Msg); if (dump_output_immediately && dump_to_stderr) { fflush(stdout); fprintf(stderr, ALERT_FORMAT, Msg); fputc('\n', stderr); fflush(stderr); } LYSleepAlert(); } void HTAlwaysAlert(const char *extra_prefix, const char *Msg) { if (!dump_output_immediately && LYCursesON) { HTAlert(Msg); } else { if (extra_prefix) { fprintf(((TRACE) ? stdout : stderr), "%s %s!\n", extra_prefix, Msg); fflush(stdout); LYstore_message2(ALERT_FORMAT, Msg); LYSleepAlert(); } else { fprintf(((TRACE) ? stdout : stderr), ALERT_FORMAT, NonNull(Msg)); fflush(stdout); LYstore_message2(ALERT_FORMAT, Msg); LYSleepAlert(); fprintf(((TRACE) ? stdout : stderr), "\n"); } CTRACE((tfp, "\nAlert!: %s\n\n", Msg)); CTRACE_FLUSH(tfp); } } /* Issue an informational message. HTInfoMsg() * -------------------------------- */ void HTInfoMsg(const char *Msg) { _statusline(Msg); if (non_empty(Msg)) { CTRACE((tfp, "Info message: %s\n", Msg)); LYstore_message(Msg); LYSleepInfo(); } } void HTInfoMsg2(const char *Msg2, const char *Arg) { _user_message(Msg2, Arg); if (non_empty(Msg2)) { CTRACE((tfp, "Info message: ")); CTRACE((tfp, Msg2, Arg)); CTRACE((tfp, "\n")); LYstore_message2(Msg2, Arg); LYSleepInfo(); } } /* Issue an important message. HTUserMsg() * -------------------------------- */ void HTUserMsg(const char *Msg) { _statusline(Msg); if (non_empty(Msg)) { CTRACE((tfp, "User message: %s\n", Msg)); LYstore_message(Msg); #if !(defined(USE_SLANG) || defined(WIDEC_CURSES)) if (IS_CJK_TTY) { clearok(curscr, TRUE); LYrefresh(); } #endif LYSleepMsg(); } } void HTUserMsg2(const char *Msg2, const char *Arg) { _user_message(Msg2, Arg); if (non_empty(Msg2)) { CTRACE((tfp, "User message: ")); CTRACE((tfp, Msg2, Arg)); CTRACE((tfp, "\n")); LYstore_message2(Msg2, Arg); LYSleepMsg(); } } /* Issue a progress message. HTProgress() * ------------------------- */ void HTProgress(const char *Msg) { statusline(Msg); LYstore_message(Msg); CTRACE((tfp, "%s\n", Msg)); LYSleepDelay(); } const char *HTProgressUnits(int rate) { static const char *bunits = 0; static const char *kbunits = 0; if (!bunits) { bunits = gettext("bytes"); kbunits = gettext(LYTransferName); } return ((rate == rateKB) #ifdef USE_READPROGRESS || (rate == rateEtaKB) || (rate == rateEtaKB2) #endif )? kbunits : bunits; } static const char *sprint_bytes(char *s, off_t n, const char *was_units) { static off_t kb_units = 1024; const char *u = HTProgressUnits(LYTransferRate); if (isRateInKB(LYTransferRate)) { if (n >= 10 * kb_units) { sprintf(s, "%" PRI_off_t, CAST_off_t (n / kb_units)); } else if (n > 999) { /* Avoid switching between 1016b/s and 1K/s */ sprintf(s, "%.2g", ((double) n) / (double) kb_units); } else { sprintf(s, "%" PRI_off_t, CAST_off_t (n)); u = HTProgressUnits(rateBYTES); } } else { sprintf(s, "%" PRI_off_t, CAST_off_t (n)); } if (!was_units || was_units != u) sprintf(s + strlen(s), " %s", u); return u; } #ifdef USE_READPROGRESS #define TIME_HMS_LENGTH (36) static char *sprint_tbuf(char *s, long t) { const char *format = ((LYTransferRate == rateEtaBYTES2 || LYTransferRate == rateEtaKB2) ? "% 2ld%c" : "%ld%c"); char *base = s; if (t < 0) { strcpy(s, "forever"); } else { if (t > (3600 * 24)) { sprintf(s, format, t / (3600 * 24), 'd'); s += strlen(s); t %= (3600 * 24); } if (t > 3600) { sprintf(s, format, t / 3600, 'h'); s += strlen(s); t %= 3600; } if (t > 60) { sprintf(s, format, t / 60, 'm'); s += strlen(s); t %= 60; } if (s == base) { sprintf(s, "% 2ld sec", t); } else if (t != 0) { sprintf(s, format, t, 's'); } } return base; } #endif /* USE_READPROGRESS */ /* Issue a read-progress message. HTReadProgress() * ------------------------------ */ void HTReadProgress(off_t bytes, off_t total) { static off_t bytes_last, total_last; static off_t transfer_rate = 0; static char *line = NULL; char bytesp[80], totalp[80], transferp[80]; int renew = 0; const char *was_units; #ifdef HAVE_GETTIMEOFDAY struct timeval tv; double now; static double first, last, last_active; gettimeofday(&tv, (struct timezone *) 0); now = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.; #else #if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H) static double now, first, last, last_active; struct timeb tb; ftime(&tb); now = tb.time + (double) tb.millitm / 1000; #else time_t now = time((time_t *) 0); /* once per second */ static time_t first, last, last_active; #endif #endif if (!LYShowTransferRate) LYTransferRate = rateOFF; if (bytes == 0) { first = last = last_active = now; bytes_last = bytes; } else if (bytes < 0) { /* stalled */ bytes = bytes_last; total = total_last; } /* 1 sec delay for transfer_rate calculation without g-t-o-d */ if ((bytes > 0) && (now > first)) { if (transfer_rate <= 0) { /* the very first time */ transfer_rate = (off_t) ((double) (bytes) / (now - first)); /* bytes/sec */ } total_last = total; /* * Optimal refresh time: every 0.2 sec */ #if defined(HAVE_GETTIMEOFDAY) || (defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)) if (now >= last + 0.2) renew = 1; #else /* * Use interpolation. (The transfer rate may be not constant * when we have partial content in a proxy. We adjust transfer_rate * once a second to minimize interpolation error below.) */ if ((now != last) || ((bytes - bytes_last) > (transfer_rate / 5))) { renew = 1; bytes_last += (transfer_rate / 5); /* until we got next second */ } #endif if (renew) { if (now > last) { last = now; if (bytes_last != bytes) last_active = now; bytes_last = bytes; transfer_rate = (off_t) ((double) bytes / (now - first)); /* more accurate value */ } if (total > 0) was_units = sprint_bytes(totalp, total, 0); else was_units = 0; sprint_bytes(bytesp, bytes, was_units); switch ((TransferRate) LYTransferRate) { #ifdef USE_PROGRESSBAR case rateBAR: /* * If we know the total size of the file, we can compute * a percentage, and show a corresponding progress bar. */ HTSprintf0(&line, gettext("Read %s of data"), bytesp); if (total > 0) { float percent = (float) bytes / (float) total; int meter = (int) (((float) LYcolLimit * percent) - 5); CTRACE((tfp, "rateBAR: bytes: %" PRI_off_t ", total: " "%" PRI_off_t "\n", CAST_off_t (bytes), CAST_off_t (total))); CTRACE((tfp, "meter = %d\n", meter)); HTSprintf0(&line, "%d%% ", (int) (percent * 100)); while (meter-- > 0) StrAllocCat(line, "I"); CTRACE((tfp, "%s\n", line)); CTRACE_FLUSH(tfp); } break; #endif default: if (total > 0) { HTSprintf0(&line, gettext("Read %s of %s of data"), bytesp, totalp); } else { HTSprintf0(&line, gettext("Read %s of data"), bytesp); } if (LYTransferRate != rateOFF && transfer_rate > 0) { sprint_bytes(transferp, transfer_rate, 0); HTSprintf(&line, gettext(", %s/sec"), transferp); } break; } #ifdef USE_READPROGRESS if (LYTransferRate == rateEtaBYTES || LYTransferRate == rateEtaKB || LYTransferRate == rateEtaBYTES2 || LYTransferRate == rateEtaKB2) { char tbuf[TIME_HMS_LENGTH]; if (now - last_active >= 5) HTSprintf(&line, gettext(" (stalled for %s)"), sprint_tbuf(tbuf, (long) (now - last_active))); if (total > 0 && transfer_rate) HTSprintf(&line, gettext(", ETA %s"), sprint_tbuf(tbuf, (long) ((total - bytes) / transfer_rate))); } #endif switch ((TransferRate) LYTransferRate) { #ifdef USE_PROGRESSBAR case rateBAR: /* * If we were not able to show a progress bar, just show * a "." for progress. */ if (total <= 0) StrAllocCat(line, "."); break; #endif default: StrAllocCat(line, "."); break; } if (total < -1) StrAllocCat(line, gettext(" (Press 'z' to abort)")); /* do not store the message for history page. */ statusline(line); CTRACE((tfp, "%s\n", line)); } } #ifdef LY_FIND_LEAKS FREE(line); #endif } static BOOL conf_cancelled = NO; /* used by HTConfirm only - kw */ BOOL HTLastConfirmCancelled(void) { if (conf_cancelled) { conf_cancelled = NO; /* reset */ return (YES); } else { return (NO); } } /* * Prompt for yes/no response, but let a configuration variable override * the prompt entirely. */ int HTForcedPrompt(int option, const char *msg, int dft) { int result = FALSE; const char *show = NULL; char *msg2 = NULL; if (option == FORCE_PROMPT_DFT) { result = HTConfirmDefault(msg, dft); } else { if (option == FORCE_PROMPT_YES) { show = gettext("yes"); result = YES; } else if (option == FORCE_PROMPT_NO) { show = gettext("no"); result = NO; } else { return HTConfirmDefault(msg, dft); /* bug... */ } HTSprintf(&msg2, "%s %s", msg, show); HTUserMsg(msg2); free(msg2); } return result; } #define DFT_CONFIRM ~(YES|NO) /* Seek confirmation with default answer. HTConfirmDefault() * -------------------------------------- */ int HTConfirmDefault(const char *Msg, int Dft) { /* Meta-note: don't move the following note from its place right in front of the first gettext(). As it is now, it should automatically appear in generated lynx.pot files. - kw */ /* NOTE TO TRANSLATORS: If you provide a translation for "yes", lynx * will take the first byte of the translation as a positive response * to Yes/No questions. If you provide a translation for "no", lynx * will take the first byte of the translation as a negative response * to Yes/No questions. For both, lynx will also try to show the * first byte in the prompt as a character, instead of (y) or (n), * respectively. This will not work right for multibyte charsets! * Don't translate "yes" and "no" for CJK character sets (or translate * them to "yes" and "no"). For a translation using UTF-8, don't * translate if the translation would begin with anything but a 7-bit * (US_ASCII) character. That also means do not translate if the * translation would begin with anything but a 7-bit character, if * you use a single-byte character encoding (a charset like ISO-8859-n) * but anticipate that the message catalog may be used re-encoded in * UTF-8 form. * For translations using other character sets, you may also wish to * leave "yes" and "no" untranslated, if using (y) and (n) is the * preferred behavior. * Lynx will also accept y Y n N as responses unless there is a conflict * with the first letter of the "yes" or "no" translation. */ const char *msg_yes = gettext("yes"); const char *msg_no = gettext("no"); int result = -1; /* If they're not really distinct in the first letter, revert to English */ if (TOUPPER(*msg_yes) == TOUPPER(*msg_no)) { msg_yes = "yes"; msg_no = "no"; } conf_cancelled = NO; if (dump_output_immediately) { /* Non-interactive, can't respond */ if (Dft == DFT_CONFIRM) { CTRACE((tfp, "Confirm: %s (%c/%c) ", Msg, *msg_yes, *msg_no)); } else { CTRACE((tfp, "Confirm: %s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no)); } CTRACE((tfp, "- NO, not interactive.\n")); result = NO; } else { char *msg = NULL; char fallback_y = 'y'; /* English letter response as fallback */ char fallback_n = 'n'; /* English letter response as fallback */ if (fallback_y == *msg_yes || fallback_y == *msg_no) fallback_y = '\0'; /* conflict or duplication, don't use */ if (fallback_n == *msg_yes || fallback_n == *msg_no) fallback_n = '\0'; /* conflict or duplication, don't use */ if (Dft == DFT_CONFIRM) HTSprintf0(&msg, "%s (%c/%c) ", Msg, *msg_yes, *msg_no); else HTSprintf0(&msg, "%s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no); if (LYTraceLogFP) { CTRACE((tfp, "Confirm: %s", msg)); } _statusline(msg); FREE(msg); while (result < 0) { int c = LYgetch_single(); #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; c = TOUPPER(*msg_no); } #endif /* VMS */ if (c == TOUPPER(*msg_yes)) { result = YES; } else if (c == TOUPPER(*msg_no)) { result = NO; } else if (fallback_y && c == fallback_y) { result = YES; } else if (fallback_n && c == fallback_n) { result = NO; } else if (LYCharIsINTERRUPT(c)) { /* remember we had ^G or ^C */ conf_cancelled = YES; result = NO; } else if (Dft != DFT_CONFIRM) { result = Dft; break; } } CTRACE((tfp, "- %s%s.\n", (result != NO) ? "YES" : "NO", conf_cancelled ? ", cancelled" : "")); } return (result); } /* Seek confirmation. HTConfirm() * ------------------ */ BOOL HTConfirm(const char *Msg) { return (BOOL) HTConfirmDefault(Msg, DFT_CONFIRM); } /* * Ask a post resubmission prompt with some indication of what would * be resubmitted, useful especially for going backward in history. * Try to use parts of the address or, if given, the title, depending * on how much fits on the statusline. * if_imgmap and if_file indicate how to handle an address that is * a "LYNXIMGMAP:", or a "file:" URL (presumably the List Page file), * respectively: 0: auto-deny, 1: auto-confirm, 2: prompt. * - kw */ BOOL confirm_post_resub(const char *address, const char *title, int if_imgmap, int if_file) { size_t len1; const char *msg = CONFIRM_POST_RESUBMISSION_TO; char buf[240]; char *temp = NULL; BOOL res; size_t maxlen = (size_t) (LYcolLimit - 5); if (!address) { return (NO); } else if (isLYNXIMGMAP(address)) { if (if_imgmap <= 0) return (NO); else if (if_imgmap == 1) return (YES); else msg = CONFIRM_POST_LIST_RELOAD; } else if (isFILE_URL(address)) { if (if_file <= 0) return (NO); else if (if_file == 1) return (YES); else msg = CONFIRM_POST_LIST_RELOAD; } else if (dump_output_immediately) { return (NO); } if (maxlen >= sizeof(buf)) maxlen = sizeof(buf) - 1; if ((len1 = strlen(msg)) + strlen(address) <= maxlen) { sprintf(buf, msg, address); return HTConfirm(buf); } if (len1 + strlen(temp = HTParse(address, "", PARSE_ACCESS + PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION)) <= maxlen) { sprintf(buf, msg, temp); res = HTConfirm(buf); FREE(temp); return (res); } FREE(temp); if (title && (len1 + strlen(title) <= maxlen)) { sprintf(buf, msg, title); return HTConfirm(buf); } if (len1 + strlen(temp = HTParse(address, "", PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION)) <= maxlen) { sprintf(buf, msg, temp); res = HTConfirm(buf); FREE(temp); return (res); } FREE(temp); if ((temp = HTParse(address, "", PARSE_HOST)) && *temp && len1 + strlen(temp) <= maxlen) { sprintf(buf, msg, temp); res = HTConfirm(buf); FREE(temp); return (res); } FREE(temp); return HTConfirm(CONFIRM_POST_RESUBMISSION); } /* Prompt for answer and get text back. HTPrompt() * ------------------------------------ */ char *HTPrompt(const char *Msg, const char *deflt) { char *rep = NULL; bstring *data = NULL; _statusline(Msg); BStrCopy0(data, deflt ? deflt : ""); if (!dump_output_immediately) (void) LYgetBString(&data, FALSE, 0, NORECALL); StrAllocCopy(rep, data->str); BStrFree(data); return rep; } /* * Prompt for password without echoing the reply. HTPromptPassword() * ---------------------------------------------- */ char *HTPromptPassword(const char *Msg) { char *result = NULL; bstring *data = NULL; if (!dump_output_immediately) { _statusline(Msg ? Msg : PASSWORD_PROMPT); BStrCopy0(data, ""); (void) LYgetBString(&data, TRUE, 0, NORECALL); StrAllocCopy(result, data->str); BStrFree(data); } else { printf("\n%s\n", PASSWORD_REQUIRED); StrAllocCopy(result, ""); } return result; } /* Prompt both username and password. HTPromptUsernameAndPassword() * ---------------------------------- * * On entry, * Msg is the prompting message. * *username and * *password are char pointers which contain default * or zero-length strings; they are changed * to point to result strings. * IsProxy should be TRUE if this is for * proxy authentication. * * If *username is not NULL, it is taken * to point to a default value. * Initial value of *password is * completely discarded. * * On exit, * *username and *password point to newly allocated * strings -- original strings pointed to by them * are NOT freed. * */ void HTPromptUsernameAndPassword(const char *Msg, char **username, char **password, int IsProxy) { if ((IsProxy == FALSE && authentication_info[0] && authentication_info[1]) || (IsProxy == TRUE && proxyauth_info[0] && proxyauth_info[1])) { /* * The -auth or -pauth parameter gave us both the username * and password to use for the first realm or proxy server, * respectively, so just use them without any prompting. - FM */ StrAllocCopy(*username, (IsProxy ? proxyauth_info[0] : authentication_info[0])); if (IsProxy) { FREE(proxyauth_info[0]); } else { FREE(authentication_info[0]); } StrAllocCopy(*password, (IsProxy ? proxyauth_info[1] : authentication_info[1])); if (IsProxy) { FREE(proxyauth_info[1]); } else { FREE(authentication_info[1]); } } else if (dump_output_immediately) { /* * We are not interactive and don't have both the * username and password from the command line, * but might have one or the other. - FM */ if ((IsProxy == FALSE && authentication_info[0]) || (IsProxy == TRUE && proxyauth_info[0])) { /* * Use the command line username. - FM */ StrAllocCopy(*username, (IsProxy ? proxyauth_info[0] : authentication_info[0])); if (IsProxy) { FREE(proxyauth_info[0]); } else { FREE(authentication_info[0]); } } else { /* * Default to "WWWuser". - FM */ StrAllocCopy(*username, "WWWuser"); } if ((IsProxy == FALSE && authentication_info[1]) || (IsProxy == TRUE && proxyauth_info[1])) { /* * Use the command line password. - FM */ StrAllocCopy(*password, (IsProxy ? proxyauth_info[1] : authentication_info[1])); if (IsProxy) { FREE(proxyauth_info[1]); } else { FREE(authentication_info[1]); } } else { /* * Default to a zero-length string. - FM */ StrAllocCopy(*password, ""); } printf("\n%s\n", USERNAME_PASSWORD_REQUIRED); } else { /* * We are interactive and don't have both the * username and password from the command line, * but might have one or the other. - FM */ if ((IsProxy == FALSE && authentication_info[0]) || (IsProxy == TRUE && proxyauth_info[0])) { /* * Offer the command line username in the * prompt for the first realm. - FM */ StrAllocCopy(*username, (IsProxy ? proxyauth_info[0] : authentication_info[0])); if (IsProxy) { FREE(proxyauth_info[0]); } else { FREE(authentication_info[0]); } } /* * Prompt for confirmation or entry of the username. - FM */ if (Msg != NULL) { *username = HTPrompt(Msg, *username); } else { *username = HTPrompt(USERNAME_PROMPT, *username); } if ((IsProxy == FALSE && authentication_info[1]) || (IsProxy == TRUE && proxyauth_info[1])) { /* * Use the command line password for the first realm. - FM */ StrAllocCopy(*password, (IsProxy ? proxyauth_info[1] : authentication_info[1])); if (IsProxy) { FREE(proxyauth_info[1]); } else { FREE(authentication_info[1]); } } else if (non_empty(*username)) { /* * We have a non-zero length username, * so prompt for the password. - FM */ *password = HTPromptPassword(PASSWORD_PROMPT); } else { /* * Return a zero-length password. - FM */ StrAllocCopy(*password, ""); } } } /* Confirm a cookie operation. HTConfirmCookie() * --------------------------- * * On entry, * server is the server sending the Set-Cookie. * domain is the domain of the cookie. * path is the path of the cookie. * name is the name of the cookie. * value is the value of the cookie. * * On exit, * Returns FALSE on cancel, * TRUE if the cookie should be set. */ BOOL HTConfirmCookie(domain_entry * de, const char *server, const char *name, const char *value) { int ch; const char *prompt = ADVANCED_COOKIE_CONFIRMATION; if (de == NULL) return FALSE; /* If the user has specified a list of domains to allow or deny * from the config file, then they'll already have de->bv set to * ACCEPT_ALWAYS or REJECT_ALWAYS so we can relax and let the * default cookie handling code cope with this fine. */ /* * If the user has specified a constant action, don't prompt at all. */ if (de->bv == ACCEPT_ALWAYS) return TRUE; if (de->bv == REJECT_ALWAYS) return FALSE; if (dump_output_immediately) { /* * Non-interactive, can't respond. Use the LYSetCookies value * based on its compilation or configuration setting, or on the * command line toggle. - FM */ return LYSetCookies; } /* * Estimate how much of the cookie we can show. */ if (!LYAcceptAllCookies) { int namelen, valuelen, space_free, percentage; char *message = 0; space_free = (LYcolLimit - (LYstrCells(prompt) - 10) /* %s and %.*s and %.*s chars */ -(int) strlen(server)); if (space_free < 0) space_free = 0; namelen = (int) strlen(name); valuelen = (int) strlen(value); if ((namelen + valuelen) > space_free) { /* * Argh... there isn't enough space on our single line for * the whole cookie. Reduce them both by a percentage. * This should be smarter. */ percentage = (100 * space_free) / (namelen + valuelen); namelen = (percentage * namelen) / 100; valuelen = (percentage * valuelen) / 100; } HTSprintf(&message, prompt, server, namelen, name, valuelen, value); _statusline(message); FREE(message); } for (;;) { if (LYAcceptAllCookies) { ch = 'A'; } else { ch = LYgetch_single(); #if defined(LOCALE) && defined(HAVE_GETTEXT) { #define L_PAREN '(' #define R_PAREN ')' /* * Special-purpose workaround for gettext support (we should do * this in a more general way) -TD * * NOTE TO TRANSLATORS: If the prompt has been rendered into * another language, and if yes/no are distinct, assume the * translator can make an ordered list in parentheses with one * capital letter for each as we assumed in HTConfirmDefault(). * The list has to be in the same order as in the original message, * and the four capital letters chosen to not match those in the * original unless they have the same position. * * Example: * (Y/N/Always/neVer) - English (original) * (O/N/Toujours/Jamais) - French */ char *p = gettext("Y/N/A/V"); /* placeholder for comment */ const char *s = "YNAV\007\003"; /* see ADVANCED_COOKIE_CONFIRMATION */ if (StrChr(s, ch) == 0 && isalpha(ch) && (p = strrchr(prompt, L_PAREN)) != 0) { CTRACE((tfp, "Looking for %c in %s\n", ch, p)); while (*p != R_PAREN && *p != 0 && isalpha(UCH(*s))) { if (isalpha(UCH(*p)) && (*p == TOUPPER(*p))) { CTRACE((tfp, "...testing %c/%c\n", *p, *s)); if (*p == ch) { ch = *s; break; } ++s; } ++p; } } } #endif } #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; ch = 'N'; } #endif /* VMS */ switch (ch) { case 'A': /* * Set to accept all cookies for this domain. */ de->bv = ACCEPT_ALWAYS; HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain); return TRUE; case 'N': /* * Reject the cookie. */ reject: HTUserMsg(REJECTING_COOKIE); return FALSE; case 'V': /* * Set to reject all cookies from this domain. */ de->bv = REJECT_ALWAYS; HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain); return FALSE; case 'Y': /* * Accept the cookie. */ HTInfoMsg(ALLOWING_COOKIE); return TRUE; default: if (LYCharIsINTERRUPT(ch)) goto reject; continue; } } } /* Confirm redirection of POST. HTConfirmPostRedirect() * ---------------------------- * * On entry, * Redirecting_url is the Location. * server_status is the server status code. * * On exit, * Returns 0 on cancel, * 1 for redirect of POST with content, * 303 for redirect as GET without content */ int HTConfirmPostRedirect(const char *Redirecting_url, int server_status) { int result = -1; char *show_POST_url = NULL; char *StatusInfo = 0; char *url = 0; int on_screen = 0; /* 0 - show menu * 1 - show url * 2 - menu is already on screen */ if (server_status == 303 || server_status == 302) { /* * HTTP.c should not have called us for either of * these because we're treating 302 as historical, * so just return 303. - FM */ return 303; } if (dump_output_immediately) { if (server_status == 301) { /* * Treat 301 as historical, i.e., like 303 (GET * without content), when not interactive. - FM */ return 303; } else { /* * Treat anything else (e.g., 305, 306 or 307) as too * dangerous to redirect without confirmation, and thus * cancel when not interactive. - FM */ return 0; } } if (user_mode == NOVICE_MODE) { on_screen = 2; LYmove(LYlines - 2, 0); HTSprintf0(&StatusInfo, SERVER_ASKED_FOR_REDIRECTION, server_status); LYaddstr(StatusInfo); LYclrtoeol(); LYmove(LYlines - 1, 0); HTSprintf0(&url, "URL: %.*s", (LYcols < 250 ? LYcolLimit - 5 : 250), Redirecting_url); LYaddstr(url); LYclrtoeol(); if (server_status == 301) { _statusline(PROCEED_GET_CANCEL); } else { _statusline(PROCEED_OR_CANCEL); } } else { HTSprintf0(&StatusInfo, "%d %.*s", server_status, 251, ((server_status == 301) ? ADVANCED_POST_GET_REDIRECT : ADVANCED_POST_REDIRECT)); StrAllocCopy(show_POST_url, LOCATION_HEADER); StrAllocCat(show_POST_url, Redirecting_url); } while (result < 0) { int c; switch (on_screen) { case 0: _statusline(StatusInfo); break; case 1: _statusline(show_POST_url); } c = LYgetch_single(); switch (c) { case 'P': /* * Proceed with 301 or 307 redirect of POST * with same method and POST content. - FM */ FREE(show_POST_url); result = 1; break; case 7: case 'C': /* * Cancel request. */ FREE(show_POST_url); result = 0; break; case 'U': /* * Show URL for intermediate or advanced mode. */ if (user_mode != NOVICE_MODE) { if (on_screen == 1) { on_screen = 0; } else { on_screen = 1; } } break; case 'G': if (server_status == 301) { /* * Treat as 303 (GET without content). */ FREE(show_POST_url); result = 303; break; } /* fall through to default */ default: /* * Get another character. */ if (on_screen == 1) { on_screen = 0; } else { on_screen = 2; } } } FREE(StatusInfo); FREE(url); return (result); } #define okToSleep() (!crawl && !traversal && LYCursesON && !no_pause) /* * Sleep for the given message class's time. */ void LYSleepAlert(void) { if (okToSleep()) LYSleep(AlertSecs); } void LYSleepDelay(void) { if (okToSleep()) LYSleep(DelaySecs); } void LYSleepInfo(void) { if (okToSleep()) LYSleep(InfoSecs); } void LYSleepMsg(void) { if (okToSleep()) LYSleep(MessageSecs); } #ifdef USE_CMD_LOGGING void LYSleepReplay(void) { if (okToSleep()) LYSleep(ReplaySecs); } #endif /* USE_CMD_LOGGING */ /* * LYstrerror emulates the ANSI strerror() function. */ #ifndef LYStrerror char *LYStrerror(int code) { static char temp[80]; sprintf(temp, "System errno is %d.\r\n", code); return temp; } #endif /* HAVE_STRERROR */ lynx2-8-8/src/HTAlert.h000644 023711 023712 00000011452 11452321030 015210 0ustar00dickeylynx000000 000000 /* * $LynxId: HTAlert.h,v 1.34 2010/09/26 16:36:38 tom Exp $ * * Displaying messages and getting input for WWW Library * ===================================================== * * May 92 Created By C.T. Barker * Feb 93 Portablized etc TBL */ #ifndef HTALERT_H #define HTALERT_H 1 #include #ifdef __cplusplus extern "C" { #endif #define ALERT_PREFIX_LEN 5 /* Display a message and get the input * * On entry, * Msg is the message. * * On exit, * Return value is malloc'd string which must be freed. */ extern char *HTPrompt(const char *Msg, const char *deflt); /* Display a message, don't wait for input * * On entry, * The input is a list of parameters for printf. */ extern void HTAlert(const char *Msg); extern void HTAlwaysAlert(const char *extra_prefix, const char *Msg); extern void HTInfoMsg(const char *Msg); extern void HTInfoMsg2(const char *Msg, const char *Arg); extern void HTUserMsg(const char *Msg); extern void HTUserMsg2(const char *Msg, const char *Arg); /* Display a progress message for information (and diagnostics) only * * On entry, * The input is a list of parameters for printf. */ extern const char *HTProgressUnits(int kilobytes); extern void HTProgress(const char *Msg); extern void HTReadProgress(off_t bytes, off_t total); #define _HTProgress(msg) mustshow = TRUE, HTProgress(msg) /* * Indicates whether last HTConfirm was cancelled (^G or ^C) and * resets flag. (so only call once!) - kw */ extern BOOL HTLastConfirmCancelled(void); /* * Supports logic for forced yes/no prompt results. */ extern int HTForcedPrompt(int Opt, const char *Msg, int Dft); /* Display a message, then wait for 'yes' or 'no', allowing default * response if a return or left-arrow is used. * * On entry, * Takes a list of parameters for printf. * * On exit, * If the user enters 'YES', returns TRUE, returns FALSE * otherwise. */ extern int HTConfirmDefault(const char *Msg, int Dft); /* Display a message, then wait for 'yes' or 'no'. * * On entry, * Takes a list of parameters for printf. * * On exit, * If the user enters 'YES', returns TRUE, returns FALSE * otherwise. */ extern BOOL HTConfirm(const char *Msg); extern BOOL confirm_post_resub(const char *address, const char *title, int if_imgmap, int if_file); /* Prompt for password without echoing the reply */ extern char *HTPromptPassword(const char *Msg); /* Prompt both username and password HTPromptUsernameAndPassword() * --------------------------------- * On entry, * Msg is the prompting message. * *username and * *password are char pointers; they are changed * to point to result strings. * IsProxy should be TRUE if this is for * proxy authentication. * * If *username is not NULL, it is taken * to point to a default value. * Initial value of *password is * completely discarded. * * On exit, * *username and *password point to newly allocated * strings -- original strings pointed to by them * are NOT freed. * */ extern void HTPromptUsernameAndPassword(const char *Msg, char **username, char **password, int IsProxy); /* Confirm a cookie operation. HTConfirmCookie() * --------------------------- * * On entry, * server is the server sending the Set-Cookie. * domain is the domain of the cookie. * path is the path of the cookie. * name is the name of the cookie. * value is the value of the cookie. * * On exit, * Returns FALSE on cancel, * TRUE if the cookie should be set. */ extern BOOL HTConfirmCookie(domain_entry * dp, const char *server, const char *name, const char *value); /* Confirm redirection of POST. HTConfirmPostRedirect() * ---------------------------- * On entry, * Redirecting_url is the Location. * server_status is the server status code. * * On exit, * Returns 0 on cancel, * 1 for redirect of POST with content, * 303 for redirect as GET without content */ extern int HTConfirmPostRedirect(const char *Redirecting_url, int server_status); extern void LYSleepAlert(void); extern void LYSleepDelay(void); extern void LYSleepInfo(void); extern void LYSleepMsg(void); extern void LYSleepReplay(void); #ifdef HAVE_STRERROR #define LYStrerror strerror #else extern char *LYStrerror(int code); #endif /* HAVE_STRERROR */ #ifdef __cplusplus } #endif #endif /* HTALERT_H */ lynx2-8-8/src/HTFWriter.c000644 023711 023712 00000112724 12245762550 015543 0ustar00dickeylynx000000 000000 /* * $LynxId: HTFWriter.c,v 1.106 2013/11/28 11:17:04 tom Exp $ * * FILE WRITER HTFWrite.h * =========== * * This version of the stream object just writes to a C file. * The file is assumed open and left open. * * Bugs: * strings written must be less than buffer size. */ #define HTSTREAM_INTERNAL 1 #include #include #include #include #ifdef WIN_EX #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* store statusline messages */ #ifdef USE_PERSISTENT_COOKIES #include #endif /* contains the name of the temp file which is being downloaded into */ char *WWW_Download_File = NULL; BOOLEAN LYCancelDownload = FALSE; /* exported to HTFormat.c in libWWW */ #ifdef VMS static char *FIXED_RECORD_COMMAND = NULL; #ifdef USE_COMMAND_FILE /* Keep this as an option. - FM */ #define FIXED_RECORD_COMMAND_MASK "@Lynx_Dir:FIXED512 %s" #else #define FIXED_RECORD_COMMAND_MASK "%s" static unsigned long LYVMS_FixedLengthRecords(char *filename); #endif /* USE_COMMAND_FILE */ #endif /* VMS */ HTStream *HTSaveToFile(HTPresentation *pres, HTParentAnchor *anchor, HTStream *sink); /* Stream Object * ------------- */ struct _HTStream { const HTStreamClass *isa; FILE *fp; /* The file we've opened */ char *end_command; /* What to do on _free. */ char *remove_command; /* What to do on _abort. */ char *viewer_command; /* Saved external viewer */ HTFormat input_format; /* Original pres->rep */ HTFormat output_format; /* Original pres->rep_out */ HTParentAnchor *anchor; /* Original stream's anchor. */ HTStream *sink; /* Original stream's sink. */ #ifdef FNAMES_8_3 BOOLEAN idash; /* remember position to become '.' */ #endif }; /*_________________________________________________________________________ * * A C T I O N R O U T I N E S * Bug: * Most errors are ignored. */ /* Error handling * ------------------ */ static void HTFWriter_error(HTStream *me, const char *id) { char buf[200]; sprintf(buf, "%.60s: %.60s: %.60s", id, me->isa->name, LYStrerror(errno)); HTAlert(buf); /* * Only disaster results from: * me->isa->_abort(me, NULL); */ } /* Character handling * ------------------ */ static void HTFWriter_put_character(HTStream *me, int c) { if (me->fp) { putc(c, me->fp); } } /* String handling * --------------- */ static void HTFWriter_put_string(HTStream *me, const char *s) { if (me->fp) { fputs(s, me->fp); } } /* Buffer write. Buffers can (and should!) be big. * ------------ */ static void HTFWriter_write(HTStream *me, const char *s, int l) { size_t result; if (me->fp) { result = fwrite(s, (size_t) 1, (size_t) l, me->fp); if (result != (size_t) l) { HTFWriter_error(me, "HTFWriter_write"); } } } /* Free an HTML object * ------------------- * * Note that the SGML parsing context is freed, but the created * object is not, * as it takes on an existence of its own unless explicitly freed. */ static void HTFWriter_free(HTStream *me) { int len; char *path = NULL; char *addr = NULL; BOOL use_zread = NO; BOOLEAN found = FALSE; #ifdef WIN_EX HANDLE cur_handle; cur_handle = GetForegroundWindow(); #endif if (me->fp) fflush(me->fp); if (me->end_command) { /* Temp file */ LYCloseTempFP(me->fp); #ifdef VMS if (0 == strcmp(me->end_command, "SaveVMSBinaryFile")) { /* * It's a binary file saved to disk on VMS, which * we want to convert to fixed records format. - FM */ #ifdef USE_COMMAND_FILE LYSystem(FIXED_RECORD_COMMAND); #else LYVMS_FixedLengthRecords(FIXED_RECORD_COMMAND); #endif /* USE_COMMAND_FILE */ FREE(FIXED_RECORD_COMMAND); if (me->remove_command) { /* NEVER REMOVE THE FILE unless during an abort! */ FREE(me->remove_command); } } else #endif /* VMS */ if (me->input_format == HTAtom_for("www/compressed")) { /* * It's a compressed file supposedly cached to * a temporary file for uncompression. - FM */ if (me->anchor->FileCache != NULL) { BOOL skip_loadfile = (BOOL) (me->viewer_command != NULL); /* * Save the path with the "gz" or "Z" suffix trimmed, * and remove any previous uncompressed copy. - FM */ StrAllocCopy(path, me->anchor->FileCache); if ((len = (int) strlen(path)) > 3 && (!strcasecomp(&path[len - 2], "gz") || !strcasecomp(&path[len - 2], "zz"))) { #ifdef USE_ZLIB if (!skip_loadfile) { use_zread = YES; } else #endif /* USE_ZLIB */ { path[len - 3] = '\0'; (void) remove(path); } } else if (len > 4 && !strcasecomp(&path[len - 3], "bz2")) { #ifdef USE_BZLIB if (!skip_loadfile) { use_zread = YES; } else #endif /* USE_BZLIB */ { path[len - 4] = '\0'; (void) remove(path); } } else if (len > 2 && !strcasecomp(&path[len - 1], "Z")) { path[len - 2] = '\0'; (void) remove(path); } if (!use_zread) { if (!dump_output_immediately) { /* * Tell user what's happening. - FM */ _HTProgress(me->end_command); } /* * Uncompress it. - FM */ if (me->end_command && me->end_command[0]) LYSystem(me->end_command); found = LYCanReadFile(me->anchor->FileCache); } if (found) { /* * It's still there with the "gz" or "Z" suffix, * so the uncompression failed. - FM */ if (!dump_output_immediately) { lynx_force_repaint(); LYrefresh(); } HTAlert(ERROR_UNCOMPRESSING_TEMP); (void) LYRemoveTemp(me->anchor->FileCache); FREE(me->anchor->FileCache); } else { /* * Succeeded! Create a complete address * for the uncompressed file and invoke * HTLoadFile() to handle it. - FM */ #ifdef FNAMES_8_3 /* * Assuming we have just uncompressed e.g. * FILE-mpeg.gz -> FILE-mpeg, restore/shorten * the name to be fit for passing to an external * viewer, by renaming FILE-mpeg -> FILE.mpe - kw */ if (skip_loadfile) { char *new_path = NULL; char *the_dash = me->idash ? strrchr(path, '-') : 0; if (the_dash != 0) { unsigned off = (the_dash - path); StrAllocCopy(new_path, path); new_path[off] = '.'; if (strlen(new_path + off) > 4) new_path[off + 4] = '\0'; if (rename(path, new_path) == 0) { FREE(path); path = new_path; } else { FREE(new_path); } } } #endif /* FNAMES_8_3 */ LYLocalFileToURL(&addr, path); if (!use_zread) { LYRenamedTemp(me->anchor->FileCache, path); StrAllocCopy(me->anchor->FileCache, path); StrAllocCopy(me->anchor->content_encoding, "binary"); } FREE(path); if (!skip_loadfile) { /* * Lock the chartrans info we may possibly have, * so HTCharsetFormat() will not apply the default * for local files. - KW */ if (HTAnchor_getUCLYhndl(me->anchor, UCT_STAGE_PARSER) < 0) { /* * If not yet set - KW */ HTAnchor_copyUCInfoStage(me->anchor, UCT_STAGE_PARSER, UCT_STAGE_MIME, UCT_SETBY_DEFAULT + 1); } HTAnchor_copyUCInfoStage(me->anchor, UCT_STAGE_PARSER, UCT_STAGE_MIME, -1); } /* * Create a complete address for * the uncompressed file. - FM */ if (!dump_output_immediately) { /* * Tell user what's happening. - FM * HTInfoMsg2(WWW_USING_MESSAGE, addr); * but only in the history, not on screen -RS */ LYstore_message2(WWW_USING_MESSAGE, addr); } if (skip_loadfile) { /* * It's a temporary file we're passing to a viewer or * helper application. Loading the temp file through * HTLoadFile() would result in yet another HTStream * (created with HTSaveAndExecute()) which would just * copy the temp file to another temp file (or even the * same!). We can skip this needless duplication by * using the viewer_command which has already been * determined when the HTCompressed stream was created. * - kw */ FREE(me->end_command); HTAddParam(&(me->end_command), me->viewer_command, 1, me->anchor->FileCache); HTEndParam(&(me->end_command), me->viewer_command, 1); if (!dump_output_immediately) { /* * Tell user what's happening. - FM */ HTProgress(me->end_command); #ifndef WIN_EX stop_curses(); #endif } #ifdef _WIN_CC exec_command(me->end_command, FALSE); #else LYSystem(me->end_command); #endif if (me->remove_command) { /* NEVER REMOVE THE FILE unless during an abort!!! */ FREE(me->remove_command); } if (!dump_output_immediately) { #ifdef WIN_EX if (focus_window) { HTInfoMsg(gettext("Set focus1")); (void) SetForegroundWindow(cur_handle); } #else start_curses(); #endif } } else { (void) HTLoadFile(addr, me->anchor, me->output_format, me->sink); } if (dump_output_immediately && me->output_format == HTAtom_for("www/present")) { FREE(addr); (void) remove(me->anchor->FileCache); FREE(me->anchor->FileCache); FREE(me->remove_command); FREE(me->end_command); FREE(me->viewer_command); FREE(me); return; } } FREE(addr); } if (me->remove_command) { /* NEVER REMOVE THE FILE unless during an abort!!! */ FREE(me->remove_command); } } else if (strcmp(me->end_command, "SaveToFile")) { /* * It's a temporary file we're passing to a viewer or helper * application. - FM */ if (!dump_output_immediately) { /* * Tell user what's happening. - FM */ _HTProgress(me->end_command); #ifndef WIN_EX stop_curses(); #endif } #ifdef _WIN_CC exec_command(me->end_command, wait_viewer_termination); #else LYSystem(me->end_command); #endif if (me->remove_command) { /* NEVER REMOVE THE FILE unless during an abort!!! */ FREE(me->remove_command); } if (!dump_output_immediately) { #ifdef WIN_EX if (focus_window) { HTInfoMsg(gettext("Set focus2")); (void) SetForegroundWindow(cur_handle); } #else start_curses(); #endif } } else { /* * It's a file we saved to disk for handling via a menu. - FM */ if (me->remove_command) { /* NEVER REMOVE THE FILE unless during an abort!!! */ FREE(me->remove_command); } if (!dump_output_immediately) { #ifdef WIN_EX if (focus_window) { HTInfoMsg(gettext("Set focus3")); (void) SetForegroundWindow(cur_handle); } #else start_curses(); #endif } } FREE(me->end_command); } FREE(me->viewer_command); if (dump_output_immediately) { if (me->anchor->FileCache) (void) remove(me->anchor->FileCache); FREE(me); #ifdef USE_PERSISTENT_COOKIES /* * We want to save cookies picked up when in source mode. ... */ if (persistent_cookies) LYStoreCookies(LYCookieSaveFile); #endif /* USE_PERSISTENT_COOKIES */ exit_immediately(EXIT_SUCCESS); } FREE(me); return; } #ifdef VMS # define REMOVE_COMMAND "delete/noconfirm/nolog %s;" #else # define REMOVE_COMMAND "%s" #endif /* VMS */ /* Abort writing * ------------- */ static void HTFWriter_abort(HTStream *me, HTError e GCC_UNUSED) { CTRACE((tfp, "HTFWriter_abort called\n")); LYCloseTempFP(me->fp); FREE(me->viewer_command); if (me->end_command) { /* Temp file */ CTRACE((tfp, "HTFWriter: Aborting: file not executed or saved.\n")); FREE(me->end_command); if (me->remove_command) { #ifdef VMS LYSystem(me->remove_command); #else (void) chmod(me->remove_command, 0600); /* Ignore errors */ if (0 != unlink(me->remove_command)) { char buf[560]; sprintf(buf, "%.60s '%.400s': %.60s", gettext("Error deleting file"), me->remove_command, LYStrerror(errno)); HTAlert(buf); } #endif FREE(me->remove_command); } } FREE(WWW_Download_File); FREE(me); } /* Structured Object Class * ----------------------- */ static const HTStreamClass HTFWriter = /* As opposed to print etc */ { "FileWriter", HTFWriter_free, HTFWriter_abort, HTFWriter_put_character, HTFWriter_put_string, HTFWriter_write }; /* Subclass-specific Methods * ------------------------- */ HTStream *HTFWriter_new(FILE *fp) { HTStream *me; if (!fp) return NULL; me = typecalloc(HTStream); if (me == NULL) outofmem(__FILE__, "HTFWriter_new"); assert(me != NULL); me->isa = &HTFWriter; me->fp = fp; me->end_command = NULL; me->remove_command = NULL; me->anchor = NULL; me->sink = NULL; return me; } /* Make system command from template * --------------------------------- * * See mailcap spec for description of template. */ static char *mailcap_substitute(HTParentAnchor *anchor, HTPresentation *pres, char *fnam) { char *result = LYMakeMailcapCommand(pres->command, anchor->content_type_params, fnam); #if defined(UNIX) /* if we don't have a "%s" token, expect to provide the file via stdin */ if (!LYMailcapUsesPctS(pres->command)) { char *prepend = 0; const char *format = "( %s ) < %s"; HTSprintf(&prepend, "( %s", result); /* ...avoid quoting */ HTAddParam(&prepend, format, 2, fnam); /* ...to quote if needed */ FREE(result); result = prepend; } #endif return result; } /* Take action using a system command * ---------------------------------- * * originally from Ghostview handling by Marc Andreseen. * Creates temporary file, writes to it, executes system command * on end-document. The suffix of the temp file can be given * in case the application is fussy, or so that a generic opener can * be used. */ HTStream *HTSaveAndExecute(HTPresentation *pres, HTParentAnchor *anchor, HTStream *sink) { char fnam[LY_MAXPATH]; const char *suffix; HTStream *me; if (traversal) { LYCancelledFetch = TRUE; return (NULL); } #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) if (pres->quality >= 999.0) { /* exec link */ if (dump_output_immediately) { LYCancelledFetch = TRUE; return (NULL); } if (no_exec) { HTAlert(EXECUTION_DISABLED); return HTPlainPresent(pres, anchor, sink); } if (!local_exec) { if (local_exec_on_local_files && (LYJumpFileURL || !StrNCmp(anchor->address, "file://localhost", 16))) { /* allow it to continue */ ; } else { char *buf = 0; HTSprintf0(&buf, EXECUTION_DISABLED_FOR_FILE, key_for_func(LYK_OPTIONS)); HTAlert(buf); FREE(buf); return HTPlainPresent(pres, anchor, sink); } } } #endif /* EXEC_LINKS || EXEC_SCRIPTS */ if (dump_output_immediately) { return (HTSaveToFile(pres, anchor, sink)); } me = typecalloc(HTStream); if (me == NULL) outofmem(__FILE__, "HTSaveAndExecute"); assert(me != NULL); me->isa = &HTFWriter; me->input_format = pres->rep; me->output_format = pres->rep_out; me->anchor = anchor; me->sink = sink; if (LYCachedTemp(fnam, &(anchor->FileCache))) { /* This used to be LYNewBinFile(fnam); changed to a different call so * that the open fp gets registered in the list keeping track of temp * files, equivalent to when LYOpenTemp() gets called below. This * avoids a file descriptor leak caused by LYCloseTempFP() not being * able to find the fp. The binary suffix is expected to not be used, * it's only for fallback in unusual error cases. - kw */ me->fp = LYOpenTempRewrite(fnam, BIN_SUFFIX, BIN_W); } else { #if defined(WIN_EX) && !defined(__CYGWIN__) /* 1998/01/04 (Sun) */ if (!StrNCmp(anchor->address, "file://localhost", 16)) { /* 1998/01/23 (Fri) 17:38:26 */ char *cp, *view_fname; me->fp = NULL; view_fname = fnam + 3; LYStrNCpy(view_fname, anchor->address + 17, sizeof(fnam) - 5); HTUnEscape(view_fname); if (StrChr(view_fname, ':') == NULL) { fnam[0] = windows_drive[0]; fnam[1] = windows_drive[1]; fnam[2] = '/'; view_fname = fnam; } /* 1998/04/21 (Tue) 11:04:16 */ cp = view_fname; while (*cp) { if (IS_SJIS_HI1(UCH(*cp)) || IS_SJIS_HI2(UCH(*cp))) { cp += 2; continue; } else if (*cp == '/') { *cp = '\\'; } cp++; } if (StrChr(view_fname, ' ')) view_fname = quote_pathname(view_fname); StrAllocCopy(me->viewer_command, pres->command); me->end_command = mailcap_substitute(anchor, pres, view_fname); me->remove_command = NULL; return me; } #endif /* * Check for a suffix. * Save the file under a suitably suffixed name. */ if (!strcasecomp(pres->rep->name, "text/html")) { suffix = HTML_SUFFIX; } else if (!strncasecomp(pres->rep->name, "text/", 5)) { suffix = TEXT_SUFFIX; } else if ((suffix = HTFileSuffix(pres->rep, anchor->content_encoding)) == 0 || *suffix != '.') { if (!strncasecomp(pres->rep->name, "application/", 12)) { suffix = BIN_SUFFIX; } else { suffix = HTML_SUFFIX; } } me->fp = LYOpenTemp(fnam, suffix, BIN_W); } if (!me->fp) { HTAlert(CANNOT_OPEN_TEMP); FREE(me); return NULL; } StrAllocCopy(me->viewer_command, pres->command); /* * Make command to process file. */ me->end_command = mailcap_substitute(anchor, pres, fnam); /* * Make command to delete file. */ me->remove_command = 0; HTAddParam(&(me->remove_command), REMOVE_COMMAND, 1, fnam); HTEndParam(&(me->remove_command), REMOVE_COMMAND, 1); StrAllocCopy(anchor->FileCache, fnam); return me; } /* Format Converter using system command * ------------------------------------- */ /* @@@@@@@@@@@@@@@@@@@@@@ */ /* Save to a local file LJM!!! * -------------------- * * usually a binary file that can't be displayed * * originally from Ghostview handling by Marc Andreseen. * Asks the user if he wants to continue, creates a temporary * file, and writes to it. In HTSaveToFile_Free * the user will see a list of choices for download */ HTStream *HTSaveToFile(HTPresentation *pres, HTParentAnchor *anchor, HTStream *sink) { HTStream *ret_obj; char fnam[LY_MAXPATH]; const char *suffix; char *cp; int c = 0; #ifdef VMS BOOL IsBinary = TRUE; #endif ret_obj = typecalloc(HTStream); if (ret_obj == NULL) outofmem(__FILE__, "HTSaveToFile"); assert(ret_obj != NULL); ret_obj->isa = &HTFWriter; ret_obj->remove_command = NULL; ret_obj->end_command = NULL; ret_obj->input_format = pres->rep; ret_obj->output_format = pres->rep_out; ret_obj->anchor = anchor; ret_obj->sink = sink; if (dump_output_immediately) { ret_obj->fp = stdout; /* stdout */ if (HTOutputFormat == HTAtom_for("www/download")) goto Prepend_BASE; return ret_obj; } LYCancelDownload = FALSE; if (HTOutputFormat != HTAtom_for("www/download")) { if (traversal || (no_download && !override_no_download && no_disk_save)) { if (!traversal) { HTAlert(CANNOT_DISPLAY_FILE); } LYCancelDownload = TRUE; if (traversal) LYCancelledFetch = TRUE; FREE(ret_obj); return (NULL); } if (((cp = StrChr(pres->rep->name, ';')) != NULL) && strstr((cp + 1), "charset") != NULL) { _user_message(MSG_DOWNLOAD_OR_CANCEL, pres->rep->name); } else if (*(pres->rep->name) != '\0') { _user_message(MSG_DOWNLOAD_OR_CANCEL, pres->rep->name); } else { _statusline(CANNOT_DISPLAY_FILE_D_OR_C); } while (c != 'D' && c != 'C' && !LYCharIsINTERRUPT(c)) { c = LYgetch_single(); #ifdef VMS /* * 'C'ancel on Control-C or Control-Y and * a 'N'o to the "really exit" query. - FM */ if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; c = 'C'; } #endif /* VMS */ } /* * Cancel on 'C', 'c' or Control-G or Control-C. */ if (c == 'C' || LYCharIsINTERRUPT(c)) { _statusline(CANCELLING_FILE); LYCancelDownload = TRUE; FREE(ret_obj); return (NULL); } } /* * Set up a 'D'ownload. */ if (LYCachedTemp(fnam, &(anchor->FileCache))) { /* This used to be LYNewBinFile(fnam); changed to a different call so * that the open fp gets registered in the list keeping track of temp * files, equivalent to when LYOpenTemp() gets called below. This * avoids a file descriptor leak caused by LYCloseTempFP() not being * able to find the fp. The binary suffix is expected to not be used, * it's only for fallback in unusual error cases. - kw */ ret_obj->fp = LYOpenTempRewrite(fnam, BIN_SUFFIX, BIN_W); } else { /* * Check for a suffix. * Save the file under a suitably suffixed name. */ if (!strcasecomp(pres->rep->name, "text/html")) { suffix = HTML_SUFFIX; } else if (!strncasecomp(pres->rep->name, "text/", 5)) { suffix = TEXT_SUFFIX; } else if (!strncasecomp(pres->rep->name, "application/", 12)) { suffix = BIN_SUFFIX; } else if ((suffix = HTFileSuffix(pres->rep, anchor->content_encoding)) == 0 || *suffix != '.') { suffix = HTML_SUFFIX; } ret_obj->fp = LYOpenTemp(fnam, suffix, BIN_W); } if (!ret_obj->fp) { HTAlert(CANNOT_OPEN_OUTPUT); FREE(ret_obj); return NULL; } if (0 == strncasecomp(pres->rep->name, "text/", 5) || 0 == strcasecomp(pres->rep->name, "application/postscript") || 0 == strcasecomp(pres->rep->name, "application/x-RUNOFF-MANUAL")) /* * It's a text file requested via 'd'ownload. Keep adding others to * the above list, 'til we add a configurable procedure. - FM */ #ifdef VMS IsBinary = FALSE; #endif /* * Any "application/foo" or other non-"text/foo" types that are actually * text but not checked, above, will be treated as binary, so show the type * to help sort that out later. Unix folks don't need to know this, but * we'll show it to them, too. - FM */ HTInfoMsg2(CONTENT_TYPE_MSG, pres->rep->name); StrAllocCopy(WWW_Download_File, fnam); /* * Make command to delete file. */ ret_obj->remove_command = 0; HTAddParam(&(ret_obj->remove_command), REMOVE_COMMAND, 1, fnam); HTEndParam(&(ret_obj->remove_command), REMOVE_COMMAND, 1); #ifdef VMS if (IsBinary && UseFixedRecords) { StrAllocCopy(ret_obj->end_command, "SaveVMSBinaryFile"); FIXED_RECORD_COMMAND = 0; HTAddParam(&FIXED_RECORD_COMMAND, FIXED_RECORD_COMMAND_MASK, 1, fnam); HTEndParam(&FIXED_RECORD_COMMAND, FIXED_RECORD_COMMAND_MASK, 1); } else { #endif /* VMS */ StrAllocCopy(ret_obj->end_command, "SaveToFile"); #ifdef VMS } #endif /* VMS */ _statusline(RETRIEVING_FILE); StrAllocCopy(anchor->FileCache, fnam); Prepend_BASE: if (LYPrependBaseToSource && !strncasecomp(pres->rep->name, "text/html", 9) && !anchor->content_encoding) { /* * Add the document's base as a BASE tag at the top of the file, so * that any partial or relative URLs within it will be resolved * relative to that if no BASE tag is present and replaces it. Note * that the markup will be technically invalid if a DOCTYPE * declaration, or HTML or HEAD tags, are present, and thus the file * may need editing for perfection. - FM * * Add timestamp (last reload). */ char *temp = NULL; if (non_empty(anchor->content_base)) { StrAllocCopy(temp, anchor->content_base); } else if (non_empty(anchor->content_location)) { StrAllocCopy(temp, anchor->content_location); } if (temp) { LYRemoveBlanks(temp); if (!is_url(temp)) { FREE(temp); } } fprintf(ret_obj->fp, "\n", anchor->address); if (non_empty(anchor->date)) { fprintf(ret_obj->fp, "\n", anchor->date); if (non_empty(anchor->last_modified) && strcmp(anchor->last_modified, anchor->date) && strcmp(anchor->last_modified, "Thu, 01 Jan 1970 00:00:01 GMT")) { fprintf(ret_obj->fp, "\n", anchor->last_modified); } } fprintf(ret_obj->fp, "\n\n", (temp ? temp : anchor->address)); FREE(temp); } if (LYPrependCharsetToSource && !strncasecomp(pres->rep->name, "text/html", 9) && !anchor->content_encoding) { /* * Add the document's charset as a META CHARSET tag at the top of the * file, so HTTP charset header will not be forgotten when a document * saved as local file. We add this line only(!) if HTTP charset * present. - LP Note that the markup will be technically invalid if a * DOCTYPE declaration, or HTML or HEAD tags, are present, and thus the * file may need editing for perfection. - FM */ char *temp = NULL; if (non_empty(anchor->charset)) { StrAllocCopy(temp, anchor->charset); LYRemoveBlanks(temp); fprintf(ret_obj->fp, "\n\n", temp); } FREE(temp); } return ret_obj; } /* Set up stream for uncompressing - FM * ------------------------------- */ HTStream *HTCompressed(HTPresentation *pres, HTParentAnchor *anchor, HTStream *sink) { HTStream *me; HTFormat format; char *type = NULL; HTPresentation *Pres = NULL; HTPresentation *Pnow = NULL; int n, i; BOOL can_present = FALSE; char fnam[LY_MAXPATH]; char temp[LY_MAXPATH]; /* actually stores just a suffix */ const char *suffix; char *uncompress_mask = NULL; const char *compress_suffix = ""; const char *middle; /* * Deal with any inappropriate invocations of this function, or a download * request, in which case we won't bother to uncompress the file. - FM */ if (!(anchor->content_encoding && anchor->content_type)) { /* * We have no idea what we're dealing with, so treat it as a binary * stream. - FM */ format = HTAtom_for("application/octet-stream"); me = HTStreamStack(format, pres->rep_out, sink, anchor); return me; } n = HTList_count(HTPresentations); for (i = 0; i < n; i++) { Pnow = (HTPresentation *) HTList_objectAt(HTPresentations, i); if (!strcasecomp(Pnow->rep->name, anchor->content_type) && Pnow->rep_out == WWW_PRESENT) { const char *program = ""; /* * Pick the best presentation. User-defined mappings are at the * end of the list, and unless the quality is lower, we prefer * those. */ if (Pres == 0) Pres = Pnow; else if (Pres->quality > Pnow->quality) continue; else Pres = Pnow; /* * We have a presentation mapping for it. - FM */ can_present = TRUE; switch (HTEncodingToCompressType(anchor->content_encoding)) { case cftGzip: if ((program = HTGetProgramPath(ppGZIP)) != NULL) { /* * It's compressed with the modern gzip. - FM */ StrAllocCopy(uncompress_mask, program); StrAllocCat(uncompress_mask, " -d --no-name %s"); compress_suffix = "gz"; } break; case cftDeflate: if ((program = HTGetProgramPath(ppINFLATE)) != NULL) { /* * It's compressed with a zlib wrapper. */ StrAllocCopy(uncompress_mask, program); StrAllocCat(uncompress_mask, " %s"); compress_suffix = "zz"; } break; case cftBzip2: if ((program = HTGetProgramPath(ppBZIP2)) != NULL) { StrAllocCopy(uncompress_mask, program); StrAllocCat(uncompress_mask, " -d %s"); compress_suffix = "bz2"; } break; case cftCompress: if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) { /* * It's compressed the old fashioned Unix way. - FM */ StrAllocCopy(uncompress_mask, program); StrAllocCat(uncompress_mask, " %s"); compress_suffix = "Z"; } break; case cftNone: break; } } } if (can_present == FALSE || /* no presentation mapping */ uncompress_mask == NULL || /* not gzip or compress */ StrChr(anchor->content_type, ';') || /* wrong charset */ HTOutputFormat == HTAtom_for("www/download") || /* download */ !strcasecomp(pres->rep_out->name, "www/download") || /* download */ (traversal && /* only handle html or plain text for traversals */ strcasecomp(anchor->content_type, "text/html") && strcasecomp(anchor->content_type, "text/plain"))) { /* * Cast the Content-Encoding to a Content-Type and pass it back to be * handled as that type. - FM */ if (StrChr(anchor->content_encoding, '/') == NULL) { /* * Use "x-" prefix, none of the types we are likely to construct * here are official. That is we generate "application/x-gzip" and * so on. - kw */ if (!strncasecomp(anchor->content_encoding, "x-", 2)) StrAllocCopy(type, "application/"); else StrAllocCopy(type, "application/x-"); StrAllocCat(type, anchor->content_encoding); } else { StrAllocCopy(type, anchor->content_encoding); } format = HTAtom_for(type); FREE(type); FREE(uncompress_mask); me = HTStreamStack(format, pres->rep_out, sink, anchor); return me; } /* * Set up the stream structure for uncompressing and then handling based on * the uncompressed Content-Type.- FM */ me = typecalloc(HTStream); if (me == NULL) outofmem(__FILE__, "HTCompressed"); assert(me != NULL); me->isa = &HTFWriter; me->input_format = pres->rep; me->output_format = pres->rep_out; me->anchor = anchor; me->sink = sink; #ifdef FNAMES_8_3 me->idash = FALSE; #endif /* * Remove any old versions of the file. - FM */ if (anchor->FileCache) { (void) LYRemoveTemp(anchor->FileCache); FREE(anchor->FileCache); } /* * Get a new temporary filename and substitute a suitable suffix. - FM */ middle = NULL; if (!strcasecomp(anchor->content_type, "text/html")) { middle = HTML_SUFFIX; middle++; /* point to 'h' of .htm(l) - kw */ } else if (!strncasecomp(anchor->content_type, "text/", 5)) { middle = TEXT_SUFFIX + 1; } else if (!strncasecomp(anchor->content_type, "application/", 12)) { /* FIXME: why is this BEFORE HTFileSuffix? */ middle = BIN_SUFFIX + 1; } else if ((suffix = HTFileSuffix(HTAtom_for(anchor->content_type), NULL)) && *suffix == '.') { #if defined(VMS) || defined(FNAMES_8_3) if (StrChr(suffix + 1, '.') == NULL) #endif middle = suffix + 1; } temp[0] = 0; /* construct the suffix */ if (middle) { #ifdef FNAMES_8_3 me->idash = TRUE; /* remember position of '-' - kw */ strcat(temp, "-"); /* NAME-htm, NAME-txt, etc. - hack for DOS */ #else strcat(temp, "."); /* NAME.html, NAME-txt etc. */ #endif /* FNAMES_8_3 */ strcat(temp, middle); #ifdef VMS strcat(temp, "-"); /* NAME.html-gz, NAME.txt-gz, NAME.txt-Z etc. */ #else strcat(temp, "."); /* NAME-htm.gz (DOS), NAME.html.gz (UNIX)etc. */ #endif /* VMS */ } strcat(temp, compress_suffix); /* * Open the file for receiving the compressed input stream. - FM */ me->fp = LYOpenTemp(fnam, temp, BIN_W); if (!me->fp) { HTAlert(CANNOT_OPEN_TEMP); FREE(uncompress_mask); FREE(me); return NULL; } /* * me->viewer_command will be NULL if the converter Pres found above is not * for an external viewer but an internal HTStream converter. We also * don't set it under conditions where HTSaveAndExecute would disallow * execution of the command. - KW */ if (!dump_output_immediately && !traversal #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) && (Pres->quality < 999.0 || (!no_exec && /* allowed exec link or script ? */ (local_exec || (local_exec_on_local_files && (LYJumpFileURL || !StrNCmp(anchor->address, "file://localhost", 16)))))) #endif /* EXEC_LINKS || EXEC_SCRIPTS */ ) { StrAllocCopy(me->viewer_command, Pres->command); } /* * Make command to process file. - FM */ #ifdef USE_BZLIB if (compress_suffix[0] == 'b' /* must be bzip2 */ && !me->viewer_command) { /* * We won't call bzip2 externally, so we don't need to supply a command * for it. */ StrAllocCopy(me->end_command, ""); } else #endif #ifdef USE_ZLIB /* FIXME: allow deflate here, e.g., 'z' */ if (compress_suffix[0] == 'g' /* must be gzip */ && !me->viewer_command) { /* * We won't call gzip or compress externally, so we don't need to * supply a command for it. */ StrAllocCopy(me->end_command, ""); } else #endif /* USE_ZLIB */ { me->end_command = 0; HTAddParam(&(me->end_command), uncompress_mask, 1, fnam); HTEndParam(&(me->end_command), uncompress_mask, 1); } FREE(uncompress_mask); /* * Make command to delete file. - FM */ me->remove_command = 0; HTAddParam(&(me->remove_command), REMOVE_COMMAND, 1, fnam); HTEndParam(&(me->remove_command), REMOVE_COMMAND, 1); /* * Save the filename and return the structure. - FM */ StrAllocCopy(anchor->FileCache, fnam); return me; } /* Dump output to stdout - LJM & FM * --------------------- * */ HTStream *HTDumpToStdout(HTPresentation *pres GCC_UNUSED, HTParentAnchor *anchor, HTStream *sink GCC_UNUSED) { HTStream *ret_obj; ret_obj = typecalloc(HTStream); if (ret_obj == NULL) outofmem(__FILE__, "HTDumpToStdout"); assert(ret_obj != NULL); ret_obj->isa = &HTFWriter; ret_obj->remove_command = NULL; ret_obj->end_command = NULL; ret_obj->anchor = anchor; ret_obj->fp = stdout; /* stdout */ return ret_obj; } #if defined(VMS) && !defined(USE_COMMAND_FILE) #include #include /* RMS status codes */ #include /* I/O function codes */ #include /* file information block defs */ #include /* attribute request codes */ #ifdef NOTDEFINED /*** Not all versions of VMS compilers have these. ***/ #include /* file characteristics */ #include /* file attribute defs */ #else /*** So we'll define what we need from them ourselves. ***/ #define FCH$V_CONTIGB 0x005 /* pos of cont best try bit */ #define FCH$M_CONTIGB (1 << FCH$V_CONTIGB) /* contig best try bit mask */ /* VMS I/O User's Reference Manual: Part I (V5.x doc set) */ struct fatdef { unsigned char fat$b_rtype, fat$b_rattrib; unsigned short fat$w_rsize; unsigned long fat$l_hiblk, fat$l_efblk; unsigned short fat$w_ffbyte; unsigned char fat$b_bktsize, fat$b_vfcsize; unsigned short fat$w_maxrec, fat$w_defext, fat$w_gbc; unsigned:16,:32,:16; /* 6 bytes reserved, 2 bytes not used */ unsigned short fat$w_versions; }; #endif /* NOTDEFINED */ /* arbitrary descriptor without type and class info */ typedef struct dsc { unsigned short len, mbz; void *adr; } Desc; extern unsigned long sys$open(), sys$qiow(), sys$dassgn(); #define syswork(sts) ((sts) & 1) #define sysfail(sts) (!syswork(sts)) /* * 25-Jul-1995 - Pat Rankin (rankin@eql.caltech.edu) * * Force a file to be marked as having fixed-length, 512 byte records * without implied carriage control, and with best_try_contiguous set. */ static unsigned long LYVMS_FixedLengthRecords(char *filename) { struct FAB fab; /* RMS file access block */ struct fibdef fib; /* XQP file information block */ struct fatdef recattr; /* XQP file "record" attributes */ struct atrdef attr_rqst_list[3]; /* XQP attribute request itemlist */ Desc fib_dsc; unsigned short channel, iosb[4]; unsigned long fchars, sts, tmp; /* initialize file access block */ fab = cc$rms_fab; fab.fab$l_fna = filename; fab.fab$b_fns = (unsigned char) strlen(filename); fab.fab$l_fop = FAB$M_UFO; /* user file open; no further RMS processing */ fab.fab$b_fac = FAB$M_PUT; /* need write access */ fab.fab$b_shr = FAB$M_NIL; /* exclusive access */ sts = sys$open(&fab); /* channel in stv; $dassgn to close */ if (sts == RMS$_FLK) { /* For MultiNet, at least, if the file was just written by a remote NFS client, the local NFS server might still have it open, and the failed access attempt will provoke it to be closed, so try again. */ sts = sys$open(&fab); } if (sysfail(sts)) return sts; /* RMS supplies a user-mode channel (see FAB$L_FOP FAB$V_UFO doc) */ channel = (unsigned short) fab.fab$l_stv; /* set up ACP interface structures */ /* file information block, passed by descriptor; it's okay to start with an empty FIB after RMS has accessed the file for us */ fib_dsc.len = sizeof fib; fib_dsc.mbz = 0; fib_dsc.adr = &fib; memset((void *) &fib, 0, sizeof fib); /* attribute request list */ attr_rqst_list[0].atr$w_size = sizeof recattr; attr_rqst_list[0].atr$w_type = ATR$C_RECATTR; *(void **) &attr_rqst_list[0].atr$l_addr = (void *) &recattr; attr_rqst_list[1].atr$w_size = sizeof fchars; attr_rqst_list[1].atr$w_type = ATR$C_UCHAR; *(void **) &attr_rqst_list[1].atr$l_addr = (void *) &fchars; attr_rqst_list[2].atr$w_size = attr_rqst_list[2].atr$w_type = 0; attr_rqst_list[2].atr$l_addr = 0; /* file "record" attributes */ memset((void *) &recattr, 0, sizeof recattr); fchars = 0; /* file characteristics */ /* get current attributes */ sts = sys$qiow(0, channel, IO$_ACCESS, iosb, (void (*)()) 0, 0, &fib_dsc, 0, 0, 0, attr_rqst_list, 0); if (syswork(sts)) sts = iosb[0]; /* set desired attributes */ if (syswork(sts)) { recattr.fat$b_rtype = FAB$C_SEQ | FAB$C_FIX; /* org=seq, rfm=fix */ recattr.fat$w_rsize = recattr.fat$w_maxrec = 512; /* lrl=mrs=512 */ recattr.fat$b_rattrib = 0; /* rat=none */ fchars |= FCH$M_CONTIGB; /* contiguous-best-try */ sts = sys$qiow(0, channel, IO$_DEACCESS, iosb, (void (*)()) 0, 0, &fib_dsc, 0, 0, 0, attr_rqst_list, 0); if (syswork(sts)) sts = iosb[0]; } /* all done */ tmp = sys$dassgn(channel); if (syswork(sts)) sts = tmp; return sts; } #endif /* VMS && !USE_COMMAND_FILE */ lynx2-8-8/src/HTFont.h000644 023711 023712 00000002124 10132351772 015055 0ustar00dickeylynx000000 000000 /* The portable font concept (!?*) */ /* Line mode browser version: */ #ifndef HTFONT_H #define HTFONT_H typedef long int HTMLFont; /* For now */ #define HT_FONT 0 #define HT_CAPITALS 1 #define HT_BOLD 2 #define HT_UNDERLINE 4 #define HT_INVERSE 8 #define HT_DOUBLE 0x10 #define HT_BLACK 0 #define HT_WHITE 1 /* * Lynx internal character representations. */ #define HT_NON_BREAK_SPACE ((char)1) #define HT_EN_SPACE ((char)2) #define LY_UNDERLINE_START_CHAR '\003' #define LY_UNDERLINE_END_CHAR '\004' /* Turn about is fair play ASCII platforms use EBCDIC tab; EBCDIC platforms use ASCII tab for LY_BOLD_START_CHAR. */ #ifdef EBCDIC #define LY_BOLD_START_CHAR '\011' #else #define LY_BOLD_START_CHAR '\005' #endif #define LY_BOLD_END_CHAR '\006' #define LY_SOFT_HYPHEN ((char)7) #define LY_SOFT_NEWLINE ((char)8) #ifdef EBCDIC #define IsSpecialAttrChar(a) (((a) > '\002') && ((a) <= '\011') && ((a)!='\t')) #else #define IsSpecialAttrChar(a) (((a) > '\002') && ((a) <= '\010')) #endif #define IsNormalChar(a) ((a) != '\0' && !IsSpecialAttrChar(a)) #endif /* HTFONT_H */ lynx2-8-8/src/HTForms.h000644 023711 023712 00000011715 11716074571 015253 0ustar00dickeylynx000000 000000 /* * $LynxId: HTForms.h,v 1.33 2012/02/12 22:30:53 tom Exp $ */ #ifndef HTFORMS_H #define HTFORMS_H #ifndef LYSTRUCTS_H #include #endif /* LYSTRUCTS_H */ #ifdef __cplusplus extern "C" { #endif /* change_form_link() calls change_form_link_ex() with all its args and FALSE * as last arg */ extern int change_form_link(int cur, DocInfo *newdoc, BOOLEAN *refresh_screen, int use_last_tfpos, int immediate_submit); extern int change_form_link_ex(int cur, DocInfo *newdoc, BOOLEAN *refresh_screen, int use_last_tfpos, int immediate_submit, int draw_only); /* InputFieldData is used to pass the info between HTML.c and Gridtext.c in * HText_beginInput() */ typedef struct _InputFieldData { const char *accept; const char *align; int checked; const char *iclass; int disabled; int readonly; const char *error; const char *height; const char *id; const char *lang; const char *max; const char *maxlength; const char *md; const char *min; const char *name; int size; const char *src; const char *type; char *value; const char *width; int name_cs; /* charset handle for name */ int value_cs; /* charset handle for value */ const char *accept_cs; } InputFieldData; /* The OptionType structure is for a linked list of option entries */ typedef struct _OptionType { char *name; /* the name of the entry */ char *cp_submit_value; /* the value to submit */ int value_cs; /* charset value is in */ struct _OptionType *next; /* the next entry */ } OptionType; /* * The FormInfo structure is used to contain the form field data within each * anchor. A pointer to this structure is in the TextAnchor struct. */ typedef struct _FormInfo { char *name; /* the name of the link */ int number; /* which form is the link within */ int type; /* string, int, etc. */ char *value; /* user entered string data */ char *orig_value; /* the original value */ int size; /* width on the screen */ size_t maxlength; /* max width of data */ int group; /* a group associated with the link * this is used for select's */ int num_value; /* value of the numerical fields */ int hrange; /* high numerical range */ int lrange; /* low numerical range */ OptionType *select_list; /* array of option choices */ char *submit_action; /* form's action */ int submit_method; /* form's method */ char *submit_enctype; /* form's entype */ char *submit_title; /* form's title */ BOOL no_cache; /* Always resubmit? */ char *cp_submit_value; /* option value to submit */ char *orig_submit_value; /* original submit value */ int size_l; /* The length of the option list */ int disabled; /* If YES, can't change values */ int readonly; /* If YES, can't change values */ int name_cs; int value_cs; char *accept_cs; } FormInfo; #define FormIsReadonly(form) ((form) && ((form)->disabled || (form)->readonly)) /* * As structure for info associated with a form. There is some redundancy * here, this shouldn't waste too much memory since the total number of forms * (as opposed to form fields) per doc is expected to be rather small. More * things which are per form rather than per field could be moved here. - kw */ typedef struct _PerFormInfo { int number; /* form number, see GridText.c */ int disabled; /* If YES, can't change values */ FormInfo data; struct _PerFormInfo *next; /* pointer to next form in doc */ int nfields; /* number of fields */ FormInfo *first_field; FormInfo *last_field; /* pointer to last field in form */ char *accept_cs; char *thisacceptcs; /* used during submit */ } PerFormInfo; #define HYPERTEXT_ANCHOR 1 #define INPUT_ANCHOR 2 /* forms mode input fields */ #define INTERNAL_LINK_ANCHOR 5 /* 1+4, can be used as bitflag... - kw */ typedef enum { F_UNKNOWN = 0, F_TEXT_TYPE, F_PASSWORD_TYPE, F_CHECKBOX_TYPE, F_RADIO_TYPE, F_SUBMIT_TYPE, F_RESET_TYPE, F_OPTION_LIST_TYPE, F_HIDDEN_TYPE, F_TEXTAREA_TYPE, F_RANGE_TYPE, F_FILE_TYPE, F_TEXT_SUBMIT_TYPE, F_IMAGE_SUBMIT_TYPE, F_KEYGEN_TYPE, F_BUTTON_TYPE } FieldTypes; #define F_SUBMITLIKE(type) ((type) == F_SUBMIT_TYPE || \ (type) == F_IMAGE_SUBMIT_TYPE || \ (type) == F_TEXT_SUBMIT_TYPE) #define F_TEXTLIKE(type) ((type) == F_TEXT_TYPE || \ (type) == F_TEXT_SUBMIT_TYPE || \ (type) == F_PASSWORD_TYPE || \ (type) == F_FILE_TYPE || \ (type) == F_TEXTAREA_TYPE) #define WWW_FORM_LINK_TYPE 1 #define WWW_LINK_TYPE 2 #define WWW_INTERN_LINK_TYPE 6 /* can be used as a bitflag... - kw */ #define LINK_LINE_FOUND 8 /* used in follow_link_number, others - kw */ #define LINK_DO_ARROWUP 16 /* returned by HTGetLinkOrFieldStart - kw */ /* #define different lynx modes */ #define NORMAL_LYNX_MODE 1 #define FORMS_LYNX_MODE 2 #define FIRST_ORDER 1 #define MIDDLE_ORDER 2 #define LAST_ORDER 3 /* in LYForms.c */ extern void show_formlink_statusline(const FormInfo * form, int for_what); #ifdef __cplusplus } #endif #endif /* HTFORMS_H */ lynx2-8-8/src/HTInit.c000644 023711 023712 00000125704 12245762550 015066 0ustar00dickeylynx000000 000000 /* * $LynxId: HTInit.c,v 1.85 2013/11/28 11:17:21 tom Exp $ * * Configuration-specific Initialization HTInit.c * ---------------------------------------- */ /* Define a basic set of suffixes and presentations * ------------------------------------------------ */ #include /* Implements: */ #include #include #include #include #include #include #include #include #include /* LJM */ #include #include #include #include #include #define CTrace(p) CTRACE2(TRACE_CFG, p) static int HTLoadTypesConfigFile(char *fn, AcceptMedia media); static int HTLoadExtensionsConfigFile(char *fn); #define SET_SUFFIX1(suffix, description, type) \ HTSetSuffix(suffix, description, type, 1.0) #define SET_SUFFIX5(suffix, mimetype, type, description) \ HTSetSuffix5(suffix, mimetype, type, description, 1.0) #define SET_PRESENT(mimetype, command, quality, delay) \ HTSetPresentation(mimetype, command, 0, quality, delay, 0.0, 0L, media) #define SET_EXTERNL(rep_in, rep_out, command, quality) \ HTSetConversion(rep_in, rep_out, command, quality, 3.0, 0.0, 0L, mediaEXT) #define SET_INTERNL(rep_in, rep_out, command, quality) \ HTSetConversion(rep_in, rep_out, command, quality, 0.0, 0.0, 0L, mediaINT) void HTFormatInit(void) { AcceptMedia media = mediaEXT; CTrace((tfp, "HTFormatInit\n")); #ifdef NeXT SET_PRESENT("application/postscript", "open %s", 1.0, 2.0); SET_PRESENT("image/x-tiff", "open %s", 2.0, 2.0); SET_PRESENT("image/tiff", "open %s", 1.0, 2.0); SET_PRESENT("audio/basic", "open %s", 1.0, 2.0); SET_PRESENT("*", "open %s", 1.0, 0.0); #else if (LYgetXDisplay() != 0) { /* Must have X11 */ SET_PRESENT("application/postscript", "ghostview %s&", 1.0, 3.0); if (non_empty(XLoadImageCommand)) { /* *INDENT-OFF* */ SET_PRESENT("image/gif", XLoadImageCommand, 1.0, 3.0); SET_PRESENT("image/x-xbm", XLoadImageCommand, 1.0, 3.0); SET_PRESENT("image/x-xbitmap", XLoadImageCommand, 1.0, 3.0); SET_PRESENT("image/x-png", XLoadImageCommand, 2.0, 3.0); SET_PRESENT("image/png", XLoadImageCommand, 1.0, 3.0); SET_PRESENT("image/x-rgb", XLoadImageCommand, 1.0, 3.0); SET_PRESENT("image/x-tiff", XLoadImageCommand, 2.0, 3.0); SET_PRESENT("image/tiff", XLoadImageCommand, 1.0, 3.0); SET_PRESENT("image/jpeg", XLoadImageCommand, 1.0, 3.0); /* *INDENT-ON* */ } SET_PRESENT("video/mpeg", "mpeg_play %s &", 1.0, 3.0); } #endif #ifdef EXEC_SCRIPTS /* set quality to 999.0 for protected exec applications */ #ifndef VMS SET_PRESENT("application/x-csh", "csh %s", 999.0, 3.0); SET_PRESENT("application/x-sh", "sh %s", 999.0, 3.0); SET_PRESENT("application/x-ksh", "ksh %s", 999.0, 3.0); #else SET_PRESENT("application/x-VMS_script", "@%s", 999.0, 3.0); #endif /* not VMS */ #endif /* EXEC_SCRIPTS */ /* * Add our header handlers. */ SET_INTERNL("message/x-http-redirection", "*", HTMIMERedirect, 2.0); SET_INTERNL("message/x-http-redirection", "www/present", HTMIMERedirect, 2.0); SET_INTERNL("message/x-http-redirection", "www/debug", HTMIMERedirect, 1.0); SET_INTERNL("www/mime", "www/present", HTMIMEConvert, 1.0); SET_INTERNL("www/mime", "www/download", HTMIMEConvert, 1.0); SET_INTERNL("www/mime", "www/source", HTMIMEConvert, 1.0); SET_INTERNL("www/mime", "www/dump", HTMIMEConvert, 1.0); /* * Add our compressed file handlers. */ SET_INTERNL("www/compressed", "www/download", HTCompressed, 1.0); SET_INTERNL("www/compressed", "www/present", HTCompressed, 1.0); SET_INTERNL("www/compressed", "www/source", HTCompressed, 1.0); SET_INTERNL("www/compressed", "www/dump", HTCompressed, 1.0); /* * Added the following to support some content types beginning to surface. */ SET_INTERNL("application/html", "text/x-c", HTMLToC, 0.5); SET_INTERNL("application/html", "text/plain", HTMLToPlain, 0.5); SET_INTERNL("text/css", "text/plain", HTMLToPlain, 0.5); SET_INTERNL("application/html", "www/present", HTMLPresent, 2.0); SET_INTERNL("application/xhtml+xml", "www/present", HTMLPresent, 2.0); SET_INTERNL("application/xml", "www/present", HTMLPresent, 2.0); SET_INTERNL("application/html", "www/source", HTPlainPresent, 1.0); SET_INTERNL("application/x-wais-source", "www/source", HTPlainPresent, 1.0); SET_INTERNL("application/x-wais-source", "www/present", HTWSRCConvert, 2.0); SET_INTERNL("application/x-wais-source", "www/download", HTWSRCConvert, 1.0); SET_INTERNL("application/x-wais-source", "www/dump", HTWSRCConvert, 1.0); /* * Save all unknown mime types to disk. */ SET_EXTERNL("www/source", "www/present", HTSaveToFile, 1.0); SET_EXTERNL("www/source", "www/source", HTSaveToFile, 1.0); SET_EXTERNL("www/source", "www/download", HTSaveToFile, 1.0); SET_EXTERNL("www/source", "*", HTSaveToFile, 1.0); /* * Output all www/dump presentations to stdout. */ SET_EXTERNL("www/source", "www/dump", HTDumpToStdout, 1.0); /* * Now add our basic conversions. */ SET_INTERNL("text/x-sgml", "www/source", HTPlainPresent, 1.0); SET_INTERNL("text/x-sgml", "www/present", HTMLPresent, 2.0); SET_INTERNL("text/sgml", "www/source", HTPlainPresent, 1.0); SET_INTERNL("text/sgml", "www/present", HTMLPresent, 1.0); SET_INTERNL("text/css", "www/present", HTPlainPresent, 1.0); SET_INTERNL("text/plain", "www/present", HTPlainPresent, 1.0); SET_INTERNL("text/plain", "www/source", HTPlainPresent, 1.0); SET_INTERNL("text/html", "www/source", HTPlainPresent, 1.0); SET_INTERNL("text/html", "text/x-c", HTMLToC, 0.5); SET_INTERNL("text/html", "text/plain", HTMLToPlain, 0.5); SET_INTERNL("text/html", "www/present", HTMLPresent, 1.0); SET_INTERNL("text/xml", "www/present", HTMLPresent, 2.0); if (LYisAbsPath(global_type_map)) { /* These should override the default types as necessary. */ HTLoadTypesConfigFile(global_type_map, mediaSYS); } /* * Load the local maps. */ if (IsOurFile(LYAbsOrHomePath(&personal_type_map)) && LYCanReadFile(personal_type_map)) { /* These should override everything else. */ HTLoadTypesConfigFile(personal_type_map, mediaUSR); } /* * Put text/html and text/plain at beginning of list. - kw */ HTReorderPresentation(WWW_PLAINTEXT, WWW_PRESENT); HTReorderPresentation(WWW_HTML, WWW_PRESENT); /* * Analyze the list, and set 'get_accept' for those whose representations * are not redundant. */ HTFilterPresentations(); } void HTPreparsedFormatInit(void) { CTrace((tfp, "HTPreparsedFormatInit\n")); if (LYPreparsedSource) { SET_INTERNL("text/html", "www/source", HTMLParsedPresent, 1.0); SET_INTERNL("text/html", "www/dump", HTMLParsedPresent, 1.0); } } /* Some of the following is taken from: */ /* Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) Permission to use, copy, modify, and distribute this material for any purpose and without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies, and that the name of Bellcore not be used in advertising or publicity pertaining to this material without the specific, prior written permission of an authorized representative of Bellcore. BELLCORE MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. */ /****************************************************** Metamail -- A tool to help diverse mail readers cope with diverse multimedia mail formats. Author: Nathaniel S. Borenstein, Bellcore ******************************************************* */ struct MailcapEntry { char *contenttype; char *command; char *testcommand; int needsterminal; int copiousoutput; int needtofree; char *label; char *printcommand; char *nametemplate; float quality; long int maxbytes; }; static int ExitWithError(const char *txt); static int PassesTest(struct MailcapEntry *mc); static char *GetCommand(char *s, char **t) { char *s2; int quoted = 0; s = LYSkipBlanks(s); /* marca -- added + 1 for error case -- oct 24, 1993. */ s2 = typeMallocn(char, strlen(s) * 2 + 1); /* absolute max, if all % signs */ if (!s2) ExitWithError(MEMORY_EXHAUSTED_ABORT); assert(s2 != NULL); *t = s2; while (non_empty(s)) { if (quoted) { if (*s == '%') *s2++ = '%'; /* Quote through next level, ugh! */ *s2++ = *s++; quoted = 0; } else { if (*s == ';') { *s2 = '\0'; return (++s); } if (*s == ESCAPE) { quoted = 1; ++s; } else { *s2++ = *s++; } } } *s2 = '\0'; return (NULL); } /* no leading or trailing space, all lower case */ static char *Cleanse(char *s) { LYTrimLeading(s); LYTrimTrailing(s); LYLowerCase(s); return (s); } /* remove unnecessary (unquoted) blanks in a shell command */ static void TrimCommand(char *command) { LYTrimTrailing(command); #ifdef UNIX { char *s = command; char *d = command; int ch; int c0 = ' '; BOOL escape = FALSE; BOOL dquote = FALSE; BOOL squote = FALSE; while ((ch = *s++) != '\0') { if (escape) { escape = FALSE; } else if (squote) { if (ch == SQUOTE) squote = FALSE; } else if (dquote) { switch (ch) { case DQUOTE: dquote = FALSE; break; case ESCAPE: escape = TRUE; break; } } else { switch (ch) { case DQUOTE: dquote = TRUE; break; case SQUOTE: squote = TRUE; break; } } if (!escape && !dquote && !squote) { if (ch == '\t') ch = ' '; if (ch == ' ') { if (c0 == ' ') continue; } } *d++ = (char) ch; c0 = ch; } *d = '\0'; } #endif } static int ProcessMailcapEntry(FILE *fp, struct MailcapEntry *mc, AcceptMedia media) { size_t rawentryalloc = 2000, len, need; char *rawentry, *s, *t; char *LineBuf = NULL; rawentry = (char *) malloc(rawentryalloc); if (!rawentry) ExitWithError(MEMORY_EXHAUSTED_ABORT); assert(rawentry != NULL); *rawentry = '\0'; while (LYSafeGets(&LineBuf, fp) != 0) { LYTrimNewline(LineBuf); if (LineBuf[0] == '#' || LineBuf[0] == '\0') continue; len = strlen(LineBuf); need = len + strlen(rawentry) + 1; if (need > rawentryalloc) { rawentryalloc += (2000 + need); rawentry = typeRealloc(char, rawentry, rawentryalloc); if (!rawentry) ExitWithError(MEMORY_EXHAUSTED_ABORT); assert(rawentry != NULL); } if (len > 0 && LineBuf[len - 1] == ESCAPE) { LineBuf[len - 1] = '\0'; strcat(rawentry, LineBuf); } else { strcat(rawentry, LineBuf); break; } } FREE(LineBuf); t = s = LYSkipBlanks(rawentry); if (!*s) { /* totally blank entry -- quietly ignore */ FREE(rawentry); return (0); } s = StrChr(rawentry, ';'); if (s == NULL) { CTrace((tfp, "ProcessMailcapEntry: Ignoring invalid mailcap entry: %s\n", rawentry)); FREE(rawentry); return (0); } *s++ = '\0'; if (!strncasecomp(t, "text/html", 9) || !strncasecomp(t, "text/plain", 10)) { --s; *s = ';'; CTrace((tfp, "ProcessMailcapEntry: Ignoring mailcap entry: %s\n", rawentry)); FREE(rawentry); return (0); } LYRemoveBlanks(rawentry); LYLowerCase(rawentry); mc->needsterminal = 0; mc->copiousoutput = 0; mc->needtofree = 1; mc->testcommand = NULL; mc->label = NULL; mc->printcommand = NULL; mc->contenttype = NULL; StrAllocCopy(mc->contenttype, rawentry); mc->quality = (float) 1.0; mc->maxbytes = 0; t = GetCommand(s, &mc->command); if (!t) { goto assign_presentation; } s = LYSkipBlanks(t); while (s) { char *arg, *eq, *mallocd_string; t = GetCommand(s, &mallocd_string); arg = mallocd_string; eq = StrChr(arg, '='); if (eq) { *eq++ = '\0'; eq = LYSkipBlanks(eq); } if (non_empty(arg)) { arg = Cleanse(arg); if (!strcmp(arg, "needsterminal")) { mc->needsterminal = 1; } else if (!strcmp(arg, "copiousoutput")) { mc->copiousoutput = 1; } else if (eq && !strcmp(arg, "test")) { mc->testcommand = NULL; StrAllocCopy(mc->testcommand, eq); TrimCommand(mc->testcommand); CTrace((tfp, "ProcessMailcapEntry: Found testcommand:%s\n", mc->testcommand)); } else if (eq && !strcmp(arg, "description")) { mc->label = eq; /* ignored */ } else if (eq && !strcmp(arg, "label")) { mc->label = eq; /* ignored: bogus old name for description */ } else if (eq && !strcmp(arg, "print")) { mc->printcommand = eq; /* ignored */ } else if (eq && !strcmp(arg, "textualnewlines")) { /* no support for now. What does this do anyways? */ /* ExceptionalNewline(mc->contenttype, atoi(eq)); */ } else if (eq && !strcmp(arg, "q")) { mc->quality = (float) atof(eq); if (mc->quality > 0.000 && mc->quality < 0.001) mc->quality = (float) 0.001; } else if (eq && !strcmp(arg, "mxb")) { mc->maxbytes = atol(eq); if (mc->maxbytes < 0) mc->maxbytes = 0; } else if (strcmp(arg, "notes")) { /* IGNORE notes field */ if (*arg) CTrace((tfp, "ProcessMailcapEntry: Ignoring mailcap flag '%s'.\n", arg)); } } FREE(mallocd_string); s = t; } assign_presentation: FREE(rawentry); if (PassesTest(mc)) { CTrace((tfp, "ProcessMailcapEntry Setting up conversion %s : %s\n", mc->contenttype, mc->command)); HTSetPresentation(mc->contenttype, mc->command, mc->testcommand, mc->quality, 3.0, 0.0, mc->maxbytes, media); } FREE(mc->command); FREE(mc->testcommand); FREE(mc->contenttype); return (1); } #define L_CURL '{' #define R_CURL '}' static const char *LYSkipQuoted(const char *s) { int escaped = 0; ++s; /* skip first quote */ while (*s != 0) { if (escaped) { escaped = 0; } else if (*s == ESCAPE) { escaped = 1; } else if (*s == DQUOTE) { ++s; break; } ++s; } return s; } /* * Note: the tspecials[] here are those defined for Content-Type header, so * this function is not really general-purpose. */ static const char *LYSkipToken(const char *s) { static const char tspecials[] = "\"()<>@,;:\\/[]?.="; while (*s != '\0' && !WHITE(*s) && StrChr(tspecials, *s) == 0) { ++s; } return s; } static const char *LYSkipValue(const char *s) { if (*s == DQUOTE) s = LYSkipQuoted(s); else s = LYSkipToken(s); return s; } /* * Copy the value from the source, dequoting if needed. */ static char *LYCopyValue(const char *s) { const char *t; char *result = 0; int j, k; if (*s == DQUOTE) { t = LYSkipQuoted(s); StrAllocCopy(result, s + 1); result[t - s - 2] = '\0'; for (j = k = 0;; ++j, ++k) { if (result[j] == ESCAPE) { ++j; } if ((result[k] = result[j]) == '\0') break; } } else { t = LYSkipToken(s); StrAllocCopy(result, s); result[t - s] = '\0'; } return result; } /* * The "Content-Type:" field, contains zero or more parameters after a ';'. * Return the value of the named parameter, or null. */ static char *LYGetContentType(const char *name, const char *params) { char *result = 0; if (params != 0) { if (name != 0) { size_t length = strlen(name); const char *test = StrChr(params, ';'); /* skip type/subtype */ const char *next; while (test != 0) { BOOL found = FALSE; ++test; /* skip the ';' */ test = LYSkipCBlanks(test); next = LYSkipToken(test); if ((next - test) == (int) length && !StrNCmp(test, name, length)) { found = TRUE; } test = LYSkipCBlanks(next); if (*test == '=') { ++test; test = LYSkipCBlanks(test); if (found) { result = LYCopyValue(test); break; } else { test = LYSkipValue(test); } test = LYSkipCBlanks(test); } if (*test != ';') { break; /* we're lost */ } } } else { /* return the content-type */ StrAllocCopy(result, params); *LYSkipNonBlanks(result) = '\0'; } } return result; } /* * Check if the command uses a "%s" substitution. We need to know this, to * decide when to create temporary files, etc. */ BOOL LYMailcapUsesPctS(const char *controlstring) { BOOL result = FALSE; const char *from; const char *next; int prefixed = 0; int escaped = 0; for (from = controlstring; *from != '\0'; from++) { if (escaped) { escaped = 0; } else if (*from == ESCAPE) { escaped = 1; } else if (prefixed) { prefixed = 0; switch (*from) { case '%': /* not defined */ case 'n': case 'F': case 't': break; case 's': result = TRUE; break; case L_CURL: next = StrChr(from, R_CURL); if (next != 0) { from = next; break; } /* FALLTHRU */ default: break; } } else if (*from == '%') { prefixed = 1; } } return result; } /* * Build the command string for testing or executing a mailcap entry. * If a substitution from the Content-Type header is requested but no * parameters are available, return -1, otherwise 0. * * This does not support multipart %n or %F (does this apply to lynx?) */ static int BuildCommand(HTChunk *cmd, const char *controlstring, const char *TmpFileName, const char *params) { int result = 0; size_t TmpFileLen = strlen(TmpFileName); const char *from; const char *next; char *name, *value; int prefixed = 0; int escaped = 0; for (from = controlstring; *from != '\0'; from++) { if (escaped) { escaped = 0; HTChunkPutc(cmd, UCH(*from)); } else if (*from == ESCAPE) { escaped = 1; } else if (prefixed) { prefixed = 0; switch (*from) { case '%': /* not defined */ HTChunkPutc(cmd, UCH(*from)); break; case 'n': /* FALLTHRU */ case 'F': CTrace((tfp, "BuildCommand: Bad mailcap \"test\" clause: %s\n", controlstring)); break; case 't': if ((value = LYGetContentType(NULL, params)) != 0) { HTChunkPuts(cmd, value); FREE(value); } break; case 's': if (TmpFileLen) { HTChunkPuts(cmd, TmpFileName); } break; case L_CURL: next = StrChr(from, R_CURL); if (next != 0) { if (params != 0) { ++from; name = 0; HTSprintf0(&name, "%.*s", (int) (next - from), from); if ((value = LYGetContentType(name, params)) != 0) { HTChunkPuts(cmd, value); FREE(value); } else if (name) { if (!strcmp(name, "charset")) { HTChunkPuts(cmd, "ISO-8859-1"); } else { CTrace((tfp, "BuildCommand no value for %s\n", name)); } } FREE(name); } else { result = -1; } from = next; break; } /* FALLTHRU */ default: CTrace((tfp, "BuildCommand: Ignoring unrecognized format code in mailcap file '%%%c'.\n", *from)); break; } } else if (*from == '%') { prefixed = 1; } else { HTChunkPutc(cmd, UCH(*from)); } } HTChunkTerminate(cmd); return result; } /* * Build the mailcap test-command and execute it. This is only invoked when * we cannot tell just by looking at the command if it would succeed. * * Returns 0 for success, -1 for error and 1 for deferred. */ int LYTestMailcapCommand(const char *testcommand, const char *params) { int result; char TmpFileName[LY_MAXPATH]; HTChunk *expanded = 0; if (LYMailcapUsesPctS(testcommand)) { if (LYOpenTemp(TmpFileName, HTML_SUFFIX, "w") == 0) ExitWithError(CANNOT_OPEN_TEMP); LYCloseTemp(TmpFileName); } else { /* We normally don't need a temp file name - kw */ TmpFileName[0] = '\0'; } expanded = HTChunkCreate(1024); if (BuildCommand(expanded, testcommand, TmpFileName, params) != 0) { result = 1; CTrace((tfp, "PassesTest: Deferring test command: %s\n", expanded->data)); } else { CTrace((tfp, "PassesTest: Executing test command: %s\n", expanded->data)); if ((result = LYSystem(expanded->data)) != 0) { result = -1; CTrace((tfp, "PassesTest: Test failed!\n")); } else { CTrace((tfp, "PassesTest: Test passed!\n")); } } HTChunkFree(expanded); (void) LYRemoveTemp(TmpFileName); return result; } char *LYMakeMailcapCommand(const char *command, const char *params, const char *filename) { HTChunk *expanded = 0; char *result = 0; expanded = HTChunkCreate(1024); BuildCommand(expanded, command, filename, params); StrAllocCopy(result, expanded->data); HTChunkFree(expanded); return result; } #define RTR_forget 0 #define RTR_lookup 1 #define RTR_add 2 static int RememberTestResult(int mode, char *cmd, int result) { struct cmdlist_s { char *cmd; int result; struct cmdlist_s *next; }; static struct cmdlist_s *cmdlist = NULL; struct cmdlist_s *cur; switch (mode) { case RTR_forget: while (cmdlist) { cur = cmdlist->next; FREE(cmdlist->cmd); FREE(cmdlist); cmdlist = cur; } break; case RTR_lookup: for (cur = cmdlist; cur; cur = cur->next) if (!strcmp(cmd, cur->cmd)) return cur->result; return -1; case RTR_add: cur = typecalloc(struct cmdlist_s); if (cur == NULL) outofmem(__FILE__, "RememberTestResult"); assert(cur != NULL); cur->next = cmdlist; StrAllocCopy(cur->cmd, cmd); cur->result = result; cmdlist = cur; break; } return 0; } /* FIXME: this sometimes used caseless comparison, e.g., strcasecomp */ #define SameCommand(tst,ref) !strcmp(tst,ref) static int PassesTest(struct MailcapEntry *mc) { int result; /* * Make sure we have a command */ if (!mc->testcommand) return (1); /* * Save overhead of system() calls by faking these. - FM */ if (SameCommand(mc->testcommand, "test \"$DISPLAY\"") || SameCommand(mc->testcommand, "test \"$DISPLAY\" != \"\"") || SameCommand(mc->testcommand, "test -n \"$DISPLAY\"")) { FREE(mc->testcommand); CTrace((tfp, "PassesTest: Testing for XWINDOWS environment.\n")); if (LYgetXDisplay() != NULL) { CTrace((tfp, "PassesTest: Test passed!\n")); return (0 == 0); } else { CTrace((tfp, "PassesTest: Test failed!\n")); return (-1 == 0); } } if (SameCommand(mc->testcommand, "test -z \"$DISPLAY\"")) { FREE(mc->testcommand); CTrace((tfp, "PassesTest: Testing for NON_XWINDOWS environment.\n")); if (LYgetXDisplay() == NULL) { CTrace((tfp, "PassesTest: Test passed!\n")); return (0 == 0); } else { CTrace((tfp, "PassesTest: Test failed!\n")); return (-1 == 0); } } /* * Why do anything but return success for this one! - FM */ if (SameCommand(mc->testcommand, "test -n \"$LYNX_VERSION\"")) { FREE(mc->testcommand); CTrace((tfp, "PassesTest: Testing for LYNX environment.\n")); CTrace((tfp, "PassesTest: Test passed!\n")); return (0 == 0); } else /* * ... or failure for this one! - FM */ if (SameCommand(mc->testcommand, "test -z \"$LYNX_VERSION\"")) { FREE(mc->testcommand); CTrace((tfp, "PassesTest: Testing for non-LYNX environment.\n")); CTrace((tfp, "PassesTest: Test failed!\n")); return (-1 == 0); } result = RememberTestResult(RTR_lookup, mc->testcommand, 0); if (result == -1) { result = LYTestMailcapCommand(mc->testcommand, NULL); RememberTestResult(RTR_add, mc->testcommand, result ? 1 : 0); } /* * Free the test command as well since * we wont be needing it anymore. */ if (result != 1) FREE(mc->testcommand); if (result < 0) { CTrace((tfp, "PassesTest: Test failed!\n")); } else if (result == 0) { CTrace((tfp, "PassesTest: Test passed!\n")); } return (result >= 0); } static int ProcessMailcapFile(char *file, AcceptMedia media) { struct MailcapEntry mc; FILE *fp; CTrace((tfp, "ProcessMailcapFile: Loading file '%s'.\n", file)); if ((fp = fopen(file, TXT_R)) == NULL) { CTrace((tfp, "ProcessMailcapFile: Could not open '%s'.\n", file)); return (-1 == 0); } while (fp && !feof(fp)) { ProcessMailcapEntry(fp, &mc, media); } LYCloseInput(fp); RememberTestResult(RTR_forget, NULL, 0); return (0 == 0); } static int ExitWithError(const char *txt) { if (txt) fprintf(tfp, "Lynx: %s\n", txt); exit_immediately(EXIT_FAILURE); return (-1); } /* Reverse the entries from each mailcap after it has been read, so that * earlier entries have precedence. Set to 0 to get traditional lynx * behavior, which means that the last match wins. - kw */ static int reverse_mailcap = 1; static int HTLoadTypesConfigFile(char *fn, AcceptMedia media) { int result = 0; HTList *saved = HTPresentations; if (reverse_mailcap) { /* temporarily hide existing list */ HTPresentations = NULL; } result = ProcessMailcapFile(fn, media); if (reverse_mailcap) { if (result && HTPresentations) { HTList_reverse(HTPresentations); HTList_appendList(HTPresentations, saved); FREE(saved); } else { HTPresentations = saved; } } return result; } /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* Define a basic set of suffixes * ------------------------------ * * The LAST suffix for a type is that used for temporary files * of that type. * The quality is an apriori bias as to whether the file should be * used. Not that different suffixes can be used to represent files * which are of the same format but are originals or regenerated, * with different values. */ /* * Additional notes: the encoding parameter may be taken into account when * looking for a match; for that purpose "7bit", "8bit", and "binary" are * equivalent. * * Use of mixed case and of pseudo MIME types with embedded spaces should be * avoided. It was once necessary for getting the fancy strings into type * labels in FTP directory listings, but that can now be done with the * description field (using HTSetSuffix5). AFAIK the only effect of such * "fancy" (and mostly invalid) types that cannot be reproduced by using a * description fields is some statusline messages in SaveToFile (HTFWriter.c). * And showing the user an invalid MIME type as the 'Content-type:' is not such * a hot idea anyway, IMO. Still, if you want it, it is still possible (even * in lynx.cfg now), but use of it in the defaults below has been reduced. * * Case variations rely on peculiar behavior of HTAtom.c for matching. They * lead to surprising behavior, Lynx retains the case of a string in the form * first encountered after starting up. So while later suffix rules generally * override or modify earlier ones, the case used for a MIME time is determined * by the first suffix rule (or other occurrence). Matching in HTAtom_for is * effectively case insensitive, except for the first character of the string * which is treated as case-sensitive by the hash function there; best not to * rely on that, rather convert MIME types to lowercase on input as is already * done in most places (And HTAtom could become consistently case-sensitive, as * in newer W3C libwww). * - kw 1999-10-12 */ void HTFileInit(void) { #ifdef BUILTIN_SUFFIX_MAPS if (LYUseBuiltinSuffixes) { CTrace((tfp, "HTFileInit: Loading default (HTInit) extension maps.\n")); /* default suffix interpretation */ SET_SUFFIX1("*", "text/plain", "8bit"); SET_SUFFIX1("*.*", "text/plain", "8bit"); #ifdef EXEC_SCRIPTS /* * define these extensions for exec scripts. */ #ifndef VMS /* for csh exec links */ HTSetSuffix(".csh", "application/x-csh", "8bit", 0.8); HTSetSuffix(".sh", "application/x-sh", "8bit", 0.8); HTSetSuffix(".ksh", "application/x-ksh", "8bit", 0.8); #else HTSetSuffix(".com", "application/x-VMS_script", "8bit", 0.8); #endif /* !VMS */ #endif /* EXEC_SCRIPTS */ /* * Some of the old incarnation of the mappings is preserved and can be had * by defining TRADITIONAL_SUFFIXES. This is for some cases where I felt * the old rules might be preferred by someone, for some reason. It's not * done consistently. A lot more of this stuff could probably be changed * too or omitted, now that nearly the equivalent functionality is * available in lynx.cfg. - kw 1999-10-12 */ /* *INDENT-OFF* */ SET_SUFFIX1(".saveme", "application/x-Binary", "binary"); SET_SUFFIX1(".dump", "application/x-Binary", "binary"); SET_SUFFIX1(".bin", "application/x-Binary", "binary"); SET_SUFFIX1(".arc", "application/x-Compressed", "binary"); SET_SUFFIX1(".alpha-exe", "application/x-Executable", "binary"); SET_SUFFIX1(".alpha_exe", "application/x-Executable", "binary"); SET_SUFFIX1(".AXP-exe", "application/x-Executable", "binary"); SET_SUFFIX1(".AXP_exe", "application/x-Executable", "binary"); SET_SUFFIX1(".VAX-exe", "application/x-Executable", "binary"); SET_SUFFIX1(".VAX_exe", "application/x-Executable", "binary"); SET_SUFFIX5(".exe", "application/octet-stream", "binary", "Executable"); #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".exe.Z", "application/x-Comp. Executable", "binary"); SET_SUFFIX1(".Z", "application/UNIX Compressed", "binary"); SET_SUFFIX1(".tar_Z", "application/UNIX Compr. Tar", "binary"); SET_SUFFIX1(".tar.Z", "application/UNIX Compr. Tar", "binary"); #else SET_SUFFIX5(".Z", "application/x-compress", "binary", "UNIX Compressed"); SET_SUFFIX5(".Z", NULL, "compress", "UNIX Compressed"); SET_SUFFIX5(".exe.Z", "application/octet-stream", "compress", "Executable"); SET_SUFFIX5(".tar_Z", "application/x-tar", "compress", "UNIX Compr. Tar"); SET_SUFFIX5(".tar.Z", "application/x-tar", "compress", "UNIX Compr. Tar"); #endif #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1("-gz", "application/GNU Compressed", "binary"); SET_SUFFIX1("_gz", "application/GNU Compressed", "binary"); SET_SUFFIX1(".gz", "application/GNU Compressed", "binary"); SET_SUFFIX5(".tar.gz", "application/x-tar", "binary", "GNU Compr. Tar"); SET_SUFFIX5(".tgz", "application/x-tar", "gzip", "GNU Compr. Tar"); #else SET_SUFFIX5("-gz", "application/x-gzip", "binary", "GNU Compressed"); SET_SUFFIX5("_gz", "application/x-gzip", "binary", "GNU Compressed"); SET_SUFFIX5(".gz", "application/x-gzip", "binary", "GNU Compressed"); SET_SUFFIX5("-gz", NULL, "gzip", "GNU Compressed"); SET_SUFFIX5("_gz", NULL, "gzip", "GNU Compressed"); SET_SUFFIX5(".gz", NULL, "gzip", "GNU Compressed"); SET_SUFFIX5(".tar.gz", "application/x-tar", "gzip", "GNU Compr. Tar"); SET_SUFFIX5(".tgz", "application/x-tar", "gzip", "GNU Compr. Tar"); #endif #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".src", "application/x-WAIS-source", "8bit"); SET_SUFFIX1(".wsrc", "application/x-WAIS-source", "8bit"); #else SET_SUFFIX5(".wsrc", "application/x-wais-source", "8bit", "WAIS-source"); #endif SET_SUFFIX5(".zip", "application/zip", "binary", "Zip File"); SET_SUFFIX1(".zz", "application/x-deflate", "binary"); SET_SUFFIX1(".zz", "application/deflate", "binary"); SET_SUFFIX1(".bz2", "application/x-bzip2", "binary"); SET_SUFFIX1(".bz2", "application/bzip2", "binary"); #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".uu", "application/x-UUencoded", "8bit"); SET_SUFFIX1(".hqx", "application/x-Binhex", "8bit"); SET_SUFFIX1(".o", "application/x-Prog. Object", "binary"); SET_SUFFIX1(".a", "application/x-Prog. Library", "binary"); #else SET_SUFFIX5(".uu", "application/x-uuencoded", "7bit", "UUencoded"); SET_SUFFIX5(".hqx", "application/mac-binhex40", "8bit", "Mac BinHex"); HTSetSuffix5(".o", "application/octet-stream", "binary", "Prog. Object", 0.5); HTSetSuffix5(".a", "application/octet-stream", "binary", "Prog. Library", 0.5); HTSetSuffix5(".so", "application/octet-stream", "binary", "Shared Lib", 0.5); #endif SET_SUFFIX5(".oda", "application/oda", "binary", "ODA"); SET_SUFFIX5(".pdf", "application/pdf", "binary", "PDF"); SET_SUFFIX5(".eps", "application/postscript", "8bit", "Postscript"); SET_SUFFIX5(".ai", "application/postscript", "8bit", "Postscript"); SET_SUFFIX5(".ps", "application/postscript", "8bit", "Postscript"); SET_SUFFIX5(".rtf", "application/rtf", "8bit", "RTF"); SET_SUFFIX5(".dvi", "application/x-dvi", "8bit", "DVI"); SET_SUFFIX5(".hdf", "application/x-hdf", "8bit", "HDF"); SET_SUFFIX1(".cdf", "application/x-netcdf", "8bit"); SET_SUFFIX1(".nc", "application/x-netcdf", "8bit"); #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".latex", "application/x-Latex", "8bit"); SET_SUFFIX1(".tex", "application/x-Tex", "8bit"); SET_SUFFIX1(".texinfo", "application/x-Texinfo", "8bit"); SET_SUFFIX1(".texi", "application/x-Texinfo", "8bit"); #else SET_SUFFIX5(".latex", "application/x-latex", "8bit", "LaTeX"); SET_SUFFIX5(".tex", "text/x-tex", "8bit", "TeX"); SET_SUFFIX5(".texinfo", "application/x-texinfo", "8bit", "Texinfo"); SET_SUFFIX5(".texi", "application/x-texinfo", "8bit", "Texinfo"); #endif #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".t", "application/x-Troff", "8bit"); SET_SUFFIX1(".tr", "application/x-Troff", "8bit"); SET_SUFFIX1(".roff", "application/x-Troff", "8bit"); SET_SUFFIX1(".man", "application/x-Troff-man", "8bit"); SET_SUFFIX1(".me", "application/x-Troff-me", "8bit"); SET_SUFFIX1(".ms", "application/x-Troff-ms", "8bit"); #else SET_SUFFIX5(".t", "application/x-troff", "8bit", "Troff"); SET_SUFFIX5(".tr", "application/x-troff", "8bit", "Troff"); SET_SUFFIX5(".roff", "application/x-troff", "8bit", "Troff"); SET_SUFFIX5(".man", "application/x-troff-man", "8bit", "Man Page"); SET_SUFFIX5(".me", "application/x-troff-me", "8bit", "Troff me"); SET_SUFFIX5(".ms", "application/x-troff-ms", "8bit", "Troff ms"); #endif SET_SUFFIX1(".zoo", "application/x-Zoo File", "binary"); #if defined(TRADITIONAL_SUFFIXES) || defined(VMS) SET_SUFFIX1(".bak", "application/x-VMS BAK File", "binary"); SET_SUFFIX1(".bkp", "application/x-VMS BAK File", "binary"); SET_SUFFIX1(".bck", "application/x-VMS BAK File", "binary"); SET_SUFFIX5(".bkp_gz", "application/octet-stream", "gzip", "GNU BAK File"); SET_SUFFIX5(".bkp-gz", "application/octet-stream", "gzip", "GNU BAK File"); SET_SUFFIX5(".bck_gz", "application/octet-stream", "gzip", "GNU BAK File"); SET_SUFFIX5(".bck-gz", "application/octet-stream", "gzip", "GNU BAK File"); SET_SUFFIX5(".bkp-Z", "application/octet-stream", "compress", "Comp. BAK File"); SET_SUFFIX5(".bkp_Z", "application/octet-stream", "compress", "Comp. BAK File"); SET_SUFFIX5(".bck-Z", "application/octet-stream", "compress", "Comp. BAK File"); SET_SUFFIX5(".bck_Z", "application/octet-stream", "compress", "Comp. BAK File"); #else HTSetSuffix5(".bak", NULL, "binary", "Backup", 0.5); SET_SUFFIX5(".bkp", "application/octet-stream", "binary", "VMS BAK File"); SET_SUFFIX5(".bck", "application/octet-stream", "binary", "VMS BAK File"); #endif #if defined(TRADITIONAL_SUFFIXES) || defined(VMS) SET_SUFFIX1(".hlb", "application/x-VMS Help Libr.", "binary"); SET_SUFFIX1(".olb", "application/x-VMS Obj. Libr.", "binary"); SET_SUFFIX1(".tlb", "application/x-VMS Text Libr.", "binary"); SET_SUFFIX1(".obj", "application/x-VMS Prog. Obj.", "binary"); SET_SUFFIX1(".decw$book", "application/x-DEC BookReader", "binary"); SET_SUFFIX1(".mem", "application/x-RUNOFF-MANUAL", "8bit"); #else SET_SUFFIX5(".hlb", "application/octet-stream", "binary", "VMS Help Libr."); SET_SUFFIX5(".olb", "application/octet-stream", "binary", "VMS Obj. Libr."); SET_SUFFIX5(".tlb", "application/octet-stream", "binary", "VMS Text Libr."); SET_SUFFIX5(".obj", "application/octet-stream", "binary", "Prog. Object"); SET_SUFFIX5(".decw$book", "application/octet-stream", "binary", "DEC BookReader"); SET_SUFFIX5(".mem", "text/x-runoff-manual", "8bit", "RUNOFF-MANUAL"); #endif SET_SUFFIX1(".vsd", "application/visio", "binary"); SET_SUFFIX5(".lha", "application/x-lha", "binary", "lha File"); SET_SUFFIX5(".lzh", "application/x-lzh", "binary", "lzh File"); SET_SUFFIX5(".sea", "application/x-sea", "binary", "sea File"); #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX5(".sit", "application/x-sit", "binary", "sit File"); #else SET_SUFFIX5(".sit", "application/x-stuffit", "binary", "StuffIt"); #endif SET_SUFFIX5(".dms", "application/x-dms", "binary", "dms File"); SET_SUFFIX5(".iff", "application/x-iff", "binary", "iff File"); SET_SUFFIX1(".bcpio", "application/x-bcpio", "binary"); SET_SUFFIX1(".cpio", "application/x-cpio", "binary"); #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".gtar", "application/x-gtar", "binary"); #endif SET_SUFFIX1(".shar", "application/x-shar", "8bit"); SET_SUFFIX1(".share", "application/x-share", "8bit"); #ifdef TRADITIONAL_SUFFIXES SET_SUFFIX1(".sh", "application/x-sh", "8bit"); /* xtra */ #endif SET_SUFFIX1(".sv4cpio", "application/x-sv4cpio", "binary"); SET_SUFFIX1(".sv4crc", "application/x-sv4crc", "binary"); SET_SUFFIX5(".tar", "application/x-tar", "binary", "Tar File"); SET_SUFFIX1(".ustar", "application/x-ustar", "binary"); SET_SUFFIX1(".snd", "audio/basic", "binary"); SET_SUFFIX1(".au", "audio/basic", "binary"); SET_SUFFIX1(".aifc", "audio/x-aiff", "binary"); SET_SUFFIX1(".aif", "audio/x-aiff", "binary"); SET_SUFFIX1(".aiff", "audio/x-aiff", "binary"); SET_SUFFIX1(".wav", "audio/x-wav", "binary"); SET_SUFFIX1(".midi", "audio/midi", "binary"); SET_SUFFIX1(".mod", "audio/mod", "binary"); SET_SUFFIX1(".gif", "image/gif", "binary"); SET_SUFFIX1(".ief", "image/ief", "binary"); SET_SUFFIX1(".jfif", "image/jpeg", "binary"); /* xtra */ SET_SUFFIX1(".jfif-tbnl", "image/jpeg", "binary"); /* xtra */ SET_SUFFIX1(".jpe", "image/jpeg", "binary"); SET_SUFFIX1(".jpg", "image/jpeg", "binary"); SET_SUFFIX1(".jpeg", "image/jpeg", "binary"); SET_SUFFIX1(".tif", "image/tiff", "binary"); SET_SUFFIX1(".tiff", "image/tiff", "binary"); SET_SUFFIX1(".ham", "image/ham", "binary"); SET_SUFFIX1(".ras", "image/x-cmu-rast", "binary"); SET_SUFFIX1(".pnm", "image/x-portable-anymap", "binary"); SET_SUFFIX1(".pbm", "image/x-portable-bitmap", "binary"); SET_SUFFIX1(".pgm", "image/x-portable-graymap", "binary"); SET_SUFFIX1(".ppm", "image/x-portable-pixmap", "binary"); SET_SUFFIX1(".png", "image/png", "binary"); SET_SUFFIX1(".rgb", "image/x-rgb", "binary"); SET_SUFFIX1(".xbm", "image/x-xbitmap", "binary"); SET_SUFFIX1(".xpm", "image/x-xpixmap", "binary"); SET_SUFFIX1(".xwd", "image/x-xwindowdump", "binary"); SET_SUFFIX1(".rtx", "text/richtext", "8bit"); SET_SUFFIX1(".tsv", "text/tab-separated-values", "8bit"); SET_SUFFIX1(".etx", "text/x-setext", "8bit"); SET_SUFFIX1(".mpg", "video/mpeg", "binary"); SET_SUFFIX1(".mpe", "video/mpeg", "binary"); SET_SUFFIX1(".mpeg", "video/mpeg", "binary"); SET_SUFFIX1(".mov", "video/quicktime", "binary"); SET_SUFFIX1(".qt", "video/quicktime", "binary"); SET_SUFFIX1(".avi", "video/x-msvideo", "binary"); SET_SUFFIX1(".movie", "video/x-sgi-movie", "binary"); SET_SUFFIX1(".mv", "video/x-sgi-movie", "binary"); SET_SUFFIX1(".mime", "message/rfc822", "8bit"); SET_SUFFIX1(".c", "text/plain", "8bit"); SET_SUFFIX1(".cc", "text/plain", "8bit"); SET_SUFFIX1(".c++", "text/plain", "8bit"); SET_SUFFIX1(".css", "text/plain", "8bit"); SET_SUFFIX1(".h", "text/plain", "8bit"); SET_SUFFIX1(".pl", "text/plain", "8bit"); SET_SUFFIX1(".text", "text/plain", "8bit"); SET_SUFFIX1(".txt", "text/plain", "8bit"); SET_SUFFIX1(".php", "text/html", "8bit"); SET_SUFFIX1(".php3", "text/html", "8bit"); SET_SUFFIX1(".html3", "text/html", "8bit"); SET_SUFFIX1(".ht3", "text/html", "8bit"); SET_SUFFIX1(".phtml", "text/html", "8bit"); SET_SUFFIX1(".shtml", "text/html", "8bit"); SET_SUFFIX1(".sht", "text/html", "8bit"); SET_SUFFIX1(".htmlx", "text/html", "8bit"); SET_SUFFIX1(".htm", "text/html", "8bit"); SET_SUFFIX1(".html", "text/html", "8bit"); /* *INDENT-ON* */ } else { /* LYSuffixRules */ /* * Note that even .html -> text/html, .htm -> text/html are omitted if * default maps are compiled in but then skipped because of a * configuration file directive. Whoever changes the config file in * this way can easily also add the SUFFIX rules there. - kw */ CTrace((tfp, "HTFileInit: Skipping all default (HTInit) extension maps!\n")); } /* LYSuffixRules */ #else /* BUILTIN_SUFFIX_MAPS */ CTrace((tfp, "HTFileInit: Default (HTInit) extension maps not compiled in.\n")); /* * The following two are still used if BUILTIN_SUFFIX_MAPS was undefined. * Without one of them, lynx would always need to have a mapping specified * in a lynx.cfg or mime.types file to be usable for local HTML files at * all. That includes many of the generated user interface pages. - kw */ SET_SUFFIX1(".htm", "text/html", "8bit"); SET_SUFFIX1(".html", "text/html", "8bit"); #endif /* BUILTIN_SUFFIX_MAPS */ if (LYisAbsPath(global_extension_map)) { /* These should override the default extensions as necessary. */ HTLoadExtensionsConfigFile(global_extension_map); } /* * Load the local maps. */ if (IsOurFile(LYAbsOrHomePath(&personal_extension_map)) && LYCanReadFile(personal_extension_map)) { /* These should override everything else. */ HTLoadExtensionsConfigFile(personal_extension_map); } } /* -------------------- Extension config file reading --------------------- */ /* * The following is lifted from NCSA httpd 1.0a1, by Rob McCool; * NCSA httpd is in the public domain, as is this code. * * Modified Oct 97 - KW */ #define MAX_STRING_LEN 256 static int HTGetLine(char *s, int n, FILE *f) { register int i = 0, r; if (!f) return (1); while (1) { r = fgetc(f); s[i] = (char) r; if (s[i] == CR) { r = fgetc(f); if (r == LF) s[i] = (char) r; else if (r != EOF) ungetc(r, f); } if ((r == EOF) || (s[i] == LF) || (s[i] == CR) || (i == (n - 1))) { s[i] = '\0'; return (feof(f) ? 1 : 0); } ++i; } } static void HTGetWord(char *word, char *line, int stop, int stop2) { int x = 0, y; for (x = 0; (line[x] && UCH(line[x]) != UCH(stop) && UCH(line[x]) != UCH(stop2)); x++) { word[x] = line[x]; } word[x] = '\0'; if (line[x]) ++x; y = 0; while ((line[y++] = line[x++])) { ; } return; } static int HTLoadExtensionsConfigFile(char *fn) { char line[MAX_STRING_LEN]; char word[MAX_STRING_LEN]; char *ct; FILE *f; int count = 0; CTrace((tfp, "HTLoadExtensionsConfigFile: Loading file '%s'.\n", fn)); if ((f = fopen(fn, TXT_R)) == NULL) { CTrace((tfp, "HTLoadExtensionsConfigFile: Could not open '%s'.\n", fn)); return count; } while (!(HTGetLine(line, (int) sizeof(line), f))) { HTGetWord(word, line, ' ', '\t'); if (line[0] == '\0' || word[0] == '#') continue; ct = NULL; StrAllocCopy(ct, word); LYLowerCase(ct); while (line[0]) { HTGetWord(word, line, ' ', '\t'); if (word[0] && (word[0] != ' ')) { char *ext = NULL; HTSprintf0(&ext, ".%s", word); LYLowerCase(ext); CTrace((tfp, "setting suffix '%s' to '%s'.\n", ext, ct)); if (strstr(ct, "tex") != NULL || strstr(ct, "postscript") != NULL || strstr(ct, "sh") != NULL || strstr(ct, "troff") != NULL || strstr(ct, "rtf") != NULL) SET_SUFFIX1(ext, ct, "8bit"); else SET_SUFFIX1(ext, ct, "binary"); count++; FREE(ext); } } FREE(ct); } LYCloseInput(f); return count; } lynx2-8-8/src/HTML.c000644 023711 023712 00000735017 12245762550 014477 0ustar00dickeylynx000000 000000 /* * $LynxId: HTML.c,v 1.164 2013/11/28 11:17:39 tom Exp $ * * Structured stream to Rich hypertext converter * ============================================ * * This generates a hypertext object. It converts from the * structured stream interface of HTML events into the style- * oriented interface of the HText.h interface. This module is * only used in clients and should not be linked into servers. * * Override this module if making a new GUI browser. * * Being Overidden * */ #define HTSTREAM_INTERNAL 1 #include #define Lynx_HTML_Handler #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef VMS #include #endif /* VMS */ #ifdef USE_PRETTYSRC #include #endif #ifdef USE_COLOR_STYLE #include #include #include #include #undef SELECTED_STYLES #define pHText_changeStyle(X,Y,Z) {} #if OMIT_SCN_KEEPING # define HCODE_TO_STACK_OFF(x) /*(CSHASHSIZE+1)*/ 88888 /*special value. */ #else # define HCODE_TO_STACK_OFF(x) x /*pass computed value */ #endif #endif /* USE_COLOR_STYLE */ #ifdef USE_SOURCE_CACHE #include #endif #include #include #include #include #define STACKLEVEL(me) ((me->stack + MAX_NESTING - 1) - me->sp) #define DFT_TEXTAREA_COLS 60 #define DFT_TEXTAREA_ROWS 4 #define MAX_TEXTAREA_COLS LYcolLimit #define MAX_TEXTAREA_ROWS (3 * LYlines) #define LimitValue(name, value) \ if (name > value) { \ CTRACE((tfp, "Limited " #name " to %d, was %d\n", \ value, name)); \ name = value; \ } struct _HTStream { const HTStreamClass *isa; #ifdef USE_SOURCE_CACHE HTParentAnchor *anchor; FILE *fp; char *filename; HTChunk *chunk; HTChunk *last_chunk; /* the last chunk in a chain! */ const HTStreamClass *actions; HTStream *target; int status; #else /* .... */ #endif }; static HTStyleSheet *styleSheet = NULL; /* Application-wide */ /* Module-wide style cache */ static HTStyle *styles[HTML_ELEMENTS + LYNX_HTML_EXTRA_ELEMENTS]; /* adding 24 nested list styles */ /* and 3 header alignment styles */ /* and 3 div alignment styles */ static HTStyle *default_style = NULL; const char *LYToolbarName = "LynxPseudoToolbar"; /* used to turn off a style if the HTML author forgot to static int i_prior_style = -1; */ /* * Private function.... */ static int HTML_end_element(HTStructured * me, int element_number, char **include); static int HTML_start_element(HTStructured * me, int element_number, const BOOL *present, STRING2PTR value, int tag_charset, char **include); /* * If we have verbose_img set, display labels for images. */ #define VERBOSE_IMG(value,src_type,string) \ ((verbose_img) ? (newtitle = MakeNewTitle(value,src_type)): string) static char *MakeNewTitle(STRING2PTR value, int src_type); static char *MakeNewImageValue(STRING2PTR value); static char *MakeNewMapValue(STRING2PTR value, const char *mapstr); /* Set an internal flag that the next call to a stack-affecting method * is only internal and the stack manipulation should be skipped. - kw */ #define SET_SKIP_STACK(el_num) if (HTML_dtd.tags[el_num].contents != SGML_EMPTY) \ { me->skip_stack++; } void strtolower(char *i) { if (!i) return; while (*i) { *i = (char) TOLOWER(*i); i++; } } /* Flattening the style structure * ------------------------------ * * On the NeXT, and on any read-only browser, it is simpler for the text to * have a sequence of styles, rather than a nested tree of styles. In this * case we have to flatten the structure as it arrives from SGML tags into a * sequence of styles. */ /* * If style really needs to be set, call this. */ void actually_set_style(HTStructured * me) { if (!me->text) { /* First time through */ LYGetChartransInfo(me); UCSetTransParams(&me->T, me->UCLYhndl, me->UCI, HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT), HTAnchor_getUCInfoStage(me->node_anchor, UCT_STAGE_HTEXT)); me->text = HText_new2(me->node_anchor, me->target); HText_beginAppend(me->text); HText_setStyle(me->text, me->new_style); me->in_word = NO; LYCheckForContentBase(me); } else { HText_setStyle(me->text, me->new_style); } me->old_style = me->new_style; me->style_change = NO; } /* * If you THINK you need to change style, call this. */ static void change_paragraph_style(HTStructured * me, HTStyle *style) { if (me->new_style != style) { me->style_change = YES; me->new_style = style; } me->in_word = NO; } /* * Return true if we should write a message (to LYNXMESSAGES, or the trace * file) telling about some bad HTML that we've found. */ BOOL LYBadHTML(HTStructured * me) { BOOL code = FALSE; switch ((enumBadHtml) cfg_bad_html) { case BAD_HTML_IGNORE: break; case BAD_HTML_TRACE: code = TRUE; break; case BAD_HTML_MESSAGE: code = TRUE; break; case BAD_HTML_WARN: /* * If we're already tracing, do not add a warning. */ if (!TRACE && !me->inBadHTML) { HTUserMsg(BAD_HTML_USE_TRACE); me->inBadHTML = TRUE; } code = TRACE; break; } return code; } /* * Handle the formatted message. */ void LYShowBadHTML(const char *message) { if (dump_output_immediately && dump_to_stderr) fprintf(stderr, "%s", message); switch ((enumBadHtml) cfg_bad_html) { case BAD_HTML_IGNORE: break; case BAD_HTML_TRACE: case BAD_HTML_MESSAGE: case BAD_HTML_WARN: CTRACE((tfp, "%s", message)); break; } switch ((enumBadHtml) cfg_bad_html) { case BAD_HTML_IGNORE: case BAD_HTML_TRACE: case BAD_HTML_WARN: break; case BAD_HTML_MESSAGE: LYstore_message(message); break; } } /*_________________________________________________________________________ * * A C T I O N R O U T I N E S */ /* FIXME: this should be amended to do the substitution only when not in a * multibyte stream. */ #ifdef EXP_JAPANESE_SPACES #define FIX_JAPANESE_SPACES \ (HTCJK == CHINESE || HTCJK == JAPANESE || HTCJK == TAIPEI) /* don't replace '\n' with ' ' if Chinese or Japanese - HN */ #else #define FIX_JAPANESE_SPACES 0 #endif /* Character handling * ------------------ */ void HTML_put_character(HTStructured * me, int c) { unsigned uc = UCH(c); /* * Ignore all non-MAP content when just scanning a document for MAPs. - FM */ if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT) return; c = (int) uc; /* * Do EOL conversion if needed. - FM * * Convert EOL styles: * macintosh: cr --> lf * ascii: cr-lf --> lf * unix: lf --> lf */ if ((me->lastraw == '\r') && c == '\n') { me->lastraw = -1; return; } me->lastraw = c; if (c == '\r') { c = '\n'; uc = UCH(c); } /* * Handle SGML_LITTERAL tags that have HTChunk elements. - FM */ switch (me->sp[0].tag_number) { case HTML_COMMENT: return; /* Do Nothing */ case HTML_TITLE: if (c == LY_SOFT_HYPHEN) return; if (c != '\n' && c != '\t' && c != '\r') { HTChunkPutc(&me->title, uc); } else if (FIX_JAPANESE_SPACES) { if (c == '\t') { HTChunkPutc(&me->title, ' '); } else { return; } } else { HTChunkPutc(&me->title, ' '); } return; case HTML_STYLE: HTChunkPutc(&me->style_block, uc); return; case HTML_SCRIPT: HTChunkPutc(&me->script, uc); return; case HTML_OBJECT: HTChunkPutc(&me->object, uc); return; case HTML_TEXTAREA: HTChunkPutc(&me->textarea, uc); return; case HTML_SELECT: case HTML_OPTION: HTChunkPutc(&me->option, uc); return; case HTML_MATH: HTChunkPutc(&me->math, uc); return; default: if (me->inSELECT) { /* * If we are within a SELECT not caught by the cases above - * HTML_SELECT or HTML_OPTION may not be the last element pushed on * the style stack if there were invalid markup tags within a * SELECT element. For error recovery, treat text as part of the * OPTION text, it is probably meant to show up as user-visible * text. Having A as an open element while in SELECT is really * sick, don't make anchor text part of the option text in that * case since the option text will probably just be discarded. - * kw */ if (me->sp[0].tag_number == HTML_A) break; HTChunkPutc(&me->option, uc); return; } break; } /* end first switch */ /* * Handle all other tag content. - FM */ switch (me->sp[0].tag_number) { case HTML_PRE: /* Formatted text */ /* * We guarantee that the style is up-to-date in begin_litteral. But we * still want to strip \r's. */ if (c != '\r' && !(c == '\n' && me->inLABEL && !me->inP) && !(c == '\n' && !me->inPRE)) { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, c); } me->inPRE = TRUE; break; case HTML_LISTING: /* Literal text */ case HTML_XMP: case HTML_PLAINTEXT: /* * We guarantee that the style is up-to-date in begin_litteral. But we * still want to strip \r's. */ if (c != '\r') { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, c); } break; default: /* * Free format text. */ if (me->sp->style->id == ST_Preformatted) { if (c != '\r' && !(c == '\n' && me->inLABEL && !me->inP) && !(c == '\n' && !me->inPRE)) { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, c); } me->inPRE = TRUE; } else if (me->sp->style->id == ST_Listing || me->sp->style->id == ST_Example) { if (c != '\r') { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, c); } } else { if (me->style_change) { if ((c == '\n') || (c == ' ')) return; /* Ignore it */ UPDATE_STYLE; } if (c == '\n') { if (!FIX_JAPANESE_SPACES) { if (me->in_word) { if (HText_getLastChar(me->text) != ' ') { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, ' '); } me->in_word = NO; } } } else if (c == ' ' || c == '\t') { if (HText_getLastChar(me->text) != ' ') { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, ' '); } } else if (c == '\r') { /* ignore */ } else { me->inP = TRUE; me->inLABEL = FALSE; HText_appendCharacter(me->text, c); me->in_word = YES; } } } /* end second switch */ if (c == '\n' || c == '\t') { HText_setLastChar(me->text, ' '); /* set it to a generic separator */ } else { HText_setLastChar(me->text, c); } } /* String handling * --------------- * * This is written separately from put_character because the loop can * in some cases be promoted to a higher function call level for speed. */ void HTML_put_string(HTStructured * me, const char *s) { #ifdef USE_PRETTYSRC char *translated_string = NULL; #endif if (s == NULL || (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT)) return; #ifdef USE_PRETTYSRC if (psrc_convert_string) { StrAllocCopy(translated_string, s); TRANSLATE_AND_UNESCAPE_ENTITIES(&translated_string, TRUE, FALSE); s = (const char *) translated_string; } #endif switch (me->sp[0].tag_number) { case HTML_COMMENT: break; /* Do Nothing */ case HTML_TITLE: HTChunkPuts(&me->title, s); break; case HTML_STYLE: HTChunkPuts(&me->style_block, s); break; case HTML_SCRIPT: HTChunkPuts(&me->script, s); break; case HTML_PRE: /* Formatted text */ case HTML_LISTING: /* Literal text */ case HTML_XMP: case HTML_PLAINTEXT: /* * We guarantee that the style is up-to-date in begin_litteral */ HText_appendText(me->text, s); break; case HTML_OBJECT: HTChunkPuts(&me->object, s); break; case HTML_TEXTAREA: HTChunkPuts(&me->textarea, s); break; case HTML_SELECT: case HTML_OPTION: HTChunkPuts(&me->option, s); break; case HTML_MATH: HTChunkPuts(&me->math, s); break; default: /* Free format text? */ if (!me->sp->style->freeFormat) { /* * If we are within a preformatted text style not caught by the * cases above (HTML_PRE or similar may not be the last element * pushed on the style stack). - kw */ #ifdef USE_PRETTYSRC if (psrc_view) { /* * We do this so that a raw '\r' in the string will not be * interpreted as an internal request to break a line - passing * '\r' to HText_appendText is treated by it as a request to * insert a blank line - VH */ for (; *s; ++s) HTML_put_character(me, *s); } else #endif HText_appendText(me->text, s); break; } else { const char *p = s; char c; if (me->style_change) { for (; *p && ((*p == '\n') || (*p == '\r') || (*p == ' ') || (*p == '\t')); p++) ; /* Ignore leaders */ if (!*p) break; UPDATE_STYLE; } for (; *p; p++) { if (*p == 13 && p[1] != 10) { /* * Treat any '\r' which is not followed by '\n' as '\n', to * account for macintosh lineend in ALT attributes etc. - * kw */ c = '\n'; } else { c = *p; } if (me->style_change) { if ((c == '\n') || (c == ' ') || (c == '\t')) continue; /* Ignore it */ UPDATE_STYLE; } if (c == '\n') { if (!FIX_JAPANESE_SPACES) { if (me->in_word) { if (HText_getLastChar(me->text) != ' ') HText_appendCharacter(me->text, ' '); me->in_word = NO; } } } else if (c == ' ' || c == '\t') { if (HText_getLastChar(me->text) != ' ') HText_appendCharacter(me->text, ' '); } else if (c == '\r') { /* ignore */ } else { HText_appendCharacter(me->text, c); me->in_word = YES; } /* set the Last Character */ if (c == '\n' || c == '\t') { /* set it to a generic separator */ HText_setLastChar(me->text, ' '); } else if (c == '\r' && HText_getLastChar(me->text) == ' ') { /* * \r's are ignored. In order to keep collapsing spaces * correctly, we must default back to the previous * separator, if there was one. So we set LastChar to a * generic separator. */ HText_setLastChar(me->text, ' '); } else { HText_setLastChar(me->text, c); } } /* for */ } } /* end switch */ #ifdef USE_PRETTYSRC if (psrc_convert_string) { psrc_convert_string = FALSE; FREE(translated_string); } #endif } /* Buffer write * ------------ */ void HTML_write(HTStructured * me, const char *s, int l) { const char *p; const char *e = s + l; if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT) return; for (p = s; p < e; p++) HTML_put_character(me, *p); } /* * "Internal links" are hyperlinks whose source and destination are * within the same document, and for which the destination is given * as a URL Reference with an empty URL, but possibly with a non-empty * #fragment. (This terminology re URL-Reference vs. URL follows the * Fielding URL syntax and semantics drafts). * Differences: * (1) The document's base (in whatever way it is given) is not used for * resolving internal link references. * (2) Activating an internal link should not result in a new retrieval * of a copy of the document. * (3) Internal links are the only way to refer with a hyperlink to a document * (or a location in it) which is only known as the result of a POST * request (doesn't have a URL from which the document can be retrieved * with GET), and can only be used from within that document. * * *If track_internal_links is true, we keep track of whether a * link destination was given as an internal link. This information is * recorded in the type of the link between anchor objects, and is available * to the HText object and the mainloop from there. URL References to * internal destinations are still resolved into an absolute form before * being passed on, but using the current stream's retrieval address instead * of the base URL. * Examples: (replace [...] to have a valid absolute URL) * In document retrieved from [...]/mypath/mydoc.htm w/ base [...]/otherpath/ * a. HREF="[...]/mypath/mydoc.htm" -> [...]/mypath/mydoc.htm * b. HREF="[...]/mypath/mydoc.htm#frag" -> [...]/mypath/mydoc.htm#frag * c. HREF="mydoc.htm" -> [...]/otherpath/mydoc.htm * d. HREF="mydoc.htm#frag" -> [...]/otherpath/mydoc.htm#frag * e. HREF="" -> [...]/mypath/mydoc.htm (marked internal) * f. HREF="#frag" -> [...]/mypath/mydoc.htm#frag (marked internal) * * *If track_internal_links is false, URL-less URL-References are * resolved differently from URL-References with a non-empty URL (using the * current stream's retrieval address instead of the base), but we make no * further distinction. Resolution is then as in the examples above, execept * that there is no "(marked internal)". * * *Note that this doesn't apply to form ACTIONs (always resolved using base, * never marked internal). Also other references encountered or generated * are not marked internal, whether they have a URL or not, if in a given * context an internal link makes no sense (e.g., IMG SRC=). */ /* A flag is used to keep track of whether an "URL reference" encountered had a real "URL" or not. In the latter case, it will be marked as "internal". The flag is set before we start messing around with the string (resolution of relative URLs etc.). This variable only used locally here, don't confuse with LYinternal_flag which is for overriding non-caching similar to LYoverride_no_cache. - kw */ #define CHECK_FOR_INTERN(flag,s) \ flag = (BOOLEAN) (((s) && (*(s)=='#' || *(s)=='\0')) ? TRUE : FALSE) /* Last argument to pass to HTAnchor_findChildAndLink() calls, just an abbreviation. - kw */ #define INTERN_CHK(flag) (HTLinkType *)((flag) ? HTInternalLink : NULL) #define INTERN_LT INTERN_CHK(intern_flag) #ifdef USE_COLOR_STYLE static char *Style_className = 0; static char *Style_className_end = 0; static size_t Style_className_len = 0; static int hcode; #ifdef LY_FIND_LEAKS static void free_Style_className(void) { FREE(Style_className); } #endif static void addClassName(const char *prefix, const char *actual, size_t length) { size_t offset = strlen(prefix); size_t have = (unsigned) (Style_className_end - Style_className); size_t need = (offset + length + 1); if ((have + need) >= Style_className_len) { Style_className_len += 1024 + 2 * (have + need); if (Style_className == 0) { Style_className = typeMallocn(char, Style_className_len); } else { Style_className = typeRealloc(char, Style_className, Style_className_len); } if (Style_className == NULL) outofmem(__FILE__, "addClassName"); assert(Style_className != NULL); Style_className_end = Style_className + have; } if (offset) strcpy(Style_className_end, prefix); if (length) memcpy(Style_className_end + offset, actual, length); Style_className_end[offset + length] = '\0'; strtolower(Style_className_end); Style_className_end += (offset + length); } #else #define addClassName(prefix, actual, length) /* nothing */ #endif #ifdef USE_PRETTYSRC static void HTMLSRC_apply_markup(HTStructured * context, HTlexeme lexeme, int start, int tag_charset) { HT_tagspec *ts = *((start ? lexeme_start : lexeme_end) + lexeme); while (ts) { #ifdef USE_COLOR_STYLE if (ts->start) { current_tag_style = ts->style; force_current_tag_style = TRUE; forced_classname = ts->class_name; force_classname = TRUE; } #endif CTRACE((tfp, ts->start ? "SRCSTART %d\n" : "SRCSTOP %d\n", (int) lexeme)); if (ts->start) HTML_start_element(context, (int) ts->element, ts->present, (STRING2PTR) ts->value, tag_charset, NULL); else HTML_end_element(context, (int) ts->element, NULL); ts = ts->next; } } # define START TRUE # define STOP FALSE # define PSRCSTART(x) HTMLSRC_apply_markup(me,HTL_##x,START,tag_charset) # define PSRCSTOP(x) HTMLSRC_apply_markup(me,HTL_##x,STOP,tag_charset) # define PUTC(x) HTML_put_character(me,x) # define PUTS(x) HTML_put_string(me,x) #endif /* USE_PRETTYSRC */ static void LYStartArea(HTStructured * obj, const char *href, const char *alt, const char *title, int tag_charset) { BOOL new_present[HTML_AREA_ATTRIBUTES]; const char *new_value[HTML_AREA_ATTRIBUTES]; int i; for (i = 0; i < HTML_AREA_ATTRIBUTES; i++) new_present[i] = NO; if (alt) { new_present[HTML_AREA_ALT] = YES; new_value[HTML_AREA_ALT] = (const char *) alt; } if (non_empty(title)) { new_present[HTML_AREA_TITLE] = YES; new_value[HTML_AREA_TITLE] = (const char *) title; } if (href) { new_present[HTML_AREA_HREF] = YES; new_value[HTML_AREA_HREF] = (const char *) href; } (*obj->isa->start_element) (obj, HTML_AREA, new_present, new_value, tag_charset, 0); } static void LYHandleFIG(HTStructured * me, const BOOL *present, STRING2PTR value, int isobject, int imagemap, const char *id, const char *src, int convert, int start, BOOL *intern_flag GCC_UNUSED) { if (start == TRUE) { me->inFIG = TRUE; if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, NULL); } if (!isobject) { LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); me->inFIGwithP = TRUE; } else { me->inFIGwithP = FALSE; HTML_put_character(me, ' '); /* space char may be ignored */ } if (non_empty(id)) { if (present && convert) { CHECK_ID(HTML_FIG_ID); } else LYHandleID(me, id); } me->in_word = NO; me->inP = FALSE; if (clickable_images && non_empty(src)) { char *href = NULL; StrAllocCopy(href, src); CHECK_FOR_INTERN(*intern_flag, href); LYLegitimizeHREF(me, &href, TRUE, TRUE); if (*href) { me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ INTERN_CHK(*intern_flag)); /* Type */ HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, (isobject ? (imagemap ? "(IMAGE)" : "(OBJECT)") : "[FIGURE]")); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, 0); HTML_put_character(me, '-'); HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } FREE(href); } } else { /* handle end tag */ if (me->inFIGwithP) { LYEnsureDoubleSpace(me); } else { HTML_put_character(me, ' '); /* space char may be ignored */ } LYResetParagraphAlignment(me); me->inFIGwithP = FALSE; me->inFIG = FALSE; change_paragraph_style(me, me->sp->style); /* Often won't really change */ if (me->List_Nesting_Level >= 0) { UPDATE_STYLE; HText_NegateLineOne(me->text); } } } static void clear_objectdata(HTStructured * me) { if (me) { HTChunkClear(&me->object); me->object_started = FALSE; me->object_declare = FALSE; me->object_shapes = FALSE; me->object_ismap = FALSE; FREE(me->object_usemap); FREE(me->object_id); FREE(me->object_title); FREE(me->object_data); FREE(me->object_type); FREE(me->object_classid); FREE(me->object_codebase); FREE(me->object_codetype); FREE(me->object_name); } } #define HTParseALL(pp,pconst) \ { char* free_me = *pp; \ *pp = HTParse(*pp, pconst, PARSE_ALL); \ FREE(free_me); \ } /* Start Element * ------------- */ static int HTML_start_element(HTStructured * me, int element_number, const BOOL *present, STRING2PTR value, int tag_charset, char **include) { char *alt_string = NULL; char *id_string = NULL; char *newtitle = NULL; char **pdoctitle = NULL; char *href = NULL; char *map_href = NULL; char *title = NULL; char *I_value = NULL; char *I_name = NULL; char *temp = NULL; const char *Base = NULL; int dest_char_set = -1; HTParentAnchor *dest = NULL; /* An anchor's destination */ BOOL dest_ismap = FALSE; /* Is dest an image map script? */ HTChildAnchor *ID_A = NULL; /* HTML_foo_ID anchor */ int url_type = 0, i = 0; char *cp = NULL; HTMLElement ElementNumber = (HTMLElement) element_number; BOOL intern_flag = FALSE; short stbl_align = HT_ALIGN_NONE; int status = HT_OK; #ifdef USE_COLOR_STYLE char *class_name; int class_used = 0; #endif #ifdef USE_PRETTYSRC if (psrc_view && !sgml_in_psrc_was_initialized) { if (!psrc_nested_call) { HTTag *tag = &HTML_dtd.tags[element_number]; char buf[200]; const char *p; if (psrc_first_tag) { psrc_first_tag = FALSE; /* perform the special actions on the begining of the document. It's assumed that all lynx modules start generating html from tag (ie not a text) so we are able to trap this moment and initialize. */ psrc_nested_call = TRUE; HTML_start_element(me, HTML_BODY, NULL, NULL, tag_charset, NULL); HTML_start_element(me, HTML_PRE, NULL, NULL, tag_charset, NULL); PSRCSTART(entire); psrc_nested_call = FALSE; } psrc_nested_call = TRUE; /*write markup for tags and exit */ PSRCSTART(abracket); PUTC('<'); PSRCSTOP(abracket); PSRCSTART(tag); if (tagname_transform != 0) PUTS(tag->name); else { LYStrNCpy(buf, tag->name, sizeof(buf) - 1); LYLowerCase(buf); PUTS(buf); } if (present) { for (i = 0; i < tag->number_of_attributes; i++) if (present[i]) { PUTC(' '); PSRCSTART(attrib); if (attrname_transform != 0) PUTS(tag->attributes[i].name); else { LYStrNCpy(buf, tag->attributes[i].name, sizeof(buf) - 1); LYLowerCase(buf); PUTS(buf); } if (value[i]) { char q = '"'; /*0 in dquotes, 1 - in quotes, 2 mixed */ char kind = (char) (!StrChr(value[i], '"') ? 0 : !StrChr(value[i], '\'') ? q = '\'', 1 : 2); PUTC('='); PSRCSTOP(attrib); PSRCSTART(attrval); PUTC(q); /*is it special ? */ if (tag->attributes[i].type == HTMLA_ANAME) { HTStartAnchor(me, value[i], NULL); HTML_end_element(me, HTML_A, NULL); } else if (tag->attributes[i].type == HTMLA_HREF) { PSRCSTART(href); HTStartAnchor(me, NULL, value[i]); } if (kind != 2) PUTS(value[i]); else for (p = value[i]; *p; p++) if (*p != '"') PUTC(*p); else PUTS("""); /*is it special ? */ if (tag->attributes[i].type == HTMLA_HREF) { HTML_end_element(me, HTML_A, NULL); PSRCSTOP(href); } PUTC(q); PSRCSTOP(attrval); } /* if value */ } /* if present[i] */ } /* if present */ PSRCSTOP(tag); PSRCSTART(abracket); PUTC('>'); PSRCSTOP(abracket); psrc_nested_call = FALSE; return HT_OK; } /*if (!psrc_nested_call) */ /*fall through */ } #endif /* USE_PRETTYSRC */ if (LYMapsOnly) { if (!(ElementNumber == HTML_MAP || ElementNumber == HTML_AREA || ElementNumber == HTML_BASE || ElementNumber == HTML_OBJECT || ElementNumber == HTML_A)) { return HT_OK; } } else if (!me->text) { UPDATE_STYLE; } { /* me->tag_charset is charset for attribute values. */ int j = ((tag_charset < 0) ? me->UCLYhndl : tag_charset); if ((me->tag_charset != j) || (j < 0 /* for trace entry */ )) { CTRACE((tfp, "me->tag_charset: %d -> %d", me->tag_charset, j)); CTRACE((tfp, " (me->UCLYhndl: %d, tag_charset: %d)\n", me->UCLYhndl, tag_charset)); me->tag_charset = j; } } /* this should be done differently */ #if defined(USE_COLOR_STYLE) addClassName(";", HTML_dtd.tags[element_number].name, (size_t) HTML_dtd.tags[element_number].name_len); class_name = (force_classname ? forced_classname : class_string); force_classname = FALSE; if (force_current_tag_style == FALSE) { current_tag_style = (class_name[0] ? -1 : cached_tag_styles[element_number]); } else { force_current_tag_style = FALSE; } CTRACE2(TRACE_STYLE, (tfp, "CSS.elt:<%s>\n", HTML_dtd.tags[element_number].name)); if (current_tag_style == -1) { /* Append class_name */ hcode = hash_code_lowercase_on_fly(HTML_dtd.tags[element_number].name); if (class_name[0]) { int ohcode = hcode; hcode = hash_code_aggregate_char('.', hcode); hcode = hash_code_aggregate_lower_str(class_name, hcode); if (!hashStyles[hcode].name) { /* None such -> classless version */ hcode = ohcode; CTRACE2(TRACE_STYLE, (tfp, "STYLE.start_element: <%s> (class <%s> not configured), hcode=%d.\n", HTML_dtd.tags[element_number].name, class_name, hcode)); } else { addClassName(".", class_name, strlen(class_name)); CTRACE2(TRACE_STYLE, (tfp, "STYLE.start_element: <%s>.<%s>, hcode=%d.\n", HTML_dtd.tags[element_number].name, class_name, hcode)); class_used = 1; } } class_string[0] = '\0'; } else { /* (current_tag_style!=-1) */ if (class_name[0]) { addClassName(".", class_name, strlen(class_name)); class_string[0] = '\0'; } hcode = current_tag_style; CTRACE2(TRACE_STYLE, (tfp, "STYLE.start_element: <%s>, hcode=%d.\n", HTML_dtd.tags[element_number].name, hcode)); current_tag_style = -1; } #if !OMIT_SCN_KEEPING /* Can be done in other cases too... */ if (!class_used && ElementNumber == HTML_INPUT) { /* For some other too? */ const char *type = ""; int ohcode = hcode; if (present && present[HTML_INPUT_TYPE] && value[HTML_INPUT_TYPE]) type = value[HTML_INPUT_TYPE]; hcode = hash_code_aggregate_lower_str(".type.", hcode); hcode = hash_code_aggregate_lower_str(type, hcode); if (!hashStyles[hcode].name) { /* None such -> classless version */ hcode = ohcode; CTRACE2(TRACE_STYLE, (tfp, "STYLE.start_element: type <%s> not configured.\n", type)); } else { addClassName(".type.", type, strlen(type)); CTRACE2(TRACE_STYLE, (tfp, "STYLE.start_element: <%s>.type.<%s>, hcode=%d.\n", HTML_dtd.tags[element_number].name, type, hcode)); } } #endif /* !OMIT_SCN_KEEPING */ HText_characterStyle(me->text, hcode, STACK_ON); #endif /* USE_COLOR_STYLE */ /* * Handle the start tag. - FM */ switch (ElementNumber) { case HTML_HTML: break; case HTML_HEAD: break; case HTML_BASE: if (present && present[HTML_BASE_HREF] && !local_host_only && non_empty(value[HTML_BASE_HREF])) { char *base = NULL; const char *related = NULL; StrAllocCopy(base, value[HTML_BASE_HREF]); CTRACE((tfp, "*HTML_BASE: initial href=`%s'\n", NonNull(base))); if (!(url_type = LYLegitimizeHREF(me, &base, TRUE, TRUE))) { CTRACE((tfp, "HTML: BASE '%s' is not an absolute URL.\n", NonNull(base))); if (me->inBadBASE == FALSE) HTAlert(BASE_NOT_ABSOLUTE); me->inBadBASE = TRUE; } if (url_type == LYNXIMGMAP_URL_TYPE) { /* * These have a non-standard form, basically strip the prefix * or the code below would insert a nonsense host into the * pseudo URL. These should never occur where they would be * used for resolution of relative URLs anyway. We can also * strip the #map part. - kw */ temp = base; base = HTParse(base + 11, "", PARSE_ALL_WITHOUT_ANCHOR); FREE(temp); } /* * Get parent's address for defaulted fields. */ related = me->node_anchor->address; /* * Create the access field. */ temp = HTParse(base, related, PARSE_ACCESS + PARSE_PUNCTUATION); StrAllocCopy(me->base_href, temp); FREE(temp); /* * Create the host[:port] field. */ temp = HTParse(base, "", PARSE_HOST + PARSE_PUNCTUATION); if (!StrNCmp(temp, "//", 2)) { StrAllocCat(me->base_href, temp); if (!strcmp(me->base_href, "file://")) { StrAllocCat(me->base_href, "localhost"); } } else { if (isFILE_URL(me->base_href)) { StrAllocCat(me->base_href, "//localhost"); } else if (strcmp(me->base_href, STR_NEWS_URL)) { FREE(temp); StrAllocCat(me->base_href, (temp = HTParse(related, "", PARSE_HOST + PARSE_PUNCTUATION))); } } FREE(temp); /* * Create the path field. */ temp = HTParse(base, "", PARSE_PATH + PARSE_PUNCTUATION); if (*temp != '\0') { char *p = StrChr(temp, '?'); if (p) *p = '\0'; p = strrchr(temp, '/'); if (p) *(p + 1) = '\0'; /* strip after the last slash */ StrAllocCat(me->base_href, temp); } else if (!strcmp(me->base_href, STR_NEWS_URL)) { StrAllocCat(me->base_href, "*"); } else if (isNEWS_URL(me->base_href) || isNNTP_URL(me->base_href) || isSNEWS_URL(me->base_href)) { StrAllocCat(me->base_href, "/*"); } else { StrAllocCat(me->base_href, "/"); } FREE(temp); FREE(base); me->inBASE = TRUE; me->node_anchor->inBASE = TRUE; StrAllocCopy(me->node_anchor->content_base, me->base_href); /* me->base_href is a valid URL */ CTRACE((tfp, "*HTML_BASE: final href=`%s'\n", me->base_href)); } break; case HTML_META: if (present) LYHandleMETA(me, present, value, include); break; case HTML_TITLE: HTChunkClear(&me->title); break; case HTML_LINK: intern_flag = FALSE; if (present && present[HTML_LINK_HREF]) { CHECK_FOR_INTERN(intern_flag, value[HTML_LINK_HREF]); /* * Prepare to do housekeeping on the reference. - FM */ if (isEmpty(value[HTML_LINK_HREF])) { Base = (me->inBASE) ? me->base_href : me->node_anchor->address; StrAllocCopy(href, Base); } else { StrAllocCopy(href, value[HTML_LINK_HREF]); (void) LYLegitimizeHREF(me, &href, TRUE, TRUE); Base = (me->inBASE && *href != '\0' && *href != '#') ? me->base_href : me->node_anchor->address; HTParseALL(&href, Base); } /* * Handle links with a REV attribute. - FM * Handle REV="made" or REV="owner". - LM & FM * Handle REL="author" -TD */ if (present && ((present[HTML_LINK_REV] && value[HTML_LINK_REV] && (!strcasecomp("made", value[HTML_LINK_REV]) || !strcasecomp("owner", value[HTML_LINK_REV]))) || (present[HTML_LINK_REL] && value[HTML_LINK_REL] && (!strcasecomp("author", value[HTML_LINK_REL]))))) { /* * Load the owner element. - FM */ HTAnchor_setOwner(me->node_anchor, href); CTRACE((tfp, "HTML: DOC OWNER '%s' found\n", href)); FREE(href); /* * Load the RevTitle element if a TITLE attribute and value * are present. - FM */ if (present && present[HTML_LINK_TITLE] && value[HTML_LINK_TITLE] && *value[HTML_LINK_TITLE] != '\0') { StrAllocCopy(title, value[HTML_LINK_TITLE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE); LYTrimHead(title); LYTrimTail(title); if (*title != '\0') HTAnchor_setRevTitle(me->node_anchor, title); FREE(title); } break; } /* * Handle REL links. - FM */ if (present && present[HTML_LINK_REL] && value[HTML_LINK_REL]) { /* * Ignore style sheets, for now. - FM * * lss and css have different syntax - lynx shouldn't try to * parse them now (it tries to parse them as lss, so it exits * with error message on the 1st non-empty line) - VH */ #ifndef USE_COLOR_STYLE if (!strcasecomp(value[HTML_LINK_REL], "StyleSheet") || !strcasecomp(value[HTML_LINK_REL], "Style")) { CTRACE2(TRACE_STYLE, (tfp, "HTML: StyleSheet link found.\n")); CTRACE2(TRACE_STYLE, (tfp, " StyleSheets not yet implemented.\n")); FREE(href); break; } #endif /* ! USE_COLOR_STYLE */ /* * Ignore anything not registered in the 28-Mar-95 IETF HTML * 3.0 draft and W3C HTML 3.2 draft, or not appropriate for * Lynx banner links in the expired Maloney and Quin relrev * draft. We'll make this more efficient when the situation * stabilizes, and for now, we'll treat "Banner" as another * toolbar element. - FM */ if (!strcasecomp(value[HTML_LINK_REL], "Home") || !strcasecomp(value[HTML_LINK_REL], "ToC") || !strcasecomp(value[HTML_LINK_REL], "Contents") || !strcasecomp(value[HTML_LINK_REL], "Index") || !strcasecomp(value[HTML_LINK_REL], "Glossary") || !strcasecomp(value[HTML_LINK_REL], "Copyright") || !strcasecomp(value[HTML_LINK_REL], "Help") || !strcasecomp(value[HTML_LINK_REL], "Search") || !strcasecomp(value[HTML_LINK_REL], "Bookmark") || !strcasecomp(value[HTML_LINK_REL], "Banner") || !strcasecomp(value[HTML_LINK_REL], "Top") || !strcasecomp(value[HTML_LINK_REL], "Origin") || !strcasecomp(value[HTML_LINK_REL], "Navigator") || !strcasecomp(value[HTML_LINK_REL], "Disclaimer") || !strcasecomp(value[HTML_LINK_REL], "Author") || !strcasecomp(value[HTML_LINK_REL], "Editor") || !strcasecomp(value[HTML_LINK_REL], "Publisher") || !strcasecomp(value[HTML_LINK_REL], "Trademark") || !strcasecomp(value[HTML_LINK_REL], "Hotlist") || !strcasecomp(value[HTML_LINK_REL], "Begin") || !strcasecomp(value[HTML_LINK_REL], "First") || !strcasecomp(value[HTML_LINK_REL], "End") || !strcasecomp(value[HTML_LINK_REL], "Last") || !strcasecomp(value[HTML_LINK_REL], "Documentation") || !strcasecomp(value[HTML_LINK_REL], "Biblioentry") || !strcasecomp(value[HTML_LINK_REL], "Bibliography") || !strcasecomp(value[HTML_LINK_REL], "Start") || !strcasecomp(value[HTML_LINK_REL], "Appendix")) { StrAllocCopy(title, value[HTML_LINK_REL]); pdoctitle = &title; /* for setting HTAnchor's title */ } else if (!strcasecomp(value[HTML_LINK_REL], "Up") || !strcasecomp(value[HTML_LINK_REL], "Next") || !strcasecomp(value[HTML_LINK_REL], "Previous") || !strcasecomp(value[HTML_LINK_REL], "Prev") || !strcasecomp(value[HTML_LINK_REL], "Child") || !strcasecomp(value[HTML_LINK_REL], "Sibling") || !strcasecomp(value[HTML_LINK_REL], "Parent") || !strcasecomp(value[HTML_LINK_REL], "Meta") || !strcasecomp(value[HTML_LINK_REL], "URC") || !strcasecomp(value[HTML_LINK_REL], "Pointer") || !strcasecomp(value[HTML_LINK_REL], "Translation") || !strcasecomp(value[HTML_LINK_REL], "Definition") || !strcasecomp(value[HTML_LINK_REL], "Alternate") || !strcasecomp(value[HTML_LINK_REL], "Section") || !strcasecomp(value[HTML_LINK_REL], "Subsection") || !strcasecomp(value[HTML_LINK_REL], "Chapter")) { StrAllocCopy(title, value[HTML_LINK_REL]); /* not setting target HTAnchor's title, for these links of highly relative character. Instead, try to remember the REL attribute as a property of the link (but not the destination), in the (otherwise underused) link type in a special format; the LIST page generation code may later use it. - kw */ if (!intern_flag) { StrAllocCopy(temp, "RelTitle: "); StrAllocCat(temp, value[HTML_LINK_REL]); } #ifndef DISABLE_BIBP } else if (!strcasecomp(value[HTML_LINK_REL], "citehost")) { /* Citehost determination for bibp links. - RDC */ HTAnchor_setCitehost(me->node_anchor, href); CTRACE((tfp, "HTML: citehost '%s' found\n", href)); FREE(href); break; #endif } else { CTRACE((tfp, "HTML: LINK with REL=\"%s\" ignored.\n", value[HTML_LINK_REL])); FREE(href); break; } } } else if (present && present[HTML_LINK_REL] && value[HTML_LINK_REL]) { /* * If no HREF was specified, handle special REL links with * self-designated HREFs. - FM */ if (!strcasecomp(value[HTML_LINK_REL], "Home")) { StrAllocCopy(href, LynxHome); } else if (!strcasecomp(value[HTML_LINK_REL], "Help")) { StrAllocCopy(href, helpfile); } else if (!strcasecomp(value[HTML_LINK_REL], "Index")) { StrAllocCopy(href, indexfile); } else { CTRACE((tfp, "HTML: LINK with REL=\"%s\" and no HREF ignored.\n", value[HTML_LINK_REL])); break; } StrAllocCopy(title, value[HTML_LINK_REL]); pdoctitle = &title; } if (href) { /* * Create a title (link name) from the TITLE value, if present, or * default to the REL value that was loaded into title. - FM */ if (present && present[HTML_LINK_TITLE] && non_empty(value[HTML_LINK_TITLE])) { StrAllocCopy(title, value[HTML_LINK_TITLE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE); LYTrimHead(title); LYTrimTail(title); pdoctitle = &title; FREE(temp); /* forget about recording RelTitle - kw */ } if (isEmpty(title)) { FREE(href); FREE(title); break; } if (me->inA) { /* * Ugh! The LINK tag, which is a HEAD element, is in an * Anchor, which is BODY element. All we can do is close the * Anchor and cross our fingers. - FM */ SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } /* * Create anchors for the links that simulate a toolbar. - FM */ me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (temp ? (HTLinkType *) HTAtom_for(temp) : INTERN_LT)); /* Type */ FREE(temp); if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA) )) != NULL) { if (pdoctitle && !HTAnchor_title(dest)) HTAnchor_setTitle(dest, *pdoctitle); /* Don't allow CHARSET attribute to change *this* document's charset assumption. - kw */ if (dest == me->node_anchor) dest = NULL; if (present[HTML_LINK_CHARSET] && non_empty(value[HTML_LINK_CHARSET])) { dest_char_set = UCGetLYhndl_byMIME(value[HTML_LINK_CHARSET]); if (dest_char_set < 0) dest_char_set = UCLYhndl_for_unrec; } if (dest && dest_char_set >= 0) HTAnchor_setUCInfoStage(dest, dest_char_set, UCT_STAGE_PARSER, UCT_SETBY_LINK); } UPDATE_STYLE; if (!HText_hasToolbar(me->text) && (ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ LYToolbarName, /* Tag */ NULL, /* Addresss */ (HTLinkType *) 0))) { /* Type */ HText_appendCharacter(me->text, '#'); HText_setLastChar(me->text, ' '); /* absorb white space */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); HText_setToolbar(me->text); } else { /* * Add collapsible space to separate link from previous * generated links. - kw */ HTML_put_character(me, ' '); } HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); #ifdef USE_COLOR_STYLE if (present && present[HTML_LINK_CLASS] && non_empty(value[HTML_LINK_CLASS])) { char *tmp = 0; HTSprintf0(&tmp, "link.%s.%s", value[HTML_LINK_CLASS], title); CTRACE2(TRACE_STYLE, (tfp, "STYLE.link: using style <%s>\n", tmp)); HText_characterStyle(me->text, hash_code(tmp), STACK_ON); HTML_put_string(me, title); HTML_put_string(me, " ("); HTML_put_string(me, value[HTML_LINK_CLASS]); HTML_put_string(me, ")"); HText_characterStyle(me->text, hash_code(tmp), STACK_OFF); FREE(tmp); } else #endif HTML_put_string(me, title); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, 0); } FREE(href); FREE(title); break; case HTML_ISINDEX: if (((present)) && ((present[HTML_ISINDEX_HREF] && value[HTML_ISINDEX_HREF]) || (present[HTML_ISINDEX_ACTION] && value[HTML_ISINDEX_ACTION]))) { /* * Lynx was supporting ACTION, which never made it into the HTML * 2.0 specs. HTML 3.0 uses HREF, so we'll use that too, but allow * use of ACTION as an alternate until people have fully switched * over. - FM */ if (present[HTML_ISINDEX_HREF] && value[HTML_ISINDEX_HREF]) StrAllocCopy(href, value[HTML_ISINDEX_HREF]); else StrAllocCopy(href, value[HTML_ISINDEX_ACTION]); LYLegitimizeHREF(me, &href, TRUE, TRUE); Base = (me->inBASE && *href != '\0' && *href != '#') ? me->base_href : me->node_anchor->address; HTParseALL(&href, Base); HTAnchor_setIndex(me->node_anchor, href); FREE(href); } else { Base = (me->inBASE) ? me->base_href : me->node_anchor->address; HTAnchor_setIndex(me->node_anchor, Base); } /* * Support HTML 3.0 PROMPT attribute. - FM */ if (present && present[HTML_ISINDEX_PROMPT] && non_empty(value[HTML_ISINDEX_PROMPT])) { StrAllocCopy(temp, value[HTML_ISINDEX_PROMPT]); TRANSLATE_AND_UNESCAPE_ENTITIES(&temp, TRUE, FALSE); LYTrimHead(temp); LYTrimTail(temp); if (*temp != '\0') { StrAllocCat(temp, " "); HTAnchor_setPrompt(me->node_anchor, temp); } else { HTAnchor_setPrompt(me->node_anchor, ENTER_DATABASE_QUERY); } FREE(temp); } else { HTAnchor_setPrompt(me->node_anchor, ENTER_DATABASE_QUERY); } break; case HTML_NEXTID: break; case HTML_STYLE: /* * We're getting it as Literal text, which, for now, we'll just ignore. * - FM */ HTChunkClear(&me->style_block); break; case HTML_SCRIPT: /* * We're getting it as Literal text, which, for now, we'll just ignore. * - FM */ HTChunkClear(&me->script); break; case HTML_BODY: CHECK_ID(HTML_BODY_ID); if (HText_hasToolbar(me->text)) HText_appendParagraph(me->text); break; case HTML_FRAMESET: break; case HTML_FRAME: if (present && present[HTML_FRAME_NAME] && non_empty(value[HTML_FRAME_NAME])) { StrAllocCopy(id_string, value[HTML_FRAME_NAME]); TRANSLATE_AND_UNESCAPE_ENTITIES(&id_string, TRUE, FALSE); LYTrimHead(id_string); LYTrimTail(id_string); } if (present && present[HTML_FRAME_SRC] && non_empty(value[HTML_FRAME_SRC])) { StrAllocCopy(href, value[HTML_FRAME_SRC]); LYLegitimizeHREF(me, &href, TRUE, TRUE); if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ CAN_JUSTIFY_PUSH(FALSE); LYEnsureSingleSpace(me); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "FRAME:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); me->in_word = NO; CHECK_ID(HTML_FRAME_ID); HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, (id_string ? id_string : href)); FREE(href); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, 0); LYEnsureSingleSpace(me); CAN_JUSTIFY_POP; } else { CHECK_ID(HTML_FRAME_ID); } FREE(id_string); break; case HTML_NOFRAMES: LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); break; case HTML_IFRAME: if (present && present[HTML_IFRAME_NAME] && non_empty(value[HTML_IFRAME_NAME])) { StrAllocCopy(id_string, value[HTML_IFRAME_NAME]); TRANSLATE_AND_UNESCAPE_ENTITIES(&id_string, TRUE, FALSE); LYTrimHead(id_string); LYTrimTail(id_string); } if (present && present[HTML_IFRAME_SRC] && non_empty(value[HTML_IFRAME_SRC])) { StrAllocCopy(href, value[HTML_IFRAME_SRC]); LYLegitimizeHREF(me, &href, TRUE, TRUE); if (me->inA) HTML_end_element(me, HTML_A, include); me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ LYEnsureDoubleSpace(me); CAN_JUSTIFY_PUSH_F LYResetParagraphAlignment(me); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "IFRAME:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); me->in_word = NO; CHECK_ID(HTML_IFRAME_ID); HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, (id_string ? id_string : href)); FREE(href); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, 0); LYEnsureSingleSpace(me); CAN_JUSTIFY_POP; } else { CHECK_ID(HTML_IFRAME_ID); } FREE(id_string); break; case HTML_BANNER: case HTML_MARQUEE: change_paragraph_style(me, styles[HTML_BANNER]); UPDATE_STYLE; if (me->sp->tag_number == (int) ElementNumber) LYEnsureDoubleSpace(me); /* * Treat this as a toolbar if we don't have one yet, and we are in the * first half of the first page. - FM */ if ((!HText_hasToolbar(me->text) && HText_getLines(me->text) < (display_lines / 2)) && (ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ LYToolbarName, /* Tag */ NULL, /* Addresss */ (HTLinkType *) 0))) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); HText_setToolbar(me->text); } CHECK_ID(HTML_GEN_ID); break; case HTML_CENTER: case HTML_DIV: if (me->Division_Level < (MAX_NESTING - 1)) { me->Division_Level++; } else { CTRACE((tfp, "HTML: ****** Maximum nesting of %d divisions exceeded!\n", MAX_NESTING)); } if (me->inP) LYEnsureSingleSpace(me); /* always at least break line - kw */ if (ElementNumber == HTML_CENTER) { me->DivisionAlignments[me->Division_Level] = HT_CENTER; change_paragraph_style(me, styles[HTML_DCENTER]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DCENTER]->alignment; } else if (me->List_Nesting_Level >= 0 && !(present && present[HTML_DIV_ALIGN] && value[HTML_DIV_ALIGN] && (!strcasecomp(value[HTML_DIV_ALIGN], "center") || !strcasecomp(value[HTML_DIV_ALIGN], "right")))) { if (present && present[HTML_DIV_ALIGN]) me->current_default_alignment = HT_LEFT; else if (me->Division_Level == 0) me->current_default_alignment = HT_LEFT; else if (me->sp[0].tag_number == HTML_UL || me->sp[0].tag_number == HTML_OL || me->sp[0].tag_number == HTML_MENU || me->sp[0].tag_number == HTML_DIR || me->sp[0].tag_number == HTML_LI || me->sp[0].tag_number == HTML_LH || me->sp[0].tag_number == HTML_DD) me->current_default_alignment = HT_LEFT; LYHandlePlike(me, present, value, include, HTML_DIV_ALIGN, TRUE); me->DivisionAlignments[me->Division_Level] = (short) me->current_default_alignment; } else if (present && present[HTML_DIV_ALIGN] && non_empty(value[HTML_DIV_ALIGN])) { if (!strcasecomp(value[HTML_DIV_ALIGN], "center")) { me->DivisionAlignments[me->Division_Level] = HT_CENTER; change_paragraph_style(me, styles[HTML_DCENTER]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DCENTER]->alignment; } else if (!strcasecomp(value[HTML_DIV_ALIGN], "right")) { me->DivisionAlignments[me->Division_Level] = HT_RIGHT; change_paragraph_style(me, styles[HTML_DRIGHT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DRIGHT]->alignment; } else { me->DivisionAlignments[me->Division_Level] = HT_LEFT; change_paragraph_style(me, styles[HTML_DLEFT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DLEFT]->alignment; } } else { me->DivisionAlignments[me->Division_Level] = HT_LEFT; change_paragraph_style(me, styles[HTML_DLEFT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DLEFT]->alignment; } CHECK_ID(HTML_DIV_ID); break; case HTML_H1: case HTML_H2: case HTML_H3: case HTML_H4: case HTML_H5: case HTML_H6: /* * Close the previous style if not done by HTML doc. Added to get rid * of core dumps in BAD HTML on the net. * GAB 07-07-94 * But then again, these are actually allowed to nest. I guess I have * to depend on the HTML writers correct style. * GAB 07-12-94 if (i_prior_style != -1) { HTML_end_element(me, i_prior_style); } i_prior_style = ElementNumber; */ /* * Check whether we have an H# in a list, and if so, treat it as an LH. * - FM */ if ((me->List_Nesting_Level >= 0) && (me->sp[0].tag_number == HTML_UL || me->sp[0].tag_number == HTML_OL || me->sp[0].tag_number == HTML_MENU || me->sp[0].tag_number == HTML_DIR || me->sp[0].tag_number == HTML_LI)) { if (HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY) { ElementNumber = HTML_LH; } else { me->new_style = me->sp[0].style; ElementNumber = (HTMLElement) me->sp[0].tag_number; UPDATE_STYLE; } /* * Some authors use H# headers as a substitute for FONT, so check * if this one immediately followed an LI. If so, both me->inP and * me->in_word will be FALSE (though the line might not be empty * due to a bullet and/or nbsp) and we can assume it is just for a * FONT change. We thus will not create another line break nor add * to the current left indentation. - FM */ if (!(me->inP == FALSE && me->in_word == NO)) { HText_appendParagraph(me->text); HTML_put_character(me, HT_NON_BREAK_SPACE); HText_setLastChar(me->text, ' '); me->in_word = NO; me->inP = FALSE; } CHECK_ID(HTML_H_ID); break; } if (present && present[HTML_H_ALIGN] && non_empty(value[HTML_H_ALIGN])) { if (!strcasecomp(value[HTML_H_ALIGN], "center")) change_paragraph_style(me, styles[HTML_HCENTER]); else if (!strcasecomp(value[HTML_H_ALIGN], "right")) change_paragraph_style(me, styles[HTML_HRIGHT]); else if (!strcasecomp(value[HTML_H_ALIGN], "left") || !strcasecomp(value[HTML_H_ALIGN], "justify")) change_paragraph_style(me, styles[HTML_HLEFT]); else change_paragraph_style(me, styles[ElementNumber]); } else if (me->Division_Level >= 0) { if (me->DivisionAlignments[me->Division_Level] == HT_CENTER) { change_paragraph_style(me, styles[HTML_HCENTER]); } else if (me->DivisionAlignments[me->Division_Level] == HT_LEFT) { change_paragraph_style(me, styles[HTML_HLEFT]); } else if (me->DivisionAlignments[me->Division_Level] == HT_RIGHT) { change_paragraph_style(me, styles[HTML_HRIGHT]); } } else { change_paragraph_style(me, styles[ElementNumber]); } UPDATE_STYLE; CHECK_ID(HTML_H_ID); if ((bold_headers == TRUE || (ElementNumber == HTML_H1 && bold_H1 == TRUE)) && (styles[ElementNumber]->font & HT_BOLD)) { if (me->inBoldA == FALSE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_START_CHAR); } me->inBoldH = TRUE; } break; case HTML_P: LYHandlePlike(me, present, value, include, HTML_P_ALIGN, TRUE); CHECK_ID(HTML_P_ID); break; case HTML_BR: UPDATE_STYLE; CHECK_ID(HTML_GEN_ID); /* Add a \r (new line) if these three conditions are true: * 1. We are not collapsing BR's, and * 2. The previous line has text on it, or * 3. This line has text on it. * Otherwise, don't do anything. -DH 980814, TD 980827 */ if ((LYCollapseBRs == FALSE && !HText_PreviousLineEmpty(me->text, FALSE)) || !HText_LastLineEmpty(me->text, FALSE)) { HText_setLastChar(me->text, ' '); /* absorb white space */ HText_appendCharacter(me->text, '\r'); } me->in_word = NO; me->inP = FALSE; break; case HTML_WBR: UPDATE_STYLE; CHECK_ID(HTML_GEN_ID); HText_setBreakPoint(me->text); break; case HTML_HY: case HTML_SHY: UPDATE_STYLE; CHECK_ID(HTML_GEN_ID); HText_appendCharacter(me->text, LY_SOFT_HYPHEN); break; case HTML_HR: { int width; /* * Start a new line only if we had printable characters following * the previous newline, or remove the previous line if both it and * the last line are blank. - FM */ UPDATE_STYLE; if (!HText_LastLineEmpty(me->text, FALSE)) { HText_setLastChar(me->text, ' '); /* absorb white space */ HText_appendCharacter(me->text, '\r'); } else if (HText_PreviousLineEmpty(me->text, FALSE)) { HText_RemovePreviousLine(me->text); } me->in_word = NO; me->inP = FALSE; /* * Add an ID link if needed. - FM */ CHECK_ID(HTML_HR_ID); /* * Center lines within the current margins, if a right or left * ALIGNment is not specified. If WIDTH="#%" is given and not * garbage, use that to calculate the width, otherwise use the * default width. - FM */ if (present && present[HTML_HR_ALIGN] && value[HTML_HR_ALIGN]) { if (!strcasecomp(value[HTML_HR_ALIGN], "right")) { me->sp->style->alignment = HT_RIGHT; } else if (!strcasecomp(value[HTML_HR_ALIGN], "left")) { me->sp->style->alignment = HT_LEFT; } else { me->sp->style->alignment = HT_CENTER; } } else { me->sp->style->alignment = HT_CENTER; } width = LYcolLimit - me->new_style->leftIndent - me->new_style->rightIndent; if (present && present[HTML_HR_WIDTH] && value[HTML_HR_WIDTH] && isdigit(UCH(*value[HTML_HR_WIDTH])) && value[HTML_HR_WIDTH][strlen(value[HTML_HR_WIDTH]) - 1] == '%') { char *percent = NULL; int Percent, Width; StrAllocCopy(percent, value[HTML_HR_WIDTH]); percent[strlen(percent) - 1] = '\0'; Percent = atoi(percent); if (Percent > 100 || Percent < 1) width -= 5; else { Width = (width * Percent) / 100; if (Width < 1) width = 1; else width = Width; } FREE(percent); } else { width -= 5; } for (i = 0; i < width; i++) HTML_put_character(me, '_'); HText_appendCharacter(me->text, '\r'); me->in_word = NO; me->inP = FALSE; /* * Reset the alignment appropriately for the division and/or block. * - FM */ if (me->List_Nesting_Level < 0 && me->Division_Level >= 0) { me->sp->style->alignment = me->DivisionAlignments[me->Division_Level]; } else if (me->sp->style->id == ST_HeadingCenter || me->sp->style->id == ST_Heading1) { me->sp->style->alignment = HT_CENTER; } else if (me->sp->style->id == ST_HeadingRight) { me->sp->style->alignment = HT_RIGHT; } else { me->sp->style->alignment = HT_LEFT; } /* * Add a blank line and set the second line indentation for lists * and addresses, or a paragraph separator for other blocks. - FM */ if (me->List_Nesting_Level >= 0 || me->sp[0].tag_number == HTML_ADDRESS) { HText_setLastChar(me->text, ' '); /* absorb white space */ HText_appendCharacter(me->text, '\r'); } else { HText_appendParagraph(me->text); } } break; case HTML_TAB: if (!present) { /* Bad tag. Must have at least one attribute. - FM */ CTRACE((tfp, "HTML: TAB tag has no attributes. Ignored.\n")); break; } /* * If page author is using TAB within a TABLE, it's probably formatted * specifically to work well for Lynx without simple table tracking * code. Cancel tracking, it would only make things worse. - kw */ HText_cancelStbl(me->text); UPDATE_STYLE; CANT_JUSTIFY_THIS_LINE; if (present[HTML_TAB_ALIGN] && value[HTML_TAB_ALIGN] && (strcasecomp(value[HTML_TAB_ALIGN], "left") || !(present[HTML_TAB_TO] || present[HTML_TAB_INDENT]))) { /* * Just ensure a collapsible space, until we have the ALIGN and DP * attributes implemented. - FM */ HTML_put_character(me, ' '); CTRACE((tfp, "HTML: ALIGN not 'left'. Using space instead of TAB.\n")); } else if (!LYoverride_default_alignment(me) && me->current_default_alignment != HT_LEFT) { /* * Just ensure a collapsible space, until we can replace * HText_getCurrentColumn() in GridText.c with code which doesn't * require that the alignment be HT_LEFT. - FM */ HTML_put_character(me, ' '); CTRACE((tfp, "HTML: Not HT_LEFT. Using space instead of TAB.\n")); } else if ((present[HTML_TAB_TO] && non_empty(value[HTML_TAB_TO])) || (present[HTML_TAB_INDENT] && value[HTML_TAB_INDENT] && isdigit(UCH(*value[HTML_TAB_INDENT])))) { int column, target = -1; int enval = 2; column = HText_getCurrentColumn(me->text); if (present[HTML_TAB_TO] && non_empty(value[HTML_TAB_TO])) { /* * TO has priority over INDENT if both are present. - FM */ StrAllocCopy(temp, value[HTML_TAB_TO]); TRANSLATE_AND_UNESCAPE_TO_STD(&temp); if (*temp) { target = HText_getTabIDColumn(me->text, temp); } } else if (isEmpty(temp) && present[HTML_TAB_INDENT] && value[HTML_TAB_INDENT] && isdigit(UCH(*value[HTML_TAB_INDENT]))) { /* * The INDENT value is in "en" (enval per column) units. * Divide it by enval, rounding odd values up. - FM */ target = (int) (((1.0 * atoi(value[HTML_TAB_INDENT])) / enval) + (0.5)); } FREE(temp); /* * If we are being directed to a column too far to the left or * right, just add a collapsible space, otherwise, add the * appropriate number of spaces. - FM */ if (target < column || target > HText_getMaximumColumn(me->text)) { HTML_put_character(me, ' '); CTRACE((tfp, "HTML: Column out of bounds. Using space instead of TAB.\n")); } else { for (i = column; i < target; i++) HText_appendCharacter(me->text, ' '); HText_setLastChar(me->text, ' '); /* absorb white space */ } } me->in_word = NO; /* * If we have an ID attribute, save it together with the value of the * column we've reached. - FM */ if (present[HTML_TAB_ID] && non_empty(value[HTML_TAB_ID])) { StrAllocCopy(temp, value[HTML_TAB_ID]); TRANSLATE_AND_UNESCAPE_TO_STD(&temp); if (*temp) HText_setTabID(me->text, temp); FREE(temp); } break; case HTML_BASEFONT: break; case HTML_FONT: /* * FONT *may* have been declared SGML_EMPTY in HTMLDTD.c, and * SGML_character() in SGML.c *may* check for a FONT end tag to call * HTML_end_element() directly (with a check in that to bypass * decrementing of the HTML parser's stack). Or this may have been * really a end tag, for which some incarnations of SGML.c * would fake a start tag instead. - fm & kw * * But if we have an open FONT, DON'T close that one now, since FONT * tags can be legally nested AFAIK, and Lynx currently doesn't do * anything with them anyway... - kw */ #ifdef NOTUSED_FOTEMODS if (me->inFONT == TRUE) HTML_end_element(me, HTML_FONT, &include); #endif /* NOTUSED_FOTEMODS */ /* * Set flag to know we are in a FONT container, and add code to do * something about it, someday. - FM */ me->inFONT = TRUE; break; case HTML_B: /* Physical character highlighting */ case HTML_BLINK: case HTML_I: case HTML_U: case HTML_CITE: /* Logical character highlighting */ case HTML_EM: case HTML_STRONG: UPDATE_STYLE; me->Underline_Level++; CHECK_ID(HTML_GEN_ID); /* * Ignore this if inside of a bold anchor or header. Can't display * both underline and bold at same time. */ if (me->inBoldA == TRUE || me->inBoldH == TRUE) { CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level)); break; } if (me->inUnderline == FALSE) { HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); me->inUnderline = TRUE; CTRACE((tfp, "Beginning underline\n")); } else { CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level)); } break; case HTML_ABBR: /* Miscellaneous character containers */ case HTML_ACRONYM: case HTML_AU: case HTML_AUTHOR: case HTML_BIG: case HTML_CODE: case HTML_DFN: case HTML_KBD: case HTML_SAMP: case HTML_SMALL: case HTML_TT: case HTML_VAR: CHECK_ID(HTML_GEN_ID); break; /* ignore */ case HTML_SUP: HText_appendCharacter(me->text, '^'); CHECK_ID(HTML_GEN_ID); break; case HTML_SUB: HText_appendCharacter(me->text, '['); CHECK_ID(HTML_GEN_ID); break; case HTML_DEL: case HTML_S: case HTML_STRIKE: CHECK_ID(HTML_GEN_ID); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "[DEL:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); me->in_word = NO; break; case HTML_INS: CHECK_ID(HTML_GEN_ID); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "[INS:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); me->in_word = NO; break; case HTML_Q: CHECK_ID(HTML_GEN_ID); /* * Should check LANG and/or DIR attributes, and the * me->node_anchor->charset and/or yet to be added structure elements, * to determine whether we should use chevrons, but for now we'll * always use double- or single-quotes. - FM */ if (!(me->Quote_Level & 1)) HTML_put_character(me, '"'); else HTML_put_character(me, '`'); me->Quote_Level++; break; case HTML_PRE: /* Formatted text */ /* * Set our inPRE flag to FALSE so that a newline immediately following * the PRE start tag will be ignored. HTML_put_character() will set it * to TRUE when the first character within the PRE block is received. * - FM */ me->inPRE = FALSE; /* FALLTHRU */ case HTML_LISTING: /* Literal text */ /* FALLTHRU */ case HTML_XMP: /* FALLTHRU */ case HTML_PLAINTEXT: change_paragraph_style(me, styles[ElementNumber]); UPDATE_STYLE; CHECK_ID(HTML_GEN_ID); if (me->comment_end) HText_appendText(me->text, me->comment_end); break; case HTML_BLOCKQUOTE: case HTML_BQ: change_paragraph_style(me, styles[ElementNumber]); UPDATE_STYLE; if (me->sp->tag_number == (int) ElementNumber) LYEnsureDoubleSpace(me); CHECK_ID(HTML_BQ_ID); break; case HTML_NOTE: change_paragraph_style(me, styles[ElementNumber]); UPDATE_STYLE; if (me->sp->tag_number == (int) ElementNumber) LYEnsureDoubleSpace(me); CHECK_ID(HTML_NOTE_ID); { char *note = NULL; /* * Indicate the type of NOTE. */ if (present && present[HTML_NOTE_CLASS] && value[HTML_NOTE_CLASS] && (!strcasecomp(value[HTML_NOTE_CLASS], "CAUTION") || !strcasecomp(value[HTML_NOTE_CLASS], "WARNING"))) { StrAllocCopy(note, value[HTML_NOTE_CLASS]); LYUpperCase(note); StrAllocCat(note, ":"); } else if (present && present[HTML_NOTE_ROLE] && value[HTML_NOTE_ROLE] && (!strcasecomp(value[HTML_NOTE_ROLE], "CAUTION") || !strcasecomp(value[HTML_NOTE_ROLE], "WARNING"))) { StrAllocCopy(note, value[HTML_NOTE_ROLE]); LYUpperCase(note); StrAllocCat(note, ":"); } else { StrAllocCopy(note, "NOTE:"); } if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, note); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); CAN_JUSTIFY_START; FREE(note); } CAN_JUSTIFY_START; me->inLABEL = TRUE; me->in_word = NO; me->inP = FALSE; break; case HTML_ADDRESS: if (me->List_Nesting_Level < 0) { change_paragraph_style(me, styles[ElementNumber]); UPDATE_STYLE; if (me->sp->tag_number == (int) ElementNumber) LYEnsureDoubleSpace(me); } else { LYHandlePlike(me, present, value, include, -1, TRUE); } CHECK_ID(HTML_ADDRESS_ID); break; case HTML_DL: me->List_Nesting_Level++; /* increment the List nesting level */ if (me->List_Nesting_Level <= 0) { change_paragraph_style(me, present && present[HTML_DL_COMPACT] ? styles[HTML_DLC] : styles[HTML_DL]); } else if (me->List_Nesting_Level >= 6) { change_paragraph_style(me, present && present[HTML_DL_COMPACT] ? styles[HTML_DLC6] : styles[HTML_DL6]); } else { change_paragraph_style(me, present && present[HTML_DL_COMPACT] ? styles[(HTML_DLC1 - 1) + me->List_Nesting_Level] : styles[(HTML_DL1 - 1) + me->List_Nesting_Level]); } UPDATE_STYLE; /* update to the new style */ CHECK_ID(HTML_DL_ID); break; case HTML_DLC: me->List_Nesting_Level++; /* increment the List nesting level */ if (me->List_Nesting_Level <= 0) { change_paragraph_style(me, styles[HTML_DLC]); } else if (me->List_Nesting_Level >= 6) { change_paragraph_style(me, styles[HTML_DLC6]); } else { change_paragraph_style(me, styles[(HTML_DLC1 - 1) + me->List_Nesting_Level]); } UPDATE_STYLE; /* update to the new style */ CHECK_ID(HTML_DL_ID); break; case HTML_DT: CHECK_ID(HTML_GEN_ID); if (!me->style_change) { BOOL in_line_1 = HText_inLineOne(me->text); HTCoord saved_spaceBefore = me->sp->style->spaceBefore; HTCoord saved_spaceAfter = me->sp->style->spaceAfter; /* * If there are several DT elements and this is not the first, and * the preceding DT element's first (and normally only) line has * not yet been ended, suppress intervening blank line by * temporarily modifying the paragraph style in place. Ugly but * there's ample precedence. - kw */ if (in_line_1) { me->sp->style->spaceBefore = 0; /* temporary change */ me->sp->style->spaceAfter = 0; /* temporary change */ } HText_appendParagraph(me->text); me->sp->style->spaceBefore = saved_spaceBefore; /* undo */ me->sp->style->spaceAfter = saved_spaceAfter; /* undo */ me->in_word = NO; me->sp->style->alignment = HT_LEFT; } me->inP = FALSE; break; case HTML_DD: CHECK_ID(HTML_GEN_ID); HText_setLastChar(me->text, ' '); /* absorb white space */ if (!me->style_change) { if (!HText_LastLineEmpty(me->text, FALSE)) { HText_appendCharacter(me->text, '\r'); } else { HText_NegateLineOne(me->text); } } else { UPDATE_STYLE; HText_appendCharacter(me->text, '\t'); } me->sp->style->alignment = HT_LEFT; me->in_word = NO; me->inP = FALSE; break; case HTML_OL: /* * Set the default TYPE. */ me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = '1'; /* * Check whether we have a starting sequence number, or want to * continue the numbering from a previous OL in this nest. - FM */ if (present && (present[HTML_OL_SEQNUM] || present[HTML_OL_START])) { int seqnum; /* * Give preference to the valid HTML 3.0 SEQNUM attribute name over * the Netscape START attribute name (too bad the Netscape * developers didn't read the HTML 3.0 specs before re-inventing * the "wheel" as "we'll"). - FM */ if (present[HTML_OL_SEQNUM] && non_empty(value[HTML_OL_SEQNUM])) { seqnum = atoi(value[HTML_OL_SEQNUM]); } else if (present[HTML_OL_START] && non_empty(value[HTML_OL_START])) { seqnum = atoi(value[HTML_OL_START]); } else { seqnum = 1; } /* * Don't allow negative numbers less than or equal to our flags, or * numbers less than 1 if an Alphabetic or Roman TYPE. - FM */ if (present[HTML_OL_TYPE] && value[HTML_OL_TYPE]) { if (*value[HTML_OL_TYPE] == 'A') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'A'; if (seqnum < 1) seqnum = 1; } else if (*value[HTML_OL_TYPE] == 'a') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'a'; if (seqnum < 1) seqnum = 1; } else if (*value[HTML_OL_TYPE] == 'I') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'I'; if (seqnum < 1) seqnum = 1; } else if (*value[HTML_OL_TYPE] == 'i') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'i'; if (seqnum < 1) seqnum = 1; } else { if (seqnum <= OL_VOID) seqnum = OL_VOID + 1; } } else if (seqnum <= OL_VOID) { seqnum = OL_VOID + 1; } me->OL_Counter[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = seqnum; } else if (present && present[HTML_OL_CONTINUE]) { me->OL_Counter[me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11] = OL_CONTINUE; } else { me->OL_Counter[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 1; if (present && present[HTML_OL_TYPE] && value[HTML_OL_TYPE]) { if (*value[HTML_OL_TYPE] == 'A') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'A'; } else if (*value[HTML_OL_TYPE] == 'a') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'a'; } else if (*value[HTML_OL_TYPE] == 'I') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'I'; } else if (*value[HTML_OL_TYPE] == 'i') { me->OL_Type[(me->List_Nesting_Level < 11 ? me->List_Nesting_Level + 1 : 11)] = 'i'; } } } me->List_Nesting_Level++; if (me->List_Nesting_Level <= 0) { change_paragraph_style(me, styles[ElementNumber]); } else if (me->List_Nesting_Level >= 6) { change_paragraph_style(me, styles[HTML_OL6]); } else { change_paragraph_style(me, styles[HTML_OL1 + me->List_Nesting_Level - 1]); } UPDATE_STYLE; /* update to the new style */ CHECK_ID(HTML_OL_ID); break; case HTML_UL: me->List_Nesting_Level++; if (me->List_Nesting_Level <= 0) { if (!(present && present[HTML_UL_PLAIN]) && !(present && present[HTML_UL_TYPE] && value[HTML_UL_TYPE] && 0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) { change_paragraph_style(me, styles[ElementNumber]); } else { change_paragraph_style(me, styles[HTML_DIR]); ElementNumber = HTML_DIR; } } else if (me->List_Nesting_Level >= 6) { if (!(present && present[HTML_UL_PLAIN]) && !(present && present[HTML_UL_TYPE] && value[HTML_UL_TYPE] && 0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) { change_paragraph_style(me, styles[HTML_OL6]); } else { change_paragraph_style(me, styles[HTML_MENU6]); ElementNumber = HTML_DIR; } } else { if (!(present && present[HTML_UL_PLAIN]) && !(present && present[HTML_UL_TYPE] && value[HTML_UL_TYPE] && 0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) { change_paragraph_style(me, styles[HTML_OL1 + me->List_Nesting_Level - 1]); } else { change_paragraph_style(me, styles[HTML_MENU1 + me->List_Nesting_Level - 1]); ElementNumber = HTML_DIR; } } UPDATE_STYLE; /* update to the new style */ CHECK_ID(HTML_UL_ID); break; case HTML_MENU: case HTML_DIR: me->List_Nesting_Level++; if (me->List_Nesting_Level <= 0) { change_paragraph_style(me, styles[ElementNumber]); } else if (me->List_Nesting_Level >= 6) { change_paragraph_style(me, styles[HTML_MENU6]); } else { change_paragraph_style(me, styles[HTML_MENU1 + me->List_Nesting_Level - 1]); } UPDATE_STYLE; /* update to the new style */ CHECK_ID(HTML_UL_ID); break; case HTML_LH: UPDATE_STYLE; /* update to the new style */ HText_appendParagraph(me->text); CHECK_ID(HTML_GEN_ID); HTML_put_character(me, HT_NON_BREAK_SPACE); HText_setLastChar(me->text, ' '); me->in_word = NO; me->inP = FALSE; break; case HTML_LI: UPDATE_STYLE; /* update to the new style */ HText_appendParagraph(me->text); me->sp->style->alignment = HT_LEFT; CHECK_ID(HTML_LI_ID); { int surrounding_tag_number = me->sp[0].tag_number; /* * No, a LI should never occur directly within another LI, but this * may result from incomplete error recovery. So check one more * surrounding level in this case. - kw */ if (surrounding_tag_number == HTML_LI && me->sp < (me->stack + MAX_NESTING - 1)) surrounding_tag_number = me->sp[1].tag_number; if (surrounding_tag_number == HTML_OL) { char number_string[20]; int counter, seqnum; char seqtype; counter = me->List_Nesting_Level < 11 ? me->List_Nesting_Level : 11; if (present && present[HTML_LI_TYPE] && value[HTML_LI_TYPE]) { if (*value[HTML_LI_TYPE] == '1') { me->OL_Type[counter] = '1'; } else if (*value[HTML_LI_TYPE] == 'A') { me->OL_Type[counter] = 'A'; } else if (*value[HTML_LI_TYPE] == 'a') { me->OL_Type[counter] = 'a'; } else if (*value[HTML_LI_TYPE] == 'I') { me->OL_Type[counter] = 'I'; } else if (*value[HTML_LI_TYPE] == 'i') { me->OL_Type[counter] = 'i'; } } if (present && present[HTML_LI_VALUE] && ((value[HTML_LI_VALUE] != NULL) && (*value[HTML_LI_VALUE] != '\0')) && ((isdigit(UCH(*value[HTML_LI_VALUE]))) || (*value[HTML_LI_VALUE] == '-' && isdigit(UCH(*(value[HTML_LI_VALUE] + 1)))))) { seqnum = atoi(value[HTML_LI_VALUE]); if (seqnum <= OL_VOID) seqnum = OL_VOID + 1; seqtype = me->OL_Type[counter]; if (seqtype != '1' && seqnum < 1) seqnum = 1; me->OL_Counter[counter] = seqnum + 1; } else if (me->OL_Counter[counter] >= OL_VOID) { seqnum = me->OL_Counter[counter]++; seqtype = me->OL_Type[counter]; if (seqtype != '1' && seqnum < 1) { seqnum = 1; me->OL_Counter[counter] = seqnum + 1; } } else { seqnum = me->Last_OL_Count + 1; seqtype = me->Last_OL_Type; for (i = (counter - 1); i >= 0; i--) { if (me->OL_Counter[i] > OL_VOID) { seqnum = me->OL_Counter[i]++; seqtype = me->OL_Type[i]; i = 0; } } } if (seqtype == 'A') { strcpy(number_string, LYUppercaseA_OL_String(seqnum)); } else if (seqtype == 'a') { strcpy(number_string, LYLowercaseA_OL_String(seqnum)); } else if (seqtype == 'I') { strcpy(number_string, LYUppercaseI_OL_String(seqnum)); } else if (seqtype == 'i') { strcpy(number_string, LYLowercaseI_OL_String(seqnum)); } else { sprintf(number_string, "%2d.", seqnum); } me->Last_OL_Count = seqnum; me->Last_OL_Type = seqtype; /* * Hack, because there is no append string! */ for (i = 0; number_string[i] != '\0'; i++) if (number_string[i] == ' ') HTML_put_character(me, HT_NON_BREAK_SPACE); else HTML_put_character(me, number_string[i]); /* * Use HTML_put_character so that any other spaces coming * through will be collapsed. We'll use nbsp, so it won't * break at the spacing character if there are no spaces in the * subsequent text up to the right margin, but will declare it * as a normal space to ensure collapsing if a normal space * does immediately follow it. - FM */ HTML_put_character(me, HT_NON_BREAK_SPACE); HText_setLastChar(me->text, ' '); } else if (surrounding_tag_number == HTML_UL) { /* * Hack, because there is no append string! */ HTML_put_character(me, HT_NON_BREAK_SPACE); HTML_put_character(me, HT_NON_BREAK_SPACE); switch (me->List_Nesting_Level % 7) { case 0: HTML_put_character(me, '*'); break; case 1: HTML_put_character(me, '+'); break; case 2: HTML_put_character(me, 'o'); break; case 3: HTML_put_character(me, '#'); break; case 4: HTML_put_character(me, '@'); break; case 5: HTML_put_character(me, '-'); break; case 6: HTML_put_character(me, '='); break; } /* * Keep using HTML_put_character so that any other spaces * coming through will be collapsed. We use nbsp, so we won't * wrap at the spacing character if there are no spaces in the * subsequent text up to the right margin, but will declare it * as a normal space to ensure collapsing if a normal space * does immediately follow it. - FM */ HTML_put_character(me, HT_NON_BREAK_SPACE); HText_setLastChar(me->text, ' '); } else { /* * Hack, because there is no append string! */ HTML_put_character(me, HT_NON_BREAK_SPACE); HTML_put_character(me, HT_NON_BREAK_SPACE); HText_setLastChar(me->text, ' '); } } CAN_JUSTIFY_START; me->in_word = NO; me->inP = FALSE; break; case HTML_SPAN: CHECK_ID(HTML_GEN_ID); /* * Should check LANG and/or DIR attributes, and the * me->node_anchor->charset and/or yet to be added structure elements, * and do something here. - FM */ break; case HTML_BDO: CHECK_ID(HTML_GEN_ID); /* * Should check DIR (and LANG) attributes, and the * me->node_anchor->charset and/or yet to be added structure elements, * and do something here. - FM */ break; case HTML_SPOT: CHECK_ID(HTML_GEN_ID); break; case HTML_FN: change_paragraph_style(me, styles[ElementNumber]); UPDATE_STYLE; if (me->sp->tag_number == (int) ElementNumber) LYEnsureDoubleSpace(me); CHECK_ID(HTML_GEN_ID); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "FOOTNOTE:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); CAN_JUSTIFY_START me->inLABEL = TRUE; me->in_word = NO; me->inP = FALSE; break; case HTML_A: /* * If we are looking for client-side image maps, then handle an A * within a MAP that has a COORDS attribute as an AREA tag. * Unfortunately we lose the anchor text this way for the LYNXIMGMAP, * we would have to do much more parsing to collect it. After * potentially handling the A as AREA, always return immediately if * only looking for image maps, without pushing anything on the style * stack. - kw */ if (me->map_address && present && present[HTML_A_COORDS]) LYStartArea(me, present[HTML_A_HREF] ? value[HTML_A_HREF] : NULL, NULL, present[HTML_A_TITLE] ? value[HTML_A_TITLE] : NULL, tag_charset); if (LYMapsOnly) { return HT_OK; } /* * A may have been declared SGML_EMPTY in HTMLDTD.c, and * SGML_character() in SGML.c may check for an A end tag to call * HTML_end_element() directly (with a check in that to bypass * decrementing of the HTML parser's stack), so if we have an open A, * close that one now. - FM & kw */ if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } /* * Set to know we are in an anchor. */ me->inA = TRUE; /* * Load id_string if we have an ID or NAME. - FM */ if (present && present[HTML_A_ID] && non_empty(value[HTML_A_ID])) { StrAllocCopy(id_string, value[HTML_A_ID]); } else if (present && present[HTML_A_NAME] && non_empty(value[HTML_A_NAME])) { StrAllocCopy(id_string, value[HTML_A_NAME]); } if (id_string) TRANSLATE_AND_UNESCAPE_TO_STD(&id_string); /* * Handle the reference. - FM */ if (present && present[HTML_A_HREF]) { /* * Set to know we are making the content bold. */ me->inBoldA = TRUE; if (isEmpty(value[HTML_A_HREF])) StrAllocCopy(href, "#"); else StrAllocCopy(href, value[HTML_A_HREF]); CHECK_FOR_INTERN(intern_flag, href); /* '#' */ if (intern_flag) { /*** FAST WAY: ***/ TRANSLATE_AND_UNESCAPE_TO_STD(&href); } else { url_type = LYLegitimizeHREF(me, &href, TRUE, TRUE); /* * Deal with our ftp gateway kludge. - FM */ if (!url_type && !StrNCmp(href, "/foo/..", 7) && (isFTP_URL(me->node_anchor->address) || isFILE_URL(me->node_anchor->address))) { for (i = 0; (href[i] = href[i + 7]) != 0; i++) ; } } if (present[HTML_A_ISMAP]) /*??? */ intern_flag = FALSE; } else { if (bold_name_anchors == TRUE) { me->inBoldA = TRUE; } } if (present && present[HTML_A_TYPE] && value[HTML_A_TYPE]) { StrAllocCopy(temp, value[HTML_A_TYPE]); if (!intern_flag && !strcasecomp(value[HTML_A_TYPE], HTAtom_name(HTInternalLink)) && !LYIsUIPage3(me->node_anchor->address, UIP_LIST_PAGE, 0) && !LYIsUIPage3(me->node_anchor->address, UIP_ADDRLIST_PAGE, 0) && !isLYNXIMGMAP(me->node_anchor->address)) { /* Some kind of spoof? * Found TYPE="internal link" but not in a valid context * where we have written it. - kw */ CTRACE((tfp, "HTML: Found invalid HREF=\"%s\" TYPE=\"%s\"!\n", href, temp)); FREE(temp); } } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ href, /* Address */ (temp ? (HTLinkType *) HTAtom_for(temp) : INTERN_LT)); /* Type */ FREE(temp); FREE(id_string); if (me->CurrentA && present) { if (present[HTML_A_TITLE] && non_empty(value[HTML_A_TITLE])) { StrAllocCopy(title, value[HTML_A_TITLE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE); LYTrimHead(title); LYTrimTail(title); if (*title == '\0') { FREE(title); } } if (present[HTML_A_ISMAP]) dest_ismap = TRUE; if (present[HTML_A_CHARSET] && non_empty(value[HTML_A_CHARSET])) { /* * Set up to load the anchor's chartrans structures * appropriately for the current display character set if it * can handle what's claimed. - FM */ StrAllocCopy(temp, value[HTML_A_CHARSET]); TRANSLATE_AND_UNESCAPE_TO_STD(&temp); dest_char_set = UCGetLYhndl_byMIME(temp); if (dest_char_set < 0) { dest_char_set = UCLYhndl_for_unrec; } } if (title != NULL || dest_ismap == TRUE || dest_char_set >= 0) { dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA) ); } if (dest && title != NULL && HTAnchor_title(dest) == NULL) HTAnchor_setTitle(dest, title); if (dest && dest_ismap) dest->isISMAPScript = TRUE; /* Don't allow CHARSET attribute to change *this* document's charset assumption. - kw */ if (dest && dest != me->node_anchor && dest_char_set >= 0) { /* * Load the anchor's chartrans structures. This should be done * more intelligently when setting up the structured object, * but it gets the job done for now. - FM */ HTAnchor_setUCInfoStage(dest, dest_char_set, UCT_STAGE_MIME, UCT_SETBY_DEFAULT); HTAnchor_setUCInfoStage(dest, dest_char_set, UCT_STAGE_PARSER, UCT_SETBY_LINK); } FREE(temp); dest = NULL; FREE(title); } me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldA == TRUE && me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); #if defined(NOTUSED_FOTEMODS) /* * Close an HREF-less NAMED-ed now if we aren't making their content * bold, and let the check in HTML_end_element() deal with any dangling * end tag this creates. - FM */ if (href == NULL && me->inBoldA == FALSE) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } #else /*Close an HREF-less NAMED-ed now if force_empty_hrefless_a was requested - VH */ if (href == NULL && force_empty_hrefless_a) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } #endif FREE(href); break; case HTML_IMG: /* Images */ /* * If we're in an anchor, get the destination, and if it's a clickable * image for the current anchor, set our flags for faking a 0,0 * coordinate pair, which typically returns the image's default. - FM */ if (me->inA && me->CurrentA) { if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA) )) != NULL) { if (dest->isISMAPScript == TRUE) { dest_ismap = TRUE; CTRACE((tfp, "HTML: '%s' is an ISMAP script\n", dest->address)); } else if (present && present[HTML_IMG_ISMAP]) { dest_ismap = TRUE; dest->isISMAPScript = TRUE; CTRACE((tfp, "HTML: Designating '%s' as an ISMAP script\n", dest->address)); } } } intern_flag = FALSE; /* unless set below - kw */ /* * If there's a USEMAP, resolve it. - FM */ if (present && present[HTML_IMG_USEMAP] && non_empty(value[HTML_IMG_USEMAP])) { StrAllocCopy(map_href, value[HTML_IMG_USEMAP]); CHECK_FOR_INTERN(intern_flag, map_href); (void) LYLegitimizeHREF(me, &map_href, TRUE, TRUE); /* * If map_href ended up zero-length or otherwise doesn't have a * hash, it can't be valid, so ignore it. - FM */ if (findPoundSelector(map_href) == NULL) { FREE(map_href); } } /* * Handle a MAP reference if we have one at this point. - FM */ if (map_href) { /* * If the MAP reference doesn't yet begin with a scheme, check * whether a base tag is in effect. - FM */ /* * If the USEMAP value is a lone fragment and LYSeekFragMAPinCur is * set, we'll use the current document's URL for resolving. * Otherwise use the BASE. - kw */ Base = ((me->inBASE && !(*map_href == '#' && LYSeekFragMAPinCur == TRUE)) ? me->base_href : me->node_anchor->address); HTParseALL(&map_href, Base); /* * Prepend our client-side MAP access field. - FM */ StrAllocCopy(temp, STR_LYNXIMGMAP); StrAllocCat(temp, map_href); StrAllocCopy(map_href, temp); FREE(temp); } /* * Check whether we want to suppress the server-side ISMAP link if a * client-side MAP is present. - FM */ if (LYNoISMAPifUSEMAP && map_href && dest_ismap) { dest_ismap = FALSE; dest = NULL; } /* * Check for a TITLE attribute. - FM */ if (present && present[HTML_IMG_TITLE] && non_empty(value[HTML_IMG_TITLE])) { StrAllocCopy(title, value[HTML_IMG_TITLE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE); LYTrimHead(title); LYTrimTail(title); if (*title == '\0') { FREE(title); } } /* * If there's an ALT string, use it, unless the ALT string is * zero-length or just spaces and we are making all SRCs links or have * a USEMAP link. - FM */ if (((present) && (present[HTML_IMG_ALT] && value[HTML_IMG_ALT])) && (!clickable_images || ((clickable_images || map_href) && *value[HTML_IMG_ALT] != '\0'))) { StrAllocCopy(alt_string, value[HTML_IMG_ALT]); TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string, me->UsePlainSpace, me->HiddenValue); /* * If it's all spaces and we are making SRC or USEMAP links, treat * it as zero-length. - FM */ if (clickable_images || map_href) { LYTrimHead(alt_string); LYTrimTail(alt_string); if (*alt_string == '\0') { if (map_href) { StrAllocCopy(alt_string, (title ? title : (temp = MakeNewMapValue(value, "USEMAP")))); FREE(temp); } else if (dest_ismap) { StrAllocCopy(alt_string, (title ? title : (temp = MakeNewMapValue(value, "ISMAP")))); FREE(temp); } else if (me->inA == TRUE && dest) { StrAllocCopy(alt_string, (title ? title : VERBOSE_IMG(value, HTML_IMG_SRC, "[LINK]"))); } else { StrAllocCopy(alt_string, (title ? title : ((present && present[HTML_IMG_ISOBJECT]) ? "(OBJECT)" : VERBOSE_IMG(value, HTML_IMG_SRC, "[INLINE]")))); } } } } else if (map_href) { StrAllocCopy(alt_string, (title ? title : (temp = MakeNewMapValue(value, "USEMAP")))); FREE(temp); } else if ((dest_ismap == TRUE) || (me->inA && present && present[HTML_IMG_ISMAP])) { StrAllocCopy(alt_string, (title ? title : (temp = MakeNewMapValue(value, "ISMAP")))); FREE(temp); } else if (me->inA == TRUE && dest) { StrAllocCopy(alt_string, (title ? title : VERBOSE_IMG(value, HTML_IMG_SRC, "[LINK]"))); } else { if (pseudo_inline_alts || clickable_images) StrAllocCopy(alt_string, (title ? title : ((present && present[HTML_IMG_ISOBJECT]) ? "(OBJECT)" : VERBOSE_IMG(value, HTML_IMG_SRC, "[INLINE]")))); else StrAllocCopy(alt_string, NonNull(title)); } if (*alt_string == '\0' && map_href) { StrAllocCopy(alt_string, (temp = MakeNewMapValue(value, "USEMAP"))); FREE(temp); } CTRACE((tfp, "HTML IMG: USEMAP=%d ISMAP=%d ANCHOR=%d PARA=%d\n", map_href ? 1 : 0, (dest_ismap == TRUE) ? 1 : 0, me->inA, me->inP)); /* * Check for an ID attribute. - FM */ if (present && present[HTML_IMG_ID] && non_empty(value[HTML_IMG_ID])) { StrAllocCopy(id_string, value[HTML_IMG_ID]); TRANSLATE_AND_UNESCAPE_TO_STD(&id_string); if (*id_string == '\0') { FREE(id_string); } } /* * Create links to the SRC for all images, if desired. - FM */ if (clickable_images && present && present[HTML_IMG_SRC] && non_empty(value[HTML_IMG_SRC])) { StrAllocCopy(href, value[HTML_IMG_SRC]); LYLegitimizeHREF(me, &href, TRUE, TRUE); /* * If it's an ISMAP and/or USEMAP, or graphic for an anchor, end * that anchor and start one for the SRC. - FM */ if (me->inA) { /* * If we have a USEMAP, end this anchor and start a new one for * the client-side MAP. - FM */ if (map_href) { if (dest_ismap) { HTML_put_character(me, ' '); me->in_word = NO; HTML_put_string(me, (temp = MakeNewMapValue(value, "ISMAP"))); FREE(temp); } else if (dest) { HTML_put_character(me, ' '); me->in_word = NO; HTML_put_string(me, "[LINK]"); } if (me->inBoldA == TRUE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_END_CHAR); } me->inBoldA = FALSE; HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; if (dest_ismap || dest) HTML_put_character(me, '-'); if (id_string) { if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ NULL, /* Addresss */ 0)) != NULL) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); } } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ map_href, /* Addresss */ INTERN_LT); /* Type */ if (me->CurrentA && title) { if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA) )) != NULL) { if (!HTAnchor_title(dest)) HTAnchor_setTitle(dest, title); } } me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldA == FALSE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_START_CHAR); } me->inBoldA = TRUE; } else { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } HTML_put_string(me, alt_string); if (me->inBoldA == TRUE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_END_CHAR); } me->inBoldA = FALSE; HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; HTML_put_character(me, '-'); FREE(newtitle); StrAllocCopy(alt_string, ((present && present[HTML_IMG_ISOBJECT]) ? ((map_href || dest_ismap) ? "(IMAGE)" : "(OBJECT)") : VERBOSE_IMG(value, HTML_IMG_SRC, "[IMAGE]"))); if (id_string && !map_href) { if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ NULL, /* Addresss */ 0)) != NULL) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); } } } else if (map_href) { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; if (id_string) { if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ NULL, /* Addresss */ 0)) != NULL) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); } } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ map_href, /* Addresss */ INTERN_LT); /* Type */ if (me->CurrentA && title) { if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA) )) != NULL) { if (!HTAnchor_title(dest)) HTAnchor_setTitle(dest, title); } } me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldA == FALSE && me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); me->inBoldA = TRUE; HTML_put_string(me, alt_string); if (me->inBoldA == TRUE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_END_CHAR); } me->inBoldA = FALSE; HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; HTML_put_character(me, '-'); FREE(newtitle); StrAllocCopy(alt_string, ((present && present[HTML_IMG_ISOBJECT]) ? "(IMAGE)" : VERBOSE_IMG(value, HTML_IMG_SRC, "[IMAGE]"))); } else { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; if (id_string) { if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ NULL, /* Addresss */ 0)) != NULL) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); } } } /* * Create the link to the SRC. - FM */ me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ FREE(href); me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, alt_string); if (!me->inA) { if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } else { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; me->inBoldA = TRUE; } } else if (map_href) { if (me->inA) { /* * We're in an anchor and have a USEMAP, so end the anchor and * start a new one for the client-side MAP. - FM */ if (dest_ismap) { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; HTML_put_string(me, (temp = MakeNewMapValue(value, "ISMAP"))); FREE(temp); } else if (dest) { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; HTML_put_string(me, "[LINK]"); } if (me->inBoldA == TRUE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_END_CHAR); } me->inBoldA = FALSE; HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; if (dest_ismap || dest) { HTML_put_character(me, '-'); } } else { HTML_put_character(me, ' '); me->in_word = NO; } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ map_href, /* Addresss */ INTERN_LT); /* Type */ if (me->CurrentA && title) { if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA) )) != NULL) { if (!HTAnchor_title(dest)) HTAnchor_setTitle(dest, title); } } me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldA == FALSE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_START_CHAR); } me->inBoldA = TRUE; HTML_put_string(me, alt_string); if (!me->inA) { if (me->inBoldA == TRUE && me->inBoldH == FALSE) { HText_appendCharacter(me->text, LY_BOLD_END_CHAR); } me->inBoldA = FALSE; HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; } } else { /* * Just put in the ALT or pseudo-ALT string for the current anchor * or inline, with an ID link if indicated. - FM */ HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; if (id_string) { if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ NULL, /* Addresss */ (HTLinkType *) 0)) != NULL) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); } } HTML_put_string(me, alt_string); HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } FREE(map_href); FREE(alt_string); FREE(id_string); FREE(title); FREE(newtitle); dest = NULL; break; case HTML_MAP: /* * Load id_string if we have a NAME or ID. - FM */ if (present && present[HTML_MAP_NAME] && non_empty(value[HTML_MAP_NAME])) { StrAllocCopy(id_string, value[HTML_MAP_NAME]); } else if (present && present[HTML_MAP_ID] && non_empty(value[HTML_MAP_ID])) { StrAllocCopy(id_string, value[HTML_MAP_ID]); } if (id_string) { TRANSLATE_AND_UNESCAPE_TO_STD(&id_string); if (*id_string == '\0') { FREE(id_string); } } /* * Generate a target anchor in this place in the containing document. * MAP can now contain block markup, if it doesn't contain any AREAs * (or A anchors with COORDS converted to AREAs) the current location * can be used as a fallback for following a USEMAP link. - kw */ if (!LYMapsOnly) LYHandleID(me, id_string); /* * Load map_address. - FM */ if (id_string) { /* * The MAP must be in the current stream, even if it had a BASE * tag, so we'll use its address here, but still use the BASE, if * present, when resolving the AREA elements in it's content, * unless the AREA's HREF is a lone fragment and * LYSeekFragAREAinCur is set. - FM && KW */ StrAllocCopy(me->map_address, me->node_anchor->address); if ((cp = StrChr(me->map_address, '#')) != NULL) *cp = '\0'; StrAllocCat(me->map_address, "#"); StrAllocCat(me->map_address, id_string); FREE(id_string); if (present && present[HTML_MAP_TITLE] && non_empty(value[HTML_MAP_TITLE])) { StrAllocCopy(title, value[HTML_MAP_TITLE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE); LYTrimHead(title); LYTrimTail(title); if (*title == '\0') { FREE(title); } } LYAddImageMap(me->map_address, title, me->node_anchor); FREE(title); } break; case HTML_AREA: if (me->map_address && present && present[HTML_AREA_HREF] && non_empty(value[HTML_AREA_HREF])) { /* * Resolve the HREF. - FM */ StrAllocCopy(href, value[HTML_AREA_HREF]); CHECK_FOR_INTERN(intern_flag, href); (void) LYLegitimizeHREF(me, &href, TRUE, TRUE); /* * Check whether a BASE tag is in effect, and use it for resolving, * even though we used this stream's address for locating the MAP * itself, unless the HREF is a lone fragment and * LYSeekFragAREAinCur is set. - FM */ Base = (((me->inBASE && *href != '\0') && !(*href == '#' && LYSeekFragAREAinCur == TRUE)) ? me->base_href : me->node_anchor->address); HTParseALL(&href, Base); /* * Check for an ALT. - FM */ if (present[HTML_AREA_ALT] && non_empty(value[HTML_AREA_ALT])) { StrAllocCopy(alt_string, value[HTML_AREA_ALT]); } else if (present[HTML_AREA_TITLE] && non_empty(value[HTML_AREA_TITLE])) { /* * Use the TITLE as an ALT. - FM */ StrAllocCopy(alt_string, value[HTML_AREA_TITLE]); } if (alt_string != NULL) { TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string, me->UsePlainSpace, me->HiddenValue); /* * Make sure it's not just space(s). - FM */ LYTrimHead(alt_string); LYTrimTail(alt_string); if (*alt_string == '\0') { StrAllocCopy(alt_string, href); } } else { /* * Use the HREF as an ALT. - FM */ StrAllocCopy(alt_string, href); } LYAddMapElement(me->map_address, href, alt_string, me->node_anchor, intern_flag); FREE(href); FREE(alt_string); } break; case HTML_PARAM: /* * We may need to look at this someday to deal with MAPs, OBJECTs or * APPLETs optimally, but just ignore it for now. - FM */ break; case HTML_BODYTEXT: CHECK_ID(HTML_BODYTEXT_ID); /* * We may need to look at this someday to deal with OBJECTs optimally, * but just ignore it for now. - FM */ break; case HTML_TEXTFLOW: CHECK_ID(HTML_BODYTEXT_ID); /* * We may need to look at this someday to deal with APPLETs optimally, * but just ignore it for now. - FM */ break; case HTML_FIG: if (present) LYHandleFIG(me, present, value, present[HTML_FIG_ISOBJECT], present[HTML_FIG_IMAGEMAP], present[HTML_FIG_ID] ? value[HTML_FIG_ID] : NULL, present[HTML_FIG_SRC] ? value[HTML_FIG_SRC] : NULL, YES, TRUE, &intern_flag); else LYHandleFIG(me, NULL, NULL, 0, 0, NULL, NULL, YES, TRUE, &intern_flag); break; case HTML_OBJECT: if (!me->object_started) { /* * This is an outer OBJECT start tag, i.e., not a nested OBJECT, so * save its relevant attributes. - FM */ if (present) { if (present[HTML_OBJECT_DECLARE]) me->object_declare = TRUE; if (present[HTML_OBJECT_SHAPES]) me->object_shapes = TRUE; if (present[HTML_OBJECT_ISMAP]) me->object_ismap = TRUE; if (present[HTML_OBJECT_USEMAP] && non_empty(value[HTML_OBJECT_USEMAP])) { StrAllocCopy(me->object_usemap, value[HTML_OBJECT_USEMAP]); TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_usemap); if (*me->object_usemap == '\0') { FREE(me->object_usemap); } } if (present[HTML_OBJECT_ID] && non_empty(value[HTML_OBJECT_ID])) { StrAllocCopy(me->object_id, value[HTML_OBJECT_ID]); TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_id); if (*me->object_id == '\0') { FREE(me->object_id); } } if (present[HTML_OBJECT_TITLE] && non_empty(value[HTML_OBJECT_TITLE])) { StrAllocCopy(me->object_title, value[HTML_OBJECT_TITLE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_title, TRUE, FALSE); LYTrimHead(me->object_title); LYTrimTail(me->object_title); if (me->object_title == '\0') { FREE(me->object_title); } } if (present[HTML_OBJECT_DATA] && non_empty(value[HTML_OBJECT_DATA])) { StrAllocCopy(me->object_data, value[HTML_OBJECT_DATA]); TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_data); if (*me->object_data == '\0') { FREE(me->object_data); } } if (present[HTML_OBJECT_TYPE] && non_empty(value[HTML_OBJECT_TYPE])) { StrAllocCopy(me->object_type, value[HTML_OBJECT_TYPE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_type, TRUE, FALSE); LYTrimHead(me->object_type); LYTrimTail(me->object_type); if (me->object_type == '\0') { FREE(me->object_type); } } if (present[HTML_OBJECT_CLASSID] && non_empty(value[HTML_OBJECT_CLASSID])) { StrAllocCopy(me->object_classid, value[HTML_OBJECT_CLASSID]); TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_classid, TRUE, FALSE); LYTrimHead(me->object_classid); LYTrimTail(me->object_classid); if (me->object_classid == '\0') { FREE(me->object_classid); } } if (present[HTML_OBJECT_CODEBASE] && non_empty(value[HTML_OBJECT_CODEBASE])) { StrAllocCopy(me->object_codebase, value[HTML_OBJECT_CODEBASE]); TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_codebase); if (*me->object_codebase == '\0') { FREE(me->object_codebase); } } if (present[HTML_OBJECT_CODETYPE] && non_empty(value[HTML_OBJECT_CODETYPE])) { StrAllocCopy(me->object_codetype, value[HTML_OBJECT_CODETYPE]); TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_codetype, TRUE, FALSE); LYTrimHead(me->object_codetype); LYTrimTail(me->object_codetype); if (me->object_codetype == '\0') { FREE(me->object_codetype); } } if (present[HTML_OBJECT_NAME] && non_empty(value[HTML_OBJECT_NAME])) { StrAllocCopy(me->object_name, value[HTML_OBJECT_NAME]); TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_name, TRUE, FALSE); LYTrimHead(me->object_name); LYTrimTail(me->object_name); if (me->object_name == '\0') { FREE(me->object_name); } } } /* * If we can determine now that we are not going to do anything * special to the OBJECT element's SGML contents, like skipping it * completely or collecting it up in order to add something after * it, then generate any output that should be emitted in the place * of the OBJECT start tag NOW, then don't initialize special * handling but return, letting our SGML parser know that further * content is to be parsed normally not literally. We could defer * this until we have collected the contents and then recycle the * contents (as was previously always done), but that has a higher * chance of completely losing content in case of nesting errors in * the input, incomplete transmissions, etc. - kw */ if ((!present || (me->object_declare == FALSE && me->object_name == NULL && me->object_shapes == FALSE && me->object_usemap == NULL))) { if (!LYMapsOnly) { if (!clickable_images || me->object_data == NULL || !(me->object_data != NULL && me->object_classid == NULL && me->object_codebase == NULL && me->object_codetype == NULL)) FREE(me->object_data); if (me->object_data) { HTStartAnchor5(me, (me->object_id ? value[HTML_OBJECT_ID] : NULL), value[HTML_OBJECT_DATA], value[HTML_OBJECT_TYPE], tag_charset); if ((me->object_type != NULL) && !strncasecomp(me->object_type, "image/", 6)) HTML_put_string(me, "(IMAGE)"); else HTML_put_string(me, "(OBJECT)"); HTML_end_element(me, HTML_A, NULL); } else if (me->object_id) LYHandleID(me, me->object_id); } clear_objectdata(me); /* * We do NOT want the HTML_put_* functions that are going to be * called for the OBJECT's character content to add to the * chunk, so we don't push on the stack. Instead we keep a * counter for open OBJECT tags that are treated this way, so * HTML_end_element can skip handling the corresponding end tag * that is going to arrive unexpectedly as far as our stack is * concerned. */ status = HT_PARSER_OTHER_CONTENT; if (me->sp[0].tag_number == HTML_FIG && me->objects_figged_open > 0) { ElementNumber = (HTMLElement) HTML_OBJECT_M; } else { me->objects_mixed_open++; SET_SKIP_STACK(HTML_OBJECT); } } else if (me->object_declare == FALSE && me->object_name == NULL && me->object_shapes == TRUE) { LYHandleFIG(me, present, value, 1, 1 || me->object_ismap, me->object_id, ((me->object_data && !me->object_classid) ? value[HTML_OBJECT_DATA] : NULL), NO, TRUE, &intern_flag); clear_objectdata(me); status = HT_PARSER_OTHER_CONTENT; me->objects_figged_open++; ElementNumber = HTML_FIG; } else { /* * Set flag that we are accumulating OBJECT content. - FM */ me->object_started = TRUE; } } break; case HTML_OVERLAY: if (clickable_images && me->inFIG && present && present[HTML_OVERLAY_SRC] && non_empty(value[HTML_OVERLAY_SRC])) { StrAllocCopy(href, value[HTML_OVERLAY_SRC]); LYLegitimizeHREF(me, &href, TRUE, TRUE); if (*href) { if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ HTML_put_character(me, ' '); HText_appendCharacter(me->text, '+'); me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, "[OVERLAY]"); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); HTML_put_character(me, ' '); me->in_word = NO; } FREE(href); } break; case HTML_APPLET: me->inAPPLET = TRUE; me->inAPPLETwithP = FALSE; HTML_put_character(me, ' '); /* space char may be ignored */ /* * Load id_string if we have an ID or NAME. - FM */ if (present && present[HTML_APPLET_ID] && non_empty(value[HTML_APPLET_ID])) { StrAllocCopy(id_string, value[HTML_APPLET_ID]); } else if (present && present[HTML_APPLET_NAME] && non_empty(value[HTML_APPLET_NAME])) { StrAllocCopy(id_string, value[HTML_APPLET_NAME]); } if (id_string) { TRANSLATE_AND_UNESCAPE_TO_STD(&id_string); LYHandleID(me, id_string); FREE(id_string); } me->in_word = NO; /* * If there's an ALT string, use it, unless the ALT string is * zero-length and we are making all sources links. - FM */ if (present && present[HTML_APPLET_ALT] && value[HTML_APPLET_ALT] && (!clickable_images || (clickable_images && *value[HTML_APPLET_ALT] != '\0'))) { StrAllocCopy(alt_string, value[HTML_APPLET_ALT]); TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string, me->UsePlainSpace, me->HiddenValue); /* * If it's all spaces and we are making sources links, treat it as * zero-length. - FM */ if (clickable_images) { LYTrimHead(alt_string); LYTrimTail(alt_string); if (*alt_string == '\0') { StrAllocCopy(alt_string, "[APPLET]"); } } } else { if (clickable_images) StrAllocCopy(alt_string, "[APPLET]"); else StrAllocCopy(alt_string, ""); } /* * If we're making all sources links, get the source. - FM */ if (clickable_images && present && present[HTML_APPLET_CODE] && non_empty(value[HTML_APPLET_CODE])) { char *base = NULL; Base = (me->inBASE) ? me->base_href : me->node_anchor->address; /* * Check for a CODEBASE attribute. - FM */ if (present[HTML_APPLET_CODEBASE] && non_empty(value[HTML_APPLET_CODEBASE])) { StrAllocCopy(base, value[HTML_APPLET_CODEBASE]); LYRemoveBlanks(base); TRANSLATE_AND_UNESCAPE_TO_STD(&base); /* * Force it to be a directory. - FM */ if (*base == '\0') StrAllocCopy(base, "/"); LYAddHtmlSep(&base); LYLegitimizeHREF(me, &base, TRUE, FALSE); HTParseALL(&base, Base); } StrAllocCopy(href, value[HTML_APPLET_CODE]); LYLegitimizeHREF(me, &href, TRUE, FALSE); HTParseALL(&href, (base ? base : Base)); FREE(base); if (*href) { if (me->inA) { if (me->inBoldA == TRUE && me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); HTML_put_character(me, '-'); } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, alt_string); if (me->inA == FALSE) { if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; } HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } FREE(href); } else if (*alt_string) { /* * Just put up the ALT string, if non-zero. - FM */ HTML_put_string(me, alt_string); HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } FREE(alt_string); FREE(id_string); break; case HTML_BGSOUND: /* * If we're making all sources links, get the source. - FM */ if (clickable_images && present && present[HTML_BGSOUND_SRC] && non_empty(value[HTML_BGSOUND_SRC])) { StrAllocCopy(href, value[HTML_BGSOUND_SRC]); LYLegitimizeHREF(me, &href, TRUE, TRUE); if (*href == '\0') { FREE(href); break; } if (me->inA) { if (me->inBoldA == TRUE && me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); HTML_put_character(me, '-'); } else { HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, "[BGSOUND]"); if (me->inA == FALSE) { if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; } HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; FREE(href); } break; case HTML_EMBED: if (pseudo_inline_alts || clickable_images) HTML_put_character(me, ' '); /* space char may be ignored */ /* * Load id_string if we have an ID or NAME. - FM */ if (present && present[HTML_EMBED_ID] && non_empty(value[HTML_EMBED_ID])) { StrAllocCopy(id_string, value[HTML_EMBED_ID]); } else if (present && present[HTML_EMBED_NAME] && non_empty(value[HTML_EMBED_NAME])) { StrAllocCopy(id_string, value[HTML_EMBED_NAME]); } if (id_string) { TRANSLATE_AND_UNESCAPE_TO_STD(&id_string); LYHandleID(me, id_string); FREE(id_string); } if (pseudo_inline_alts || clickable_images) me->in_word = NO; /* * If there's an ALT string, use it, unless the ALT string is * zero-length and we are making all sources links. - FM */ if (present && present[HTML_EMBED_ALT] && value[HTML_EMBED_ALT] && (!clickable_images || (clickable_images && *value[HTML_EMBED_ALT] != '\0'))) { StrAllocCopy(alt_string, value[HTML_EMBED_ALT]); TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string, me->UsePlainSpace, me->HiddenValue); /* * If it's all spaces and we are making sources links, treat it as * zero-length. - FM */ if (clickable_images) { LYTrimHead(alt_string); LYTrimTail(alt_string); if (*alt_string == '\0') { StrAllocCopy(alt_string, "[EMBED]"); } } } else { if (pseudo_inline_alts || clickable_images) StrAllocCopy(alt_string, "[EMBED]"); else StrAllocCopy(alt_string, ""); } /* * If we're making all sources links, get the source. - FM */ if (clickable_images && present && present[HTML_EMBED_SRC] && non_empty(value[HTML_EMBED_SRC])) { StrAllocCopy(href, value[HTML_EMBED_SRC]); LYLegitimizeHREF(me, &href, TRUE, TRUE); if (*href) { if (me->inA) { if (me->inBoldA == TRUE && me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); HTML_put_character(me, '-'); } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ me->CurrentANum = HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, alt_string); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); if (me->inA == FALSE) { if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; } HTML_put_character(me, ' '); me->in_word = NO; } FREE(href); } else if (*alt_string) { /* * Just put up the ALT string, if non-zero. - FM */ HTML_put_string(me, alt_string); HTML_put_character(me, ' '); /* space char may be ignored */ me->in_word = NO; } FREE(alt_string); FREE(id_string); break; case HTML_CREDIT: LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); me->inCREDIT = TRUE; CHECK_ID(HTML_GEN_ID); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "CREDIT:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); CAN_JUSTIFY_START; if (me->inFIG) /* * Assume all text in the FIG container is intended to be * paragraphed. - FM */ me->inFIGwithP = TRUE; if (me->inAPPLET) /* * Assume all text in the APPLET container is intended to be * paragraphed. - FM */ me->inAPPLETwithP = TRUE; me->inLABEL = TRUE; me->in_word = NO; me->inP = FALSE; break; case HTML_CAPTION: LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); me->inCAPTION = TRUE; CHECK_ID(HTML_CAPTION_ID); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, "CAPTION:"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); CAN_JUSTIFY_START; if (me->inFIG) /* * Assume all text in the FIG container is intended to be * paragraphed. - FM */ me->inFIGwithP = TRUE; if (me->inAPPLET) /* * Assume all text in the APPLET container is intended to be * paragraphed. - FM */ me->inAPPLETwithP = TRUE; me->inLABEL = TRUE; me->in_word = NO; me->inP = FALSE; break; case HTML_FORM: { char *action = NULL; char *method = NULL; char *enctype = NULL; const char *accept_cs = NULL; HTChildAnchor *source; HTAnchor *link_dest; /* * FORM may have been declared SGML_EMPTY in HTMLDTD.c, and * SGML_character() in SGML.c may check for a FORM end tag to call * HTML_end_element() directly (with a check in that to bypass * decrementing of the HTML parser's stack), so if we have an open * FORM, close that one now. - FM */ if (me->inFORM) { CTRACE((tfp, "HTML: Missing FORM end tag. Faking it!\n")); SET_SKIP_STACK(HTML_FORM); HTML_end_element(me, HTML_FORM, include); } /* * Set to know we are in a new form. */ me->inFORM = TRUE; EMIT_IFDEF_USE_JUSTIFY_ELTS(form_in_htext = TRUE); if (present && present[HTML_FORM_ACCEPT_CHARSET]) { accept_cs = (value[HTML_FORM_ACCEPT_CHARSET] ? value[HTML_FORM_ACCEPT_CHARSET] : "UNKNOWN"); } Base = (me->inBASE) ? me->base_href : me->node_anchor->address; if (present && present[HTML_FORM_ACTION] && value[HTML_FORM_ACTION]) { StrAllocCopy(action, value[HTML_FORM_ACTION]); LYLegitimizeHREF(me, &action, TRUE, TRUE); /* * Check whether a base tag is in effect. Note that actions * always are resolved w.r.t. to the base, even if the action * is empty. - FM */ HTParseALL(&action, Base); } else { StrAllocCopy(action, Base); } source = HTAnchor_findChildAndLink(me->node_anchor, NULL, action, (HTLinkType *) 0); if ((link_dest = HTAnchor_followLink(source)) != NULL) { /* * Memory leak fixed. 05-28-94 Lynx 2-3-1 Garrett Arch Blythe */ char *cp_freeme = HTAnchor_address(link_dest); if (cp_freeme != NULL) { StrAllocCopy(action, cp_freeme); FREE(cp_freeme); } else { StrAllocCopy(action, ""); } } if (present && present[HTML_FORM_METHOD]) StrAllocCopy(method, (value[HTML_FORM_METHOD] ? value[HTML_FORM_METHOD] : "GET")); if (present && present[HTML_FORM_ENCTYPE] && non_empty(value[HTML_FORM_ENCTYPE])) { StrAllocCopy(enctype, value[HTML_FORM_ENCTYPE]); LYLowerCase(enctype); } if (present) { /* * Check for a TITLE attribute, and if none is present, check * for a SUBJECT attribute as a synonym. - FM */ if (present[HTML_FORM_TITLE] && non_empty(value[HTML_FORM_TITLE])) { StrAllocCopy(title, value[HTML_FORM_TITLE]); } else if (present[HTML_FORM_SUBJECT] && non_empty(value[HTML_FORM_SUBJECT])) { StrAllocCopy(title, value[HTML_FORM_SUBJECT]); } if (non_empty(title)) { TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE); LYTrimHead(title); LYTrimTail(title); if (*title == '\0') { FREE(title); } } } HText_beginForm(action, method, enctype, title, accept_cs); FREE(action); FREE(method); FREE(enctype); FREE(title); } CHECK_ID(HTML_FORM_ID); break; case HTML_FIELDSET: LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); CHECK_ID(HTML_GEN_ID); break; case HTML_LEGEND: LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); CHECK_ID(HTML_CAPTION_ID); break; case HTML_LABEL: CHECK_ID(HTML_LABEL_ID); break; case HTML_KEYGEN: CHECK_ID(HTML_KEYGEN_ID); break; case HTML_BUTTON: { InputFieldData I; int chars; /* init */ memset(&I, 0, sizeof(I)); I.name_cs = ATTR_CS_IN; I.value_cs = ATTR_CS_IN; UPDATE_STYLE; if (present && present[HTML_BUTTON_TYPE] && value[HTML_BUTTON_TYPE]) { if (!strcasecomp(value[HTML_BUTTON_TYPE], "submit") || !strcasecomp(value[HTML_BUTTON_TYPE], "reset")) { /* * It's a button for submitting or resetting a form. - FM */ I.type = value[HTML_BUTTON_TYPE]; } else { /* * Ugh, it's a button for a script. - FM */ I.type = value[HTML_BUTTON_TYPE]; } } else { /* default, if no type given, is a submit button */ I.type = "submit"; } /* * Before any input field, add a collapsible space if we're not in * a PRE block, to promote a wrap there for any long values that * would extend past the right margin from our current position in * the line. If we are in a PRE block, start a new line if the * last line already is within 6 characters of the wrap point for * PRE blocks. - FM */ if (me->sp[0].tag_number != HTML_PRE && !me->inPRE && me->sp->style->freeFormat) { HTML_put_character(me, ' '); me->in_word = NO; } else if (HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 6)) { HTML_put_character(me, '\n'); me->in_word = NO; } HTML_put_character(me, '('); if (!(present && present[HTML_BUTTON_NAME] && value[HTML_BUTTON_NAME])) { I.name = ""; } else if (StrChr(value[HTML_BUTTON_NAME], '&') == NULL) { I.name = value[HTML_BUTTON_NAME]; } else { StrAllocCopy(I_name, value[HTML_BUTTON_NAME]); UNESCAPE_FIELDNAME_TO_STD(&I_name); I.name = I_name; } if (present && present[HTML_BUTTON_VALUE] && non_empty(value[HTML_BUTTON_VALUE])) { /* * Convert any HTML entities or decimal escaping. - FM */ StrAllocCopy(I_value, value[HTML_BUTTON_VALUE]); me->UsePlainSpace = TRUE; TRANSLATE_AND_UNESCAPE_ENTITIES(&I_value, TRUE, me->HiddenValue); me->UsePlainSpace = FALSE; I.value = I_value; /* * Convert any newlines or tabs to spaces, and trim any lead or * trailing spaces. - FM */ LYReduceBlanks(I.value); } else if (!strcasecomp(I.type, "button")) { if (non_empty(I.name)) { StrAllocCopy(I.value, I.name); } else { StrAllocCopy(I.value, "BUTTON"); } } else if (I.value == 0) { StrAllocCopy(I.value, "BUTTON"); } if (present && present[HTML_BUTTON_READONLY]) I.readonly = YES; if (present && present[HTML_BUTTON_DISABLED]) I.disabled = YES; if (present && present[HTML_BUTTON_CLASS] && /* Not yet used. */ non_empty(value[HTML_BUTTON_CLASS])) I.iclass = value[HTML_BUTTON_CLASS]; if (present && present[HTML_BUTTON_ID] && non_empty(value[HTML_BUTTON_ID])) { I.id = value[HTML_BUTTON_ID]; CHECK_ID(HTML_BUTTON_ID); } if (present && present[HTML_BUTTON_LANG] && /* Not yet used. */ non_empty(value[HTML_BUTTON_LANG])) I.lang = value[HTML_BUTTON_LANG]; chars = HText_beginInput(me->text, me->inUnderline, &I); /* * Submit and reset buttons have values which don't change, so * HText_beginInput() sets I.value to the string which should be * displayed, and we'll enter that instead of underscore * placeholders into the HText structure to see it instead of * underscores when dumping or printing. We also won't worry about * a wrap in PRE blocks, because the line editor never is invoked * for submit or reset buttons. - LE & FM */ if (me->sp[0].tag_number == HTML_PRE || !me->sp->style->freeFormat) { /* * We have a submit or reset button in a PRE block, so output * the entire value from the markup. If it extends to the * right margin, it will wrap there, and only the portion * before that wrap will be hightlighted on screen display * (Yuk!) but we may as well show the rest of the full value on * the next or more lines. - FM */ while (I.value[i]) HTML_put_character(me, I.value[i++]); } else { /* * The submit or reset button is not in a PRE block. Note that * if a wrap occurs before outputting the entire value, the * wrapped portion will not be highlighted or clearly indicated * as part of the link for submission or reset (Yuk!). We'll * replace any spaces in the submit or reset button value with * nbsp, to promote a wrap at the space we ensured would be * present before the start of the string, as when we use all * underscores instead of the INPUT's actual value, but we * could still get a wrap at the right margin, instead, if the * value is greater than a line width for the current style. * Also, if chars somehow ended up longer than the length of * the actual value (shouldn't have), we'll continue padding * with nbsp up to the length of chars. - FM */ for (i = 0; I.value[i]; i++) { HTML_put_character(me, (char) ((I.value[i] == ' ') ? HT_NON_BREAK_SPACE : I.value[i])); } while (i++ < chars) { HTML_put_character(me, HT_NON_BREAK_SPACE); } } HTML_put_character(me, ')'); if (me->sp[0].tag_number != HTML_PRE && me->sp->style->freeFormat) { HTML_put_character(me, ' '); me->in_word = NO; } FREE(I_value); FREE(I_name); } break; case HTML_INPUT: { InputFieldData I; int chars; BOOL UseALTasVALUE = FALSE; BOOL HaveSRClink = FALSE; char *ImageSrc = NULL; BOOL IsSubmitOrReset = FALSE; HTkcode kcode = NOKANJI; HTkcode specified_kcode = NOKANJI; /* init */ memset(&I, 0, sizeof(I)); I.name_cs = ATTR_CS_IN; I.value_cs = ATTR_CS_IN; UPDATE_STYLE; /* * Before any input field, add a collapsible space if we're not in * a PRE block, to promote a wrap there for any long values that * would extend past the right margin from our current position in * the line. If we are in a PRE block, start a new line if the * last line already is within 6 characters of the wrap point for * PRE blocks. - FM */ if (me->sp[0].tag_number != HTML_PRE && !me->inPRE && me->sp->style->freeFormat) { HTML_put_character(me, ' '); me->in_word = NO; } else if (HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 6)) { HTML_put_character(me, '\n'); me->in_word = NO; } /* * Get the TYPE and make sure we can handle it. - FM */ if (present && present[HTML_INPUT_TYPE] && non_empty(value[HTML_INPUT_TYPE])) { const char *not_impl = NULL; char *usingval = NULL; I.type = value[HTML_INPUT_TYPE]; if (!strcasecomp(I.type, "range")) { if (present[HTML_INPUT_MIN] && non_empty(value[HTML_INPUT_MIN])) I.min = value[HTML_INPUT_MIN]; if (present[HTML_INPUT_MAX] && non_empty(value[HTML_INPUT_MAX])) I.max = value[HTML_INPUT_MAX]; /* * Not yet implemented. */ #ifdef NOTDEFINED not_impl = "[RANGE Input]"; if (me->inFORM) HText_DisableCurrentForm(); #endif /* NOTDEFINED */ CTRACE((tfp, "HTML: Ignoring TYPE=\"range\"\n")); break; } else if (!strcasecomp(I.type, "file")) { if (present[HTML_INPUT_ACCEPT] && non_empty(value[HTML_INPUT_ACCEPT])) I.accept = value[HTML_INPUT_ACCEPT]; #ifndef USE_FILE_UPLOAD not_impl = "[FILE Input]"; CTRACE((tfp, "Attempting to fake as: %s\n", I.type)); #ifdef NOTDEFINED if (me->inFORM) HText_DisableCurrentForm(); #endif /* NOTDEFINED */ CTRACE((tfp, "HTML: Ignoring TYPE=\"file\"\n")); #endif /* USE_FILE_UPLOAD */ } else if (!strcasecomp(I.type, "button")) { /* * Ugh, a button for a script. */ not_impl = "[BUTTON Input]"; } if (not_impl != NULL) { if (me->inUnderline == FALSE) { HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); } HTML_put_string(me, not_impl); if (usingval != NULL) { HTML_put_string(me, usingval); FREE(usingval); } else { HTML_put_string(me, " (not implemented)"); } if (me->inUnderline == FALSE) { HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); } } } CTRACE((tfp, "Ok, we're trying type=[%s]\n", NONNULL(I.type))); /* * Check for an unclosed TEXTAREA. */ if (me->inTEXTAREA) { if (LYBadHTML(me)) { LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag.\n"); } } /* * Check for an unclosed SELECT, try to close it if found. */ if (me->inSELECT) { CTRACE((tfp, "HTML: Missing SELECT end tag, faking it...\n")); if (me->sp->tag_number != HTML_SELECT) { SET_SKIP_STACK(HTML_SELECT); } HTML_end_element(me, HTML_SELECT, include); } /* * Handle the INPUT as for a FORM. - FM */ if (!(present && present[HTML_INPUT_NAME] && non_empty(value[HTML_INPUT_NAME]))) { I.name = ""; } else if (StrChr(value[HTML_INPUT_NAME], '&') == NULL) { I.name = value[HTML_INPUT_NAME]; } else { StrAllocCopy(I_name, value[HTML_INPUT_NAME]); UNESCAPE_FIELDNAME_TO_STD(&I_name); I.name = I_name; } if ((present && present[HTML_INPUT_ALT] && non_empty(value[HTML_INPUT_ALT]) && I.type && !strcasecomp(I.type, "image")) && !(present && present[HTML_INPUT_VALUE] && non_empty(value[HTML_INPUT_VALUE]))) { /* * This is a TYPE="image" using an ALT rather than VALUE * attribute to indicate the link string for text clients or * GUIs with image loading off, so set the flag to use that as * if it were a VALUE attribute. - FM */ UseALTasVALUE = TRUE; } if (verbose_img && !clickable_images && present && present[HTML_INPUT_SRC] && non_empty(value[HTML_INPUT_SRC]) && I.type && !strcasecomp(I.type, "image")) { ImageSrc = MakeNewImageValue(value); } else if (clickable_images == TRUE && present && present[HTML_INPUT_SRC] && non_empty(value[HTML_INPUT_SRC]) && I.type && !strcasecomp(I.type, "image")) { StrAllocCopy(href, value[HTML_INPUT_SRC]); /* * We have a TYPE="image" with a non-zero-length SRC attribute * and want clickable images. Make the SRC's value a link if * it's still not zero-length legitimizing it. - FM */ LYLegitimizeHREF(me, &href, TRUE, TRUE); if (*href) { if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ NULL, /* Tag */ href, /* Addresss */ (HTLinkType *) 0); /* Type */ HText_beginAnchor(me->text, me->inUnderline, me->CurrentA); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_START_CHAR); HTML_put_string(me, VERBOSE_IMG(value, HTML_INPUT_SRC, "[IMAGE]")); FREE(newtitle); if (me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, 0); HTML_put_character(me, '-'); HaveSRClink = TRUE; } FREE(href); } CTRACE((tfp, "2.Ok, we're trying type=[%s] (present=%p)\n", NONNULL(I.type), present)); /* text+file don't go in here */ if ((UseALTasVALUE == TRUE) || (present && present[HTML_INPUT_VALUE] && value[HTML_INPUT_VALUE] && (*value[HTML_INPUT_VALUE] || (I.type && (!strcasecomp(I.type, "checkbox") || !strcasecomp(I.type, "radio")))))) { /* * Convert any HTML entities or decimal escaping. - FM */ int CurrentCharSet = current_char_set; BOOL CurrentEightBitRaw = HTPassEightBitRaw; BOOLEAN CurrentUseDefaultRawMode = LYUseDefaultRawMode; HTCJKlang CurrentHTCJK = HTCJK; if (I.type && !strcasecomp(I.type, "hidden")) { me->HiddenValue = TRUE; current_char_set = LATIN1; /* Default ISO-Latin1 */ LYUseDefaultRawMode = TRUE; HTMLSetCharacterHandling(current_char_set); } CTRACE((tfp, "3.Ok, we're trying type=[%s]\n", NONNULL(I.type))); if (!I.type) me->UsePlainSpace = TRUE; else if (!strcasecomp(I.type, "text") || #ifdef USE_FILE_UPLOAD !strcasecomp(I.type, "file") || #endif !strcasecomp(I.type, "submit") || !strcasecomp(I.type, "image") || !strcasecomp(I.type, "reset")) { CTRACE((tfp, "normal field type: %s\n", NONNULL(I.type))); me->UsePlainSpace = TRUE; } StrAllocCopy(I_value, ((UseALTasVALUE == TRUE) ? value[HTML_INPUT_ALT] : value[HTML_INPUT_VALUE])); if (me->UsePlainSpace && !me->HiddenValue) { I.value_cs = current_char_set; } CTRACE((tfp, "4.Ok, we're trying type=[%s]\n", NONNULL(I.type))); TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value, ATTR_CS_IN, I.value_cs, (BOOL) (me->UsePlainSpace && !me->HiddenValue), me->UsePlainSpace, me->HiddenValue); I.value = I_value; if (me->UsePlainSpace == TRUE) { /* * Convert any newlines or tabs to spaces, and trim any * lead or trailing spaces. - FM */ LYReduceBlanks(I.value); } me->UsePlainSpace = FALSE; if (I.type && !strcasecomp(I.type, "hidden")) { me->HiddenValue = FALSE; current_char_set = CurrentCharSet; LYUseDefaultRawMode = CurrentUseDefaultRawMode; HTMLSetCharacterHandling(current_char_set); HTPassEightBitRaw = CurrentEightBitRaw; HTCJK = CurrentHTCJK; } } else if (HaveSRClink == TRUE) { /* * We put up an [IMAGE] link and '-' for a TYPE="image" and * didn't get a VALUE or ALT string, so fake a "Submit" value. * If we didn't put up a link, then HText_beginInput() will use * "[IMAGE]-Submit". - FM */ StrAllocCopy(I_value, "Submit"); I.value = I_value; } else if (ImageSrc) { /* [IMAGE]-Submit with verbose images and not clickable images. * Use ImageSrc if no other alt or value is supplied. --LE */ I.value = ImageSrc; } if (present && present[HTML_INPUT_READONLY]) I.readonly = YES; if (present && present[HTML_INPUT_CHECKED]) I.checked = YES; if (present && present[HTML_INPUT_SIZE] && non_empty(value[HTML_INPUT_SIZE])) I.size = atoi(value[HTML_INPUT_SIZE]); LimitValue(I.size, MAX_LINE); if (present && present[HTML_INPUT_MAXLENGTH] && non_empty(value[HTML_INPUT_MAXLENGTH])) I.maxlength = value[HTML_INPUT_MAXLENGTH]; if (present && present[HTML_INPUT_DISABLED]) I.disabled = YES; if (present && present[HTML_INPUT_ACCEPT_CHARSET]) { /* Not yet used. */ I.accept_cs = (value[HTML_INPUT_ACCEPT_CHARSET] ? value[HTML_INPUT_ACCEPT_CHARSET] : "UNKNOWN"); } if (present && present[HTML_INPUT_ALIGN] && /* Not yet used. */ non_empty(value[HTML_INPUT_ALIGN])) I.align = value[HTML_INPUT_ALIGN]; if (present && present[HTML_INPUT_CLASS] && /* Not yet used. */ non_empty(value[HTML_INPUT_CLASS])) I.iclass = value[HTML_INPUT_CLASS]; if (present && present[HTML_INPUT_ERROR] && /* Not yet used. */ non_empty(value[HTML_INPUT_ERROR])) I.error = value[HTML_INPUT_ERROR]; if (present && present[HTML_INPUT_HEIGHT] && /* Not yet used. */ non_empty(value[HTML_INPUT_HEIGHT])) I.height = value[HTML_INPUT_HEIGHT]; if (present && present[HTML_INPUT_WIDTH] && /* Not yet used. */ non_empty(value[HTML_INPUT_WIDTH])) I.width = value[HTML_INPUT_WIDTH]; if (present && present[HTML_INPUT_ID] && non_empty(value[HTML_INPUT_ID])) { I.id = value[HTML_INPUT_ID]; CHECK_ID(HTML_INPUT_ID); } if (present && present[HTML_INPUT_LANG] && /* Not yet used. */ non_empty(value[HTML_INPUT_LANG])) I.lang = value[HTML_INPUT_LANG]; if (present && present[HTML_INPUT_MD] && /* Not yet used. */ non_empty(value[HTML_INPUT_MD])) I.md = value[HTML_INPUT_MD]; chars = HText_beginInput(me->text, me->inUnderline, &I); CTRACE((tfp, "I.%s have %d chars, or something\n", NONNULL(I.type), chars)); /* * Submit and reset buttons have values which don't change, so * HText_beginInput() sets I.value to the string which should be * displayed, and we'll enter that instead of underscore * placeholders into the HText structure to see it instead of * underscores when dumping or printing. We also won't worry about * a wrap in PRE blocks, because the line editor never is invoked * for submit or reset buttons. - LE & FM */ if (I.type && (!strcasecomp(I.type, "submit") || !strcasecomp(I.type, "reset") || !strcasecomp(I.type, "image"))) IsSubmitOrReset = TRUE; if (I.type && chars == 3 && !strcasecomp(I.type, "radio")) { /* * Put a (_) placeholder, and one space (collapsible) before * the label that is expected to follow. - FM */ HTML_put_string(me, "(_)"); HText_endInput(me->text); chars = 0; me->in_word = YES; if (me->sp[0].tag_number != HTML_PRE && me->sp->style->freeFormat) { HTML_put_character(me, ' '); me->in_word = NO; } } else if (I.type && chars == 3 && !strcasecomp(I.type, "checkbox")) { /* * Put a [_] placeholder, and one space (collapsible) before * the label that is expected to follow. - FM */ HTML_put_string(me, "[_]"); HText_endInput(me->text); chars = 0; me->in_word = YES; if (me->sp[0].tag_number != HTML_PRE && me->sp->style->freeFormat) { HTML_put_character(me, ' '); me->in_word = NO; } } else if ((me->sp[0].tag_number == HTML_PRE || !me->sp->style->freeFormat) && chars > 6 && IsSubmitOrReset == FALSE) { /* * This is not a submit or reset button, and we are in a PRE * block with a field intended to exceed 6 character widths. * The code inadequately handles INPUT fields in PRE tags if * wraps occur (at the right margin) for the underscore * placeholders. We'll put up a minimum of 6 underscores, * since we should have wrapped artificially, above, if the * INPUT begins within 6 columns of the right margin, and if * any more would exceed the wrap column, we'll ignore them. * Note that if we somehow get tripped up and a wrap still does * occur before all 6 of the underscores are output, the * wrapped ones won't be treated as part of the editing window, * nor be highlighted when not editing (Yuk!). - FM */ for (i = 0; i < 6; i++) { HTML_put_character(me, '_'); chars--; } } CTRACE((tfp, "I.%s, %d\n", NONNULL(I.type), IsSubmitOrReset)); if (IsSubmitOrReset == FALSE) { /* * This is not a submit or reset button, so output the rest of * the underscore placeholders, if any more are needed. - FM */ if (chars > 0) { for (; chars > 0; chars--) HTML_put_character(me, '_'); HText_endInput(me->text); } } else { if (HTCJK == JAPANESE) { kcode = HText_getKcode(me->text); HText_updateKcode(me->text, kanji_code); specified_kcode = HText_getSpecifiedKcode(me->text); HText_updateSpecifiedKcode(me->text, kanji_code); } if (me->sp[0].tag_number == HTML_PRE || !me->sp->style->freeFormat) { /* * We have a submit or reset button in a PRE block, so * output the entire value from the markup. If it extends * to the right margin, it will wrap there, and only the * portion before that wrap will be hightlighted on screen * display (Yuk!) but we may as well show the rest of the * full value on the next or more lines. - FM */ while (I.value[i]) HTML_put_character(me, I.value[i++]); } else { /* * The submit or reset button is not in a PRE block. Note * that if a wrap occurs before outputting the entire * value, the wrapped portion will not be highlighted or * clearly indicated as part of the link for submission or * reset (Yuk!). We'll replace any spaces in the submit or * reset button value with nbsp, to promote a wrap at the * space we ensured would be present before the start of * the string, as when we use all underscores instead of * the INPUT's actual value, but we could still get a wrap * at the right margin, instead, if the value is greater * than a line width for the current style. Also, if chars * somehow ended up longer than the length of the actual * value (shouldn't have), we'll continue padding with nbsp * up to the length of chars. - FM */ for (i = 0; I.value[i]; i++) HTML_put_character(me, (char) (I.value[i] == ' ' ? HT_NON_BREAK_SPACE : I.value[i])); while (i++ < chars) HTML_put_character(me, HT_NON_BREAK_SPACE); } if (HTCJK == JAPANESE) { HText_updateKcode(me->text, kcode); HText_updateSpecifiedKcode(me->text, specified_kcode); } } if (chars != 0) { HText_endInput(me->text); } FREE(ImageSrc); FREE(I_value); FREE(I_name); } break; case HTML_TEXTAREA: /* * Set to know we are in a textarea. */ me->inTEXTAREA = TRUE; /* * Get ready for the value. */ HTChunkClear(&me->textarea); if (present && present[HTML_TEXTAREA_NAME] && value[HTML_TEXTAREA_NAME]) { StrAllocCopy(me->textarea_name, value[HTML_TEXTAREA_NAME]); me->textarea_name_cs = ATTR_CS_IN; if (StrChr(value[HTML_TEXTAREA_NAME], '&') != NULL) { UNESCAPE_FIELDNAME_TO_STD(&me->textarea_name); } } else { StrAllocCopy(me->textarea_name, ""); } if (present && present[HTML_TEXTAREA_ACCEPT_CHARSET]) { if (value[HTML_TEXTAREA_ACCEPT_CHARSET]) { StrAllocCopy(me->textarea_accept_cs, value[HTML_TEXTAREA_ACCEPT_CHARSET]); TRANSLATE_AND_UNESCAPE_TO_STD(&me->textarea_accept_cs); } else { StrAllocCopy(me->textarea_accept_cs, "UNKNOWN"); } } else { FREE(me->textarea_accept_cs); } if (present && present[HTML_TEXTAREA_COLS] && value[HTML_TEXTAREA_COLS] && isdigit(UCH(*value[HTML_TEXTAREA_COLS]))) { me->textarea_cols = atoi(value[HTML_TEXTAREA_COLS]); } else { int width; width = LYcolLimit - me->new_style->leftIndent - me->new_style->rightIndent; if (dump_output_immediately) /* don't waste too much for this */ width = HTMIN(width, DFT_TEXTAREA_COLS); if (width > 1 && (width - 1) * 6 < MAX_LINE - 3 - me->new_style->leftIndent - me->new_style->rightIndent) me->textarea_cols = width; else me->textarea_cols = DFT_TEXTAREA_COLS; } LimitValue(me->textarea_cols, MAX_TEXTAREA_COLS); if (present && present[HTML_TEXTAREA_ROWS] && value[HTML_TEXTAREA_ROWS] && isdigit(UCH(*value[HTML_TEXTAREA_ROWS]))) { me->textarea_rows = atoi(value[HTML_TEXTAREA_ROWS]); } else { me->textarea_rows = DFT_TEXTAREA_ROWS; } LimitValue(me->textarea_rows, MAX_TEXTAREA_ROWS); /* * Lynx treats disabled and readonly textarea's the same - * unmodifiable in either case. */ me->textarea_readonly = NO; if (present && present[HTML_TEXTAREA_READONLY]) me->textarea_readonly = YES; me->textarea_disabled = NO; if (present && present[HTML_TEXTAREA_DISABLED]) me->textarea_disabled = YES; if (present && present[HTML_TEXTAREA_ID] && non_empty(value[HTML_TEXTAREA_ID])) { StrAllocCopy(id_string, value[HTML_TEXTAREA_ID]); TRANSLATE_AND_UNESCAPE_TO_STD(&id_string); if ((id_string != '\0') && (ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ id_string, /* Tag */ NULL, /* Addresss */ (HTLinkType *) 0))) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); StrAllocCopy(me->textarea_id, id_string); } else { FREE(me->textarea_id); } FREE(id_string); } else { FREE(me->textarea_id); } break; case HTML_SELECT: /* * Check for an already open SELECT block. - FM */ if (me->inSELECT) { if (LYBadHTML(me)) { LYShowBadHTML("Bad HTML: SELECT start tag in SELECT element. Faking SELECT end tag. *****\n"); } if (me->sp->tag_number != HTML_SELECT) { SET_SKIP_STACK(HTML_SELECT); } HTML_end_element(me, HTML_SELECT, include); } /* * Start a new SELECT block. - FM */ LYHandleSELECT(me, present, (STRING2PTR) value, include, TRUE); break; case HTML_OPTION: { /* * An option is a special case of an input field. */ InputFieldData I; /* * Make sure we're in a select tag. */ if (!me->inSELECT) { if (LYBadHTML(me)) { LYShowBadHTML("Bad HTML: OPTION tag not within SELECT tag\n"); } /* * Too likely to cause a crash, so we'll ignore it. - FM */ break; } if (!me->first_option) { /* * Finish the data off. */ HTChunkTerminate(&me->option); /* * Finish the previous option @@@@@ */ HText_setLastOptionValue(me->text, me->option.data, me->LastOptionValue, MIDDLE_ORDER, me->LastOptionChecked, me->UCLYhndl, ATTR_CS_IN); } /* * If it's not a multiple option list and select popups are * enabled, then don't use the checkbox/button method, and don't * put anything on the screen yet. */ if (me->first_option || HTCurSelectGroupType == F_CHECKBOX_TYPE || LYSelectPopups == FALSE) { if (HTCurSelectGroupType == F_CHECKBOX_TYPE || LYSelectPopups == FALSE) { /* * Start a newline before each option. */ LYEnsureSingleSpace(me); } else { /* * Add option list designation character. */ HText_appendCharacter(me->text, '['); me->in_word = YES; } /* * Inititialize. */ memset(&I, 0, sizeof(I)); I.name_cs = -1; I.value_cs = current_char_set; I.type = "OPTION"; if ((present && present[HTML_OPTION_SELECTED]) || (me->first_option && LYSelectPopups == FALSE && HTCurSelectGroupType == F_RADIO_TYPE)) I.checked = YES; if (present && present[HTML_OPTION_VALUE] && value[HTML_OPTION_VALUE]) { /* * Convert any HTML entities or decimal escaping. - FM */ StrAllocCopy(I_value, value[HTML_OPTION_VALUE]); me->HiddenValue = TRUE; TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value, ATTR_CS_IN, ATTR_CS_IN, NO, me->UsePlainSpace, me->HiddenValue); I.value_cs = ATTR_CS_IN; me->HiddenValue = FALSE; I.value = I_value; } if (me->select_disabled || (0 && present && present[HTML_OPTION_DISABLED])) { /* 2009/5/25 - suppress check for "disabled" attribute * for Debian #525934 -TD */ I.disabled = YES; } if (present && present[HTML_OPTION_ID] && non_empty(value[HTML_OPTION_ID])) { if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor, /* Parent */ value[HTML_OPTION_ID], /* Tag */ NULL, /* Addresss */ 0)) != NULL) { /* Type */ HText_beginAnchor(me->text, me->inUnderline, ID_A); HText_endAnchor(me->text, 0); I.id = value[HTML_OPTION_ID]; } } HText_beginInput(me->text, me->inUnderline, &I); if (HTCurSelectGroupType == F_CHECKBOX_TYPE) { /* * Put a "[_]" placeholder, and one space (collapsible) * before the label that is expected to follow. - FM */ HText_appendCharacter(me->text, '['); HText_appendCharacter(me->text, '_'); HText_appendCharacter(me->text, ']'); HText_appendCharacter(me->text, ' '); HText_setLastChar(me->text, ' '); /* absorb white space */ me->in_word = NO; } else if (LYSelectPopups == FALSE) { /* * Put a "(_)" placeholder, and one space (collapsible) * before the label that is expected to follow. - FM */ HText_appendCharacter(me->text, '('); HText_appendCharacter(me->text, '_'); HText_appendCharacter(me->text, ')'); HText_appendCharacter(me->text, ' '); HText_setLastChar(me->text, ' '); /* absorb white space */ me->in_word = NO; } } /* * Get ready for the next value. */ HTChunkClear(&me->option); if ((present && present[HTML_OPTION_SELECTED]) || (me->first_option && LYSelectPopups == FALSE && HTCurSelectGroupType == F_RADIO_TYPE)) me->LastOptionChecked = TRUE; else me->LastOptionChecked = FALSE; me->first_option = FALSE; if (present && present[HTML_OPTION_VALUE] && value[HTML_OPTION_VALUE]) { if (!I_value) { /* * Convert any HTML entities or decimal escaping. - FM */ StrAllocCopy(I_value, value[HTML_OPTION_VALUE]); me->HiddenValue = TRUE; TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value, ATTR_CS_IN, ATTR_CS_IN, NO, me->UsePlainSpace, me->HiddenValue); me->HiddenValue = FALSE; } StrAllocCopy(me->LastOptionValue, I_value); } else { StrAllocCopy(me->LastOptionValue, me->option.data); } /* * If this is a popup option, print its option for use in selecting * option by number. - LE */ if (HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups && fields_are_numbered()) { char marker[8]; int opnum = HText_getOptionNum(me->text); if (opnum > 0 && opnum < 100000) { sprintf(marker, "(%d)", opnum); HTML_put_string(me, marker); for (i = (int) strlen(marker); i < 5; ++i) { HTML_put_character(me, '_'); } } } FREE(I_value); } break; case HTML_TABLE: /* * Not fully implemented. Just treat as a division with respect to any * ALIGN attribute, with a default of HT_LEFT, or leave as a PRE block * if we are presently in one. - FM * * Also notify simple table tracking code unless in a preformatted * section, or (currently) non-left alignment. * * If page author is using a TABLE within PRE, it's probably formatted * specifically to work well for Lynx without simple table tracking * code. Cancel tracking, it would only make things worse. - kw */ #ifdef EXP_NESTED_TABLES if (!nested_tables) #endif HText_cancelStbl(me->text); if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } if (me->Underline_Level > 0) { SET_SKIP_STACK(HTML_U); HTML_end_element(me, HTML_U, include); } me->inTABLE = TRUE; if (me->sp->style->id == ST_Preformatted) { UPDATE_STYLE; CHECK_ID(HTML_TABLE_ID); break; } if (me->Division_Level < (MAX_NESTING - 1)) { me->Division_Level++; } else { CTRACE((tfp, "HTML: ****** Maximum nesting of %d divisions/tables exceeded!\n", MAX_NESTING)); } if (present && present[HTML_TABLE_ALIGN] && non_empty(value[HTML_TABLE_ALIGN])) { if (!strcasecomp(value[HTML_TABLE_ALIGN], "center")) { if (no_table_center) { me->DivisionAlignments[me->Division_Level] = HT_LEFT; change_paragraph_style(me, styles[HTML_DLEFT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DLEFT]->alignment; } else { me->DivisionAlignments[me->Division_Level] = HT_CENTER; change_paragraph_style(me, styles[HTML_DCENTER]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DCENTER]->alignment; } stbl_align = HT_CENTER; } else if (!strcasecomp(value[HTML_TABLE_ALIGN], "right")) { me->DivisionAlignments[me->Division_Level] = HT_RIGHT; change_paragraph_style(me, styles[HTML_DRIGHT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DRIGHT]->alignment; stbl_align = HT_RIGHT; } else { me->DivisionAlignments[me->Division_Level] = HT_LEFT; change_paragraph_style(me, styles[HTML_DLEFT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DLEFT]->alignment; if (!strcasecomp(value[HTML_TABLE_ALIGN], "left") || !strcasecomp(value[HTML_TABLE_ALIGN], "justify")) stbl_align = HT_LEFT; } } else { me->DivisionAlignments[me->Division_Level] = HT_LEFT; change_paragraph_style(me, styles[HTML_DLEFT]); UPDATE_STYLE; me->current_default_alignment = styles[HTML_DLEFT]->alignment; /* stbl_align remains HT_ALIGN_NONE */ } CHECK_ID(HTML_TABLE_ID); HText_startStblTABLE(me->text, stbl_align); break; case HTML_TR: /* * Not fully implemented. Just start a new row, if needed, act on an * ALIGN attribute if present, and check for an ID link. - FM * Also notify simple table tracking code. - kw */ if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } if (me->Underline_Level > 0) { SET_SKIP_STACK(HTML_U); HTML_end_element(me, HTML_U, include); } UPDATE_STYLE; if (!HText_LastLineEmpty(me->text, FALSE)) { HText_setLastChar(me->text, ' '); /* absorb white space */ HText_appendCharacter(me->text, '\r'); } me->in_word = NO; if (me->sp->style->id == ST_Preformatted) { CHECK_ID(HTML_TR_ID); me->inP = FALSE; /* HText_cancelStbl(me->text); seems unnecessary here - kw */ break; } if (LYoverride_default_alignment(me)) { me->sp->style->alignment = styles[me->sp[0].tag_number]->alignment; } else if (me->List_Nesting_Level >= 0 || ((me->Division_Level < 0) && (me->sp->style->id == ST_Normal || me->sp->style->id == ST_Preformatted))) { me->sp->style->alignment = HT_LEFT; } else { me->sp->style->alignment = (short) me->current_default_alignment; } if (present && present[HTML_TR_ALIGN] && value[HTML_TR_ALIGN]) { if (!strcasecomp(value[HTML_TR_ALIGN], "center") && !(me->List_Nesting_Level >= 0 && !me->inP)) { if (no_table_center) me->sp->style->alignment = HT_LEFT; else me->sp->style->alignment = HT_CENTER; stbl_align = HT_CENTER; } else if (!strcasecomp(value[HTML_TR_ALIGN], "right") && !(me->List_Nesting_Level >= 0 && !me->inP)) { me->sp->style->alignment = HT_RIGHT; stbl_align = HT_RIGHT; } else if (!strcasecomp(value[HTML_TR_ALIGN], "left") || !strcasecomp(value[HTML_TR_ALIGN], "justify")) { me->sp->style->alignment = HT_LEFT; stbl_align = HT_LEFT; } } CHECK_ID(HTML_TR_ID); me->inP = FALSE; HText_startStblTR(me->text, stbl_align); break; case HTML_THEAD: case HTML_TFOOT: case HTML_TBODY: HText_endStblTR(me->text); /* * Not fully implemented. Just check for an ID link. - FM */ if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } if (me->Underline_Level > 0) { SET_SKIP_STACK(HTML_U); HTML_end_element(me, HTML_U, include); } UPDATE_STYLE; if (me->inTABLE) { if (present && present[HTML_TR_ALIGN] && value[HTML_TR_ALIGN]) { if (!strcasecomp(value[HTML_TR_ALIGN], "center")) { stbl_align = HT_CENTER; } else if (!strcasecomp(value[HTML_TR_ALIGN], "right")) { stbl_align = HT_RIGHT; } else if (!strcasecomp(value[HTML_TR_ALIGN], "left") || !strcasecomp(value[HTML_TR_ALIGN], "justify")) { stbl_align = HT_LEFT; } } HText_startStblRowGroup(me->text, stbl_align); } CHECK_ID(HTML_TR_ID); break; case HTML_COL: case HTML_COLGROUP: /* * Not fully implemented. Just check for an ID link. - FM */ if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } if (me->Underline_Level > 0) { SET_SKIP_STACK(HTML_U); HTML_end_element(me, HTML_U, include); } UPDATE_STYLE; if (me->inTABLE) { int span = 1; if (present && present[HTML_COL_SPAN] && value[HTML_COL_SPAN] && isdigit(UCH(*value[HTML_COL_SPAN]))) span = atoi(value[HTML_COL_SPAN]); if (present && present[HTML_COL_ALIGN] && value[HTML_COL_ALIGN]) { if (!strcasecomp(value[HTML_COL_ALIGN], "center")) { stbl_align = HT_CENTER; } else if (!strcasecomp(value[HTML_COL_ALIGN], "right")) { stbl_align = HT_RIGHT; } else if (!strcasecomp(value[HTML_COL_ALIGN], "left") || !strcasecomp(value[HTML_COL_ALIGN], "justify")) { stbl_align = HT_LEFT; } } HText_startStblCOL(me->text, span, stbl_align, (BOOL) (ElementNumber == HTML_COLGROUP)); } CHECK_ID(HTML_COL_ID); break; case HTML_TH: case HTML_TD: if (me->inA) { SET_SKIP_STACK(HTML_A); HTML_end_element(me, HTML_A, include); } if (me->Underline_Level > 0) { SET_SKIP_STACK(HTML_U); HTML_end_element(me, HTML_U, include); } UPDATE_STYLE; CHECK_ID(HTML_TD_ID); /* * Not fully implemented. Just add a collapsible space and break - FM * Also notify simple table tracking code. - kw */ HTML_put_character(me, ' '); { int colspan = 1, rowspan = 1; if (present && present[HTML_TD_COLSPAN] && value[HTML_TD_COLSPAN] && isdigit(UCH(*value[HTML_TD_COLSPAN]))) colspan = atoi(value[HTML_TD_COLSPAN]); if (present && present[HTML_TD_ROWSPAN] && value[HTML_TD_ROWSPAN] && isdigit(UCH(*value[HTML_TD_ROWSPAN]))) rowspan = atoi(value[HTML_TD_ROWSPAN]); if (present && present[HTML_TD_ALIGN] && value[HTML_TD_ALIGN]) { if (!strcasecomp(value[HTML_TD_ALIGN], "center")) { stbl_align = HT_CENTER; } else if (!strcasecomp(value[HTML_TD_ALIGN], "right")) { stbl_align = HT_RIGHT; } else if (!strcasecomp(value[HTML_TD_ALIGN], "left") || !strcasecomp(value[HTML_TD_ALIGN], "justify")) { stbl_align = HT_LEFT; } } HText_startStblTD(me->text, colspan, rowspan, stbl_align, (BOOL) (ElementNumber == HTML_TH)); } me->in_word = NO; break; case HTML_MATH: /* * We're getting it as Literal text, which, until we can process it, * we'll display as is, within brackets to alert the user. - FM */ HTChunkClear(&me->math); CHECK_ID(HTML_GEN_ID); break; default: break; } /* end switch */ if (ElementNumber >= HTML_ELEMENTS || HTML_dtd.tags[ElementNumber].contents != SGML_EMPTY) { if (me->skip_stack > 0) { CTRACE((tfp, "HTML:begin_element: internal call (level %d), leaving on stack - `%s'\n", me->skip_stack, NONNULL(GetHTStyleName(me->sp->style)))); me->skip_stack--; return status; } if (me->sp == me->stack) { if (me->stack_overrun == FALSE) { HTAlert(HTML_STACK_OVERRUN); CTRACE((tfp, "HTML: ****** Maximum nesting of %d tags exceeded!\n", MAX_NESTING)); me->stack_overrun = TRUE; } return HT_ERROR; } CTRACE((tfp, "HTML:begin_element[%d]: adding style to stack - %s (%s)\n", (int) STACKLEVEL(me), NONNULL(GetHTStyleName(me->new_style)), HTML_dtd.tags[ElementNumber].name)); (me->sp)--; me->sp[0].style = me->new_style; /* Stack new style */ me->sp[0].tag_number = ElementNumber; #ifdef USE_JUSTIFY_ELTS if (wait_for_this_stacked_elt < 0 && HTML_dtd.tags[ElementNumber].can_justify == FALSE) wait_for_this_stacked_elt = (int) (me->stack - me->sp) + MAX_NESTING; #endif } #ifdef USE_JUSTIFY_ELTS if (in_DT && ElementNumber == HTML_DD) in_DT = FALSE; else if (ElementNumber == HTML_DT) in_DT = TRUE; #endif #if defined(USE_COLOR_STYLE) /* end really empty tags straight away */ if (ReallyEmptyTagNum(element_number)) { CTRACE2(TRACE_STYLE, (tfp, "STYLE.begin_element:ending \"EMPTY\" element style\n")); HText_characterStyle(me->text, HCODE_TO_STACK_OFF(hcode), STACK_OFF); # if !OMIT_SCN_KEEPING FastTrimColorClass(HTML_dtd.tags[element_number].name, HTML_dtd.tags[element_number].name_len, Style_className, &Style_className_end, &hcode); # endif } #endif /* USE_COLOR_STYLE */ return status; } /* End Element * ----------- * * When we end an element, the style must be returned to that * in effect before that element. Note that anchors (etc?) * don't have an associated style, so that we must scan down the * stack for an element with a defined style. (In fact, the styles * should be linked to the whole stack not just the top one.) * TBL 921119 */ static int HTML_end_element(HTStructured * me, int element_number, char **include) { static char empty[1]; int i = 0; int status = HT_OK; char *temp = NULL, *cp = NULL; BOOL BreakFlag = FALSE; BOOL intern_flag = FALSE; #ifdef USE_COLOR_STYLE BOOL skip_stack_requested = FALSE; #endif EMIT_IFDEF_USE_JUSTIFY_ELTS(BOOL reached_awaited_stacked_elt = FALSE); #ifdef USE_PRETTYSRC if (psrc_view && !sgml_in_psrc_was_initialized) { if (!psrc_nested_call) { HTTag *tag = &HTML_dtd.tags[element_number]; char buf[200]; int tag_charset = 0; psrc_nested_call = TRUE; PSRCSTART(abracket); PUTS("name); else { LYStrNCpy(buf, tag->name, sizeof(buf) - 1); LYLowerCase(buf); PUTS(buf); } PSRCSTOP(tag); PSRCSTART(abracket); PUTC('>'); PSRCSTOP(abracket); psrc_nested_call = FALSE; return HT_OK; } /*fall through */ } #endif if ((me->sp >= (me->stack + MAX_NESTING - 1) || element_number != me->sp[0].tag_number) && HTML_dtd.tags[element_number].contents != SGML_EMPTY) { CTRACE((tfp, "HTML: end of element %s when expecting end of %s\n", HTML_dtd.tags[element_number].name, (me->sp == me->stack + MAX_NESTING - 1) ? "none" : (me->sp->tag_number < 0) ? "*invalid tag*" : (me->sp->tag_number >= HTML_ELEMENTS) ? "special tag" : HTML_dtd.tags[me->sp->tag_number].name)); } /* * If we're seeking MAPs, skip everything that's not a MAP or AREA tag. - * FM */ if (LYMapsOnly) { if (!(element_number == HTML_MAP || element_number == HTML_AREA || element_number == HTML_OBJECT)) { return HT_OK; } } /* * Pop state off stack if we didn't declare the element SGML_EMPTY in * HTMLDTD.c. - FM & KW */ if (HTML_dtd.tags[element_number].contents != SGML_EMPTY) { #ifdef USE_COLOR_STYLE skip_stack_requested = (BOOL) (me->skip_stack > 0); #endif if ((element_number != me->sp[0].tag_number) && me->skip_stack <= 0 && HTML_dtd.tags[HTML_LH].contents != SGML_EMPTY && (me->sp[0].tag_number == HTML_UL || me->sp[0].tag_number == HTML_OL || me->sp[0].tag_number == HTML_MENU || me->sp[0].tag_number == HTML_DIR || me->sp[0].tag_number == HTML_LI) && (element_number == HTML_H1 || element_number == HTML_H2 || element_number == HTML_H3 || element_number == HTML_H4 || element_number == HTML_H5 || element_number == HTML_H6)) { /* * Set the break flag if we're popping a dummy HTML_LH substituted * for an HTML_H# encountered in a list. */ BreakFlag = TRUE; } if (me->skip_stack == 0 && element_number == HTML_OBJECT && me->sp[0].tag_number == HTML_OBJECT_M && (me->sp < (me->stack + MAX_NESTING - 1))) me->sp[0].tag_number = HTML_OBJECT; if (me->skip_stack > 0) { CTRACE2(TRACE_STYLE, (tfp, "HTML:end_element: Internal call (level %d), leaving on stack - %s\n", me->skip_stack, NONNULL(GetHTStyleName(me->sp->style)))); me->skip_stack--; } else if (element_number == HTML_OBJECT && me->sp[0].tag_number != HTML_OBJECT && me->sp[0].tag_number != HTML_OBJECT_M && me->objects_mixed_open > 0 && !(me->objects_figged_open > 0 && me->sp[0].tag_number == HTML_FIG)) { /* * Ignore non-corresponding OBJECT tags that we didn't push because * the SGML parser was supposed to go on parsing the contents * non-literally. - kw */ CTRACE2(TRACE_STYLE, (tfp, "HTML:end_element[%d]: %s (level %d), %s - %s\n", (int) STACKLEVEL(me), "Special OBJECT handling", me->objects_mixed_open, "leaving on stack", NONNULL(GetHTStyleName(me->sp->style)))); me->objects_mixed_open--; } else if (me->stack_overrun == TRUE && element_number != me->sp[0].tag_number) { /* * Ignore non-corresponding tags if we had a stack overrun. This * is not a completely fail-safe strategy for protection against * any seriously adverse consequences of a stack overrun, and the * rendering of the document will not be as intended, but we expect * overruns to be rare, and this should offer reasonable protection * against crashes if an overrun does occur. - FM */ return HT_OK; /* let's pretend... */ } else if (element_number == HTML_SELECT && me->sp[0].tag_number != HTML_SELECT) { /* * Ignore non-corresponding SELECT tags, since we probably popped * it and closed the SELECT block to deal with markup which amounts * to a nested SELECT, or an out of order FORM end tag. - FM */ return HT_OK; } else if ((element_number != me->sp[0].tag_number) && HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY && (me->sp[0].tag_number == HTML_UL || me->sp[0].tag_number == HTML_OL || me->sp[0].tag_number == HTML_MENU || me->sp[0].tag_number == HTML_DIR || me->sp[0].tag_number == HTML_LI) && (element_number == HTML_H1 || element_number == HTML_H2 || element_number == HTML_H3 || element_number == HTML_H4 || element_number == HTML_H5 || element_number == HTML_H6)) { /* * It's an H# for which we substituted an HTML_LH, which we've * declared as SGML_EMPTY, so just return. - FM */ return HT_OK; } else if (me->sp < (me->stack + MAX_NESTING - 1)) { #ifdef USE_JUSTIFY_ELTS if (wait_for_this_stacked_elt == me->stack - me->sp + MAX_NESTING) reached_awaited_stacked_elt = TRUE; #endif if (element_number == HTML_OBJECT) { if (me->sp[0].tag_number == HTML_FIG && me->objects_figged_open > 0) { /* * It's an OBJECT for which we substituted a FIG, so pop * the FIG and pretend that's what we are being called for. * - kw */ CTRACE2(TRACE_STYLE, (tfp, "HTML:end_element[%d]: %s (level %d), %s - %s\n", (int) STACKLEVEL(me), "Special OBJECT->FIG handling", me->objects_figged_open, "treating as end FIG", NONNULL(GetHTStyleName(me->sp->style)))); me->objects_figged_open--; element_number = HTML_FIG; } } (me->sp)++; CTRACE2(TRACE_STYLE, (tfp, "HTML:end_element[%d]: Popped style off stack - %s\n", (int) STACKLEVEL(me), NONNULL(GetHTStyleName(me->sp->style)))); } else { CTRACE2(TRACE_STYLE, (tfp, "Stack underflow error! Tried to pop off more styles than exist in stack\n")); } } if (BreakFlag == TRUE) { #ifdef USE_JUSTIFY_ELTS if (reached_awaited_stacked_elt) wait_for_this_stacked_elt = -1; #endif return HT_OK; /* let's pretend... */ } /* * Check for unclosed TEXTAREA. - FM */ if (me->inTEXTAREA && element_number != HTML_TEXTAREA) { if (LYBadHTML(me)) { LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag\n"); } } if (!me->text && !LYMapsOnly) { UPDATE_STYLE; } /* * Handle the end tag. - FM */ switch (element_number) { case HTML_HTML: if (me->inA || me->inSELECT || me->inTEXTAREA) { if (LYBadHTML(me)) { char *msg = NULL; HTSprintf0(&msg, "Bad HTML: %s%s%s%s%s not closed before HTML end tag *****\n", me->inSELECT ? "SELECT" : "", (me->inSELECT && me->inTEXTAREA) ? ", " : "", me->inTEXTAREA ? "TEXTAREA" : "", (((me->inSELECT || me->inTEXTAREA) && me->inA) ? ", " : ""), me->inA ? "A" : ""); LYShowBadHTML(msg); FREE(msg); } } break; case HTML_HEAD: if (me->inBASE && (LYIsUIPage3(me->node_anchor->address, UIP_LIST_PAGE, 0) || LYIsUIPage3(me->node_anchor->address, UIP_ADDRLIST_PAGE, 0))) { /* If we are parsing the List Page, and have a BASE after we are * done with the HEAD element, propagate it back to the node_anchor * object. The base should have been inserted by showlist() to * record what document the List Page is about, and other functions * may later look for it in the anchor. - kw */ StrAllocCopy(me->node_anchor->content_base, me->base_href); } if (HText_hasToolbar(me->text)) HText_appendParagraph(me->text); break; case HTML_TITLE: HTChunkTerminate(&me->title); HTAnchor_setTitle(me->node_anchor, me->title.data); HTChunkClear(&me->title); /* * Check if it's a bookmark file, and if so, and multiple bookmark * support is on, or it's off but this isn't the default bookmark file * (e.g., because it was on before, and this is another bookmark file * that has been retrieved as a previous document), insert the current * description string and filepath for it. We pass the strings back to * the SGML parser so that any 8 bit or multibyte/CJK characters will * be handled by the parser's state and charset routines. - FM */ if (non_empty(me->node_anchor->bookmark)) { if ((LYMultiBookmarks != MBM_OFF) || (non_empty(bookmark_page) && strcmp(me->node_anchor->bookmark, bookmark_page))) { if (!include) include = &me->xinclude; for (i = 0; i <= MBM_V_MAXFILES; i++) { if (MBM_A_subbookmark[i] && !strcmp(MBM_A_subbookmark[i], me->node_anchor->bookmark)) { StrAllocCat(*include, "

    "); StrAllocCat(*include, gettext("Description:")); StrAllocCat(*include, " "); StrAllocCopy(temp, ((MBM_A_subdescript[i] && *MBM_A_subdescript[i]) ? MBM_A_subdescript[i] : gettext("(none)"))); LYEntify(&temp, TRUE); StrAllocCat(*include, temp); StrAllocCat(*include, "
       "); StrAllocCat(*include, gettext("Filepath:")); StrAllocCat(*include, " "); StrAllocCopy(temp, ((MBM_A_subbookmark[i] && *MBM_A_subbookmark[i]) ? MBM_A_subbookmark[i] : gettext("(unknown)"))); LYEntify(&temp, TRUE); StrAllocCat(*include, temp); FREE(temp); StrAllocCat(*include, "

    "); break; } } } } break; case HTML_STYLE: /* * We're getting it as Literal text, which, for now, we'll just ignore. * - FM */ HTChunkTerminate(&me->style_block); CTRACE2(TRACE_STYLE, (tfp, "HTML: STYLE content =\n%s\n", me->style_block.data)); HTChunkClear(&me->style_block); break; case HTML_SCRIPT: /* * We're getting it as Literal text, which, for now, we'll just ignore. * - FM */ HTChunkTerminate(&me->script); CTRACE((tfp, "HTML: SCRIPT content =\n%s\n", me->script.data)); HTChunkClear(&me->script); break; case HTML_BODY: if (me->inA || me->inSELECT || me->inTEXTAREA) { if (LYBadHTML(me)) { char *msg = NULL; HTSprintf0(&msg, "Bad HTML: %s%s%s%s%s not closed before BODY end tag *****\n", me->inSELECT ? "SELECT" : "", (me->inSELECT && me->inTEXTAREA) ? ", " : "", me->inTEXTAREA ? "TEXTAREA" : "", (((me->inSELECT || me->inTEXTAREA) && me->inA) ? ", " : ""), me->inA ? "A" : ""); LYShowBadHTML(msg); FREE(msg); } } break; case HTML_FRAMESET: change_paragraph_style(me, me->sp->style); /* Often won't really change */ break; case HTML_NOFRAMES: case HTML_IFRAME: LYEnsureDoubleSpace(me); LYResetParagraphAlignment(me); change_paragraph_style(me, me->sp->style); /* Often won't really change */ break; case HTML_BANNER: case HTML_MARQUEE: case HTML_BLOCKQUOTE: case HTML_BQ: case HTML_ADDRESS: /* * Set flag to know that style has ended. Fall through. i_prior_style = -1; */ change_paragraph_style(me, me->sp->style); UPDATE_STYLE; if (me->sp->tag_number == element_number) LYEnsureDoubleSpace(me); if (me->List_Nesting_Level >= 0) HText_NegateLineOne(me->text); break; case HTML_CENTER: case HTML_DIV: if (me->Division_Level >= 0) me->Division_Level--; if (me->Division_Level >= 0) { if (me->sp->style->alignment != me->DivisionAlignments[me->Division_Level]) { if (me->inP) LYEnsureSingleSpace(me); me->sp->style->alignment = me->DivisionAlignments[me->Division_Level]; } } change_paragraph_style(me, me->sp->style); if (me->style_change) { actually_set_style(me); if (me->List_Nesting_Level >= 0) HText_NegateLineOne(me->text); } else if (me->inP) LYEnsureSingleSpace(me); me->current_default_alignment = me->sp->style->alignment; break; case HTML_H1: /* header styles */ case HTML_H2: case HTML_H3: case HTML_H4: case HTML_H5: case HTML_H6: if (me->Division_Level >= 0) { me->sp->style->alignment = me->DivisionAlignments[me->Division_Level]; } else if (me->sp->style->id == ST_HeadingCenter || me->sp->style->id == ST_Heading1) { me->sp->style->alignment = HT_CENTER; } else if (me->sp->style->id == ST_HeadingRight) { me->sp->style->alignment = HT_RIGHT; } else { me->sp->style->alignment = HT_LEFT; } change_paragraph_style(me, me->sp->style); UPDATE_STYLE; if (styles[element_number]->font & HT_BOLD) { if (me->inBoldA == FALSE && me->inBoldH == TRUE) { HText_appendCharacter(me->text, LY_BOLD_END_CHAR); } me->inBoldH = FALSE; } if (me->List_Nesting_Level >= 0) HText_NegateLineOne(me->text); if (me->Underline_Level > 0 && me->inUnderline == FALSE) { HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); me->inUnderline = TRUE; } break; case HTML_P: LYHandlePlike(me, (const BOOL *) 0, (STRING2PTR) 0, include, 0, FALSE); break; case HTML_FONT: me->inFONT = FALSE; break; case HTML_B: /* Physical character highlighting */ case HTML_BLINK: case HTML_I: case HTML_U: case HTML_CITE: /* Logical character highlighting */ case HTML_EM: case HTML_STRONG: /* * Ignore any emphasis end tags if the Underline_Level is not set. - * FM */ if (me->Underline_Level <= 0) break; /* * Adjust the Underline level counter, and turn off underlining if * appropriate. - FM */ me->Underline_Level--; if (me->inUnderline && me->Underline_Level < 1) { HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); me->inUnderline = FALSE; CTRACE((tfp, "Ending underline\n")); } else { CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level)); } break; case HTML_ABBR: /* Miscellaneous character containers */ case HTML_ACRONYM: case HTML_AU: case HTML_AUTHOR: case HTML_BIG: case HTML_CODE: case HTML_DFN: case HTML_KBD: case HTML_SAMP: case HTML_SMALL: case HTML_SUP: case HTML_TT: case HTML_VAR: break; case HTML_SUB: HText_appendCharacter(me->text, ']'); break; case HTML_DEL: case HTML_S: case HTML_STRIKE: HTML_put_character(me, ' '); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, ":DEL]"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); me->in_word = NO; break; case HTML_INS: HTML_put_character(me, ' '); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); HTML_put_string(me, ":INS]"); if (me->inUnderline == FALSE) HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR); HTML_put_character(me, ' '); me->in_word = NO; break; case HTML_Q: if (me->Quote_Level > 0) me->Quote_Level--; /* * Should check LANG and/or DIR attributes, and the * me->node_anchor->charset and/or yet to be added structure elements, * to determine whether we should use chevrons, but for now we'll * always use double- or single-quotes. - FM */ if (!(me->Quote_Level & 1)) HTML_put_character(me, '"'); else HTML_put_character(me, '\''); break; case HTML_PRE: /* Formatted text */ /* * Set to know that we are no longer in a PRE block. */ HText_appendCharacter(me->text, '\n'); me->inPRE = FALSE; /* FALLTHRU */ case HTML_LISTING: /* Literal text */ /* FALLTHRU */ case HTML_XMP: /* FALLTHRU */ case HTML_PLAINTEXT: if (me->comment_start) HText_appendText(me->text, me->comment_start); change_paragraph_style(me, me->sp->style); /* Often won't really change */ if (me->List_Nesting_Level >= 0) { UPDATE_STYLE; HText_NegateLineOne(me->text); } break; case HTML_NOTE: case HTML_FN: change_paragraph_style(me, me->sp->style); /* Often won't really change */ UPDATE_STYLE; if (me->sp->tag_number == element_number) LYEnsureDoubleSpace(me); if (me->List_Nesting_Level >= 0) HText_NegateLineOne(me->text); me->inLABEL = FALSE; break; case HTML_OL: me->OL_Counter[me->List_Nesting_Level < 11 ? me->List_Nesting_Level : 11] = OL_VOID; /* FALLTHRU */ case HTML_DL: /* FALLTHRU */ case HTML_UL: /* FALLTHRU */ case HTML_MENU: /* FALLTHRU */ case HTML_DIR: me->List_Nesting_Level--; CTRACE((tfp, "HTML_end_element: Reducing List Nesting Level to %d\n", me->List_Nesting_Level)); #ifdef USE_JUSTIFY_ELTS if (element_number == HTML_DL) in_DT = FALSE; /*close the term that was without definition. */ #endif change_paragraph_style(me, me->sp->style); /* Often won't really change */ UPDATE_STYLE; if (me->List_Nesting_Level >= 0) LYEnsureSingleSpace(me); break; case HTML_SPAN: /* * Should undo anything we did based on LANG and/or DIR attributes, and * the me->node_anchor->charset and/or yet to be added structure * elements. - FM */ break; case HTML_BDO: /* * Should undo anything we did based on DIR (and/or LANG) attributes, * and the me->node_anchor->charset and/or yet to be added structure * elements. - FM */ break; case HTML_A: /* * Ignore any spurious A end tags. - FM */ if (me->inA == FALSE) break; /* * Set to know that we are no longer in an anchor. */ me->inA = FALSE; #ifdef MARK_HIDDEN_LINKS if (non_empty(hidden_link_marker) && HText_isAnchorBlank(me->text, me->CurrentANum)) { HText_appendText(me->text, hidden_link_marker); } #endif UPDATE_STYLE; if (me->inBoldA == TRUE && me->inBoldH == FALSE) HText_appendCharacter(me->text, LY_BOLD_END_CHAR); HText_endAnchor(me->text, me->CurrentANum); me->CurrentANum = 0; me->inBoldA = FALSE; if (me->Underline_Level > 0 && me->inUnderline == FALSE) { HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR); me->inUnderline = TRUE; } break; case HTML_MAP: FREE(me->map_address); break; case HTML_BODYTEXT: /* * We may need to look at this someday to deal with OBJECTs optimally, * but just ignore it for now. - FM */ change_paragraph_style(me, me->sp->style); /* Often won't really change */ break; case HTML_TEXTFLOW: /* * We may need to look at this someday to deal with APPLETs optimally, * but just ignore it for now. - FM */ change_paragraph_style(me, me->sp->style); /* Often won't really change */ break; case HTML_FIG: LYHandleFIG(me, NULL, NULL, 0, 0, NULL, NULL, NO, FALSE, &intern_flag); break; case HTML_OBJECT: /* * Finish the data off. */ { int s = 0, e = 0; char *start = NULL, *first_end = NULL, *last_end = NULL; char *first_map = NULL, *last_map = NULL; BOOL have_param = FALSE; char *data = NULL; HTChunkTerminate(&me->object); data = me->object.data; while ((cp = StrChr(data, '<')) != NULL) { /* * Look for nested OBJECTs. This procedure could get tripped * up if invalid comments are present in the content, or if an * OBJECT end tag is present in a quoted attribute. - FM */ if (!StrNCmp(cp, "\n\n

    \n

      \n", gettext("\ You can delete links using the remove bookmark command. It is usually\n\ the 'R' key but may have been remapped by you or your system\n\ administrator."), gettext("\ This file also may be edited with a standard text editor to delete\n\ outdated or invalid links, or to change their order."), gettext("\ Note: if you edit this file manually\n\ you should not change the format within the lines\n\ or add other HTML markup.\n\ Make sure any bookmark link is saved as a single line.")); #endif /* _WINDOWS */ } /* * Add the bookmark link, in Mosaic hotlist or Lynx format. - FM */ if (is_mosaic_hotlist) { time_t NowTime = time(NULL); char *TimeString = (char *) ctime(&NowTime); /* * TimeString has a \n at the end. */ fprintf(fp, "%s %s%s\n", Address, TimeString, Title); } else { fprintf(fp, "
    1. %s\n", Address, Title); } LYCloseOutput(fp); SetDefaultMode(O_BINARY); /* * If this is a cached bookmark file, set nocache for it so we'll see the * new bookmark link when that cache is retrieved. - FM */ if (!first_time && nhist > 0 && bookmark_URL) { for (i = 0; i < nhist; i++) { if (HDOC(i).bookmark && !strcmp(HDOC(i).address, bookmark_URL)) { WWWDoc.address = HDOC(i).address; WWWDoc.post_data = NULL; WWWDoc.post_content_type = NULL; WWWDoc.bookmark = HDOC(i).bookmark; WWWDoc.isHEAD = FALSE; WWWDoc.safe = FALSE; tmpanchor = HTAnchor_findAddress(&WWWDoc); if ((text = (HText *) HTAnchor_document(tmpanchor)) != NULL) { HText_setNoCache(text); } break; } } } /* * Clean up and report success. */ BStrFree(string_data); BStrFree(tmp_data); FREE(Title); FREE(Address); FREE(bookmark_URL); LYMBM_statusline(OPERATION_DONE); LYSleepMsg(); } /* * Remove a link from a bookmark file. The calling function is expected to * have used get_filename_link(), pass us the link number as cur, the * MBM_A_subbookmark[] string as cur_bookmark_page, and to have set up no_cache * itself. - FM */ void remove_bookmark_link(int cur, char *cur_bookmark_page) { FILE *fp, *nfp; char *buf = NULL; int n; #ifdef VMS char filename_buffer[NAM$C_MAXRSS + 12]; char newfile[NAM$C_MAXRSS + 12]; #define keep_tempfile FALSE #else char filename_buffer[LY_MAXPATH]; char newfile[LY_MAXPATH]; BOOLEAN keep_tempfile = FALSE; #ifdef UNIX struct stat stat_buf; BOOLEAN regular = FALSE; #endif /* UNIX */ #endif /* VMS */ char homepath[LY_MAXPATH]; CTRACE((tfp, "remove_bookmark_link: deleting link number: %d\n", cur)); if (!cur_bookmark_page) return; LYAddPathToHome(filename_buffer, sizeof(filename_buffer), cur_bookmark_page); CTRACE((tfp, "\nremove_bookmark_link: SEEKING %s\n AS %s\n\n", cur_bookmark_page, filename_buffer)); if ((fp = fopen(filename_buffer, TXT_R)) == NULL) { HTAlert(BOOKMARK_OPEN_FAILED_FOR_DEL); return; } LYAddPathToHome(homepath, sizeof(homepath), ""); if ((nfp = LYOpenScratch(newfile, homepath)) == 0) { LYCloseInput(fp); HTAlert(BOOKSCRA_OPEN_FAILED_FOR_DEL); return; } #ifdef UNIX /* * Explicitly preserve bookmark file mode on Unix. - DSL */ if (stat(filename_buffer, &stat_buf) == 0) { regular = (BOOLEAN) (S_ISREG(stat_buf.st_mode) && stat_buf.st_nlink == 1); (void) chmod(newfile, HIDE_CHMOD); if ((nfp = LYReopenTemp(newfile)) == NULL) { (void) LYCloseInput(fp); HTAlert(BOOKTEMP_REOPEN_FAIL_FOR_DEL); return; } } #endif /* UNIX */ if (is_mosaic_hotlist) { int del_line = cur * 2; /* two lines per entry */ n = -3; /* skip past cookie and name lines */ while (LYSafeGets(&buf, fp) != NULL) { n++; if (n == del_line || n == del_line + 1) continue; /* remove two lines */ if (fputs(buf, nfp) == EOF) goto failure; } } else { char *cp; BOOLEAN retain; int seen; n = -1; while (LYSafeGets(&buf, fp) != NULL) { int keep_ol = FALSE; retain = TRUE; seen = 0; cp = buf; if ((cur == 0) && LYstrstr(cp, "
      1. ")) keep_ol = TRUE; /* Do not erase, this corrects a bug in an older version */ while (n < cur && (cp = LYstrstr(cp, "") || LYstrstr((cp + 1), "\n"); retain = FALSE; } cp += 8; } if (retain && fputs(buf, nfp) == EOF) goto failure; } } FREE(buf); CTRACE((tfp, "remove_bookmark_link: files: %s %s\n", newfile, filename_buffer)); LYCloseInput(fp); fp = NULL; if (fflush(nfp) == EOF) { CTRACE((tfp, "fflush(nfp): %s", LYStrerror(errno))); goto failure; } LYCloseTempFP(nfp); nfp = NULL; #if defined(DOSPATH) || defined(__EMX__) remove(filename_buffer); #endif /* DOSPATH */ #ifdef UNIX /* * By copying onto the bookmark file, rather than renaming it, we can * preserve the original ownership of the file, provided that it is * writable by the current process. * * Changed to copy 1998-04-26 -- gil * * But if the copy fails, for example because the filesystem is full, we * are left with a corrupt bookmark file. Changed back to use the previous * mechanism [try rename(), then mv for EXDEV], except in usual cases (not * a regular file e.g., symbolic link, or has hard links). This will let * bookmarks survive a filesystem full condition in the "normal" case * (bookmark is on same filesystem as home directory, is a regular file, * has no additional hard links). * * If we first tried LYCopyFile, and that fails, also fall back to trying * the other stuff. That gives a chance to recover in case the LYCopyFile * left a corrupt target file. * * If there is an error, and that error may mean that the bookmark file has * been corrupted, don't remove the temporary newfile (which should always * be uncorrupted) in place, it may still be used to recover manually. If * this applies, produce an additional message to that effect. The temp * file will still be removed by normal program exit cleanup. - kw * 1999-11-12 */ if (!regular) { if (LYCopyFile(newfile, filename_buffer) == 0) { (void) LYRemoveTemp(newfile); return; } LYSleepAlert(); /* give a chance to see error from cp - kw */ HTUserMsg(BOOKTEMP_COPY_FAIL); keep_tempfile = TRUE; } #endif /* UNIX */ if (rename(newfile, filename_buffer) != -1) { #ifdef MULTI_USER_UNIX if (regular) chmod(filename_buffer, stat_buf.st_mode & 07777); #endif HTSYS_purge(filename_buffer); return; } else { #ifndef VMS /* * Rename won't work across file systems. Check if this is the case * and do something appropriate. Used to be ODD_RENAME */ #if defined(_WINDOWS) || defined(WIN_EX) #if defined(WIN_EX) if (GetLastError() == ERROR_NOT_SAME_DEVICE) #else /* !_WIN_EX */ if (errno == ENOTSAM) #endif /* _WIN_EX */ { if (rename(newfile, filename_buffer) != 0) { if (LYCopyFile(newfile, filename_buffer) == 0) remove(newfile); } } #else if (errno == EXDEV) { static const char MV_FMT[] = "%s %s %s"; char *buffer = 0; const char *program; if ((program = HTGetProgramPath(ppMV)) != NULL) { HTAddParam(&buffer, MV_FMT, 1, program); HTAddParam(&buffer, MV_FMT, 2, newfile); HTAddParam(&buffer, MV_FMT, 3, filename_buffer); HTEndParam(&buffer, MV_FMT, 3); if (LYSystem(buffer) == 0) { #ifdef MULTI_USER_UNIX if (regular) chmod(filename_buffer, stat_buf.st_mode & 07777); #endif FREE(buffer); return; } } FREE(buffer); keep_tempfile = TRUE; goto failure; } CTRACE((tfp, "rename(): %s", LYStrerror(errno))); #endif /* _WINDOWS */ #endif /* !VMS */ #ifdef VMS HTAlert(ERROR_RENAMING_SCRA); #else HTAlert(ERROR_RENAMING_TEMP); #endif /* VMS */ if (TRACE) perror("renaming the file"); } failure: FREE(buf); HTAlert(BOOKMARK_DEL_FAILED); if (nfp) LYCloseTempFP(nfp); if (fp != NULL) LYCloseInput(fp); if (keep_tempfile) { HTUserMsg2(gettext("File may be recoverable from %s during this session"), newfile); } else { (void) LYRemoveTemp(newfile); } } /* * Allows user to select sub-bookmarks files. - FMG & FM */ int select_multi_bookmarks(void) { int c; /* * If not enabled, pick the "default" (0). */ if (LYMultiBookmarks == MBM_OFF || LYHaveSubBookmarks() == FALSE) { if (MBM_A_subbookmark[0]) /* If it exists! */ return (0); else return (-1); } /* * For ADVANCED users, we can just mess with the status line to save the 2 * redraws of the screen, if LYMBMAdvnced is TRUE. '=' will still show the * screen and let them do it the "long" way. */ if (LYMultiBookmarks == MBM_ADVANCED && user_mode == ADVANCED_MODE) { LYMBM_statusline(MULTIBOOKMARKS_SELECT); get_advanced_choice: c = LYgetch(); #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; c = LYCharINTERRUPT2; } #endif /* VMS */ if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) || LYCharIsINTERRUPT_HARD(c)) { /* * Treat left-arrow, ^G, or ^C as cancel. */ return (-2); } if (LYisNonAlnumKeyname(c, LYK_REFRESH)) { /* * Refresh the screen. */ lynx_force_repaint(); LYrefresh(); goto get_advanced_choice; } if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) { /* * Assume default bookmark file on ENTER or right-arrow. */ return (MBM_A_subbookmark[0] ? 0 : -1); } switch (c) { case '=': /* * Get the choice via the menu. */ return (select_menu_multi_bookmarks()); default: /* * Convert to an array index, act on it if valid. * Otherwise, get another keystroke. */ if ((c = LYMBM2index(c)) < 0) { goto get_advanced_choice; } } /* * See if we have a bookmark like that. */ return (MBM_A_subbookmark[c] ? c : -1); } else { /* * Get the choice via the menu. */ return (select_menu_multi_bookmarks()); } } /* * Allows user to select sub-bookmarks files. - FMG & FM */ int select_menu_multi_bookmarks(void) { int c, d, MBM_tmp_count, MBM_allow; int MBM_screens, MBM_from, MBM_to, MBM_current; /* * If not enabled, pick the "default" (0). */ if (LYMultiBookmarks == MBM_OFF) return (0); /* * Filip M. Gieszczykiewicz (filipg@paranoia.com) & FM * --------------------------------------------------- * MBM_A_subbookmark[n] - Hold values of the respective "multi_bookmarkn" * in the lynxrc file. * * MBM_A_subdescript[n] - Hold description entries in the lynxrc file. * * Note: MBM_A_subbookmark[0] is defined to be same value as * "bookmark_file" in the lynxrc file and/or the startup * "bookmark_page". * * We make the display of bookmarks depend on rows we have available. * * We load BookmarkPage with the valid MBM_A_subbookmark[n] via * get_bookmark_filename(). Otherwise, that function returns a zero-length * string to indicate a cancel, a single space to indicate an invalid * choice, or NULL to indicate an inaccessible file. */ MBM_allow = (LYlines - 7); /* We need 7 for header and footer */ /* * Screen big enough? */ if (MBM_allow <= 0) { /* * Too small. */ HTAlert(MULTIBOOKMARKS_SMALL); return (-2); } MBM_screens = (MBM_V_MAXFILES / MBM_allow) + 1; /* int rounds off low. */ MBM_current = 1; /* Gotta start somewhere :-) */ for (;;) { MBM_from = MBM_allow * MBM_current - MBM_allow; if (MBM_from < 0) MBM_from = 0; /* 0 is default bookmark... */ if (MBM_current != 1) MBM_from++; MBM_to = (MBM_allow * MBM_current); if (MBM_to > MBM_V_MAXFILES) MBM_to = MBM_V_MAXFILES; /* * Display menu of bookmarks. NOTE that we avoid printw()'s to * increase the chances that any non-ASCII or multibyte/CJK characters * will be handled properly. - FM */ LYclear(); LYmove(1, 5); lynx_start_h1_color(); if (MBM_screens > 1) { char *shead_buffer = 0; HTSprintf0(&shead_buffer, MULTIBOOKMARKS_SHEAD_MASK, MBM_current, MBM_screens); LYaddstr(shead_buffer); FREE(shead_buffer); } else { LYaddstr(MULTIBOOKMARKS_SHEAD); } lynx_stop_h1_color(); MBM_tmp_count = 0; for (c = MBM_from; c <= MBM_to; c++) { LYmove(3 + MBM_tmp_count, 5); LYaddch((chtype) LYindex2MBM(c)); LYaddstr(" : "); if (MBM_A_subdescript[c]) LYaddstr(MBM_A_subdescript[c]); LYmove(3 + MBM_tmp_count, 36); LYaddch('('); if (MBM_A_subbookmark[c]) LYaddstr(MBM_A_subbookmark[c]); LYaddch(')'); MBM_tmp_count++; } /* * Don't need to show it if it all fits on one screen! */ if (MBM_screens > 1) { LYmove(LYlines - 2, 0); LYaddstr("'"); lynx_start_bold(); LYaddstr("["); lynx_stop_bold(); LYaddstr("' "); LYaddstr(PREVIOUS); LYaddstr(", '"); lynx_start_bold(); LYaddstr("]"); lynx_stop_bold(); LYaddstr("' "); LYaddstr(NEXT_SCREEN); } LYMBM_statusline(MULTIBOOKMARKS_SAVE); for (;;) { c = LYgetch(); #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; c = 7; } #endif /* VMS */ if ((d = LYMBM2index(c)) >= 0) { /* * See if we have a bookmark like that. */ if (MBM_A_subbookmark[d] != NULL) return (d); show_bookmark_not_defined(); LYMBM_statusline(MULTIBOOKMARKS_SAVE); } else if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) || c == 7 || c == 3) { /* * Treat left-arrow, ^G, or ^C as cancel. */ return (-2); } else if (LYisNonAlnumKeyname(c, LYK_REFRESH)) { /* * Refresh the screen. */ lynx_force_repaint(); LYrefresh(); } else if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) { /* * Assume default bookmark file on ENTER or right-arrow. */ return (MBM_A_subbookmark[0] ? 0 : -1); } else if ((c == ']' || LYisNonAlnumKeyname(c, LYK_NEXT_PAGE)) && MBM_screens > 1) { /* * Next range, if available. */ if (++MBM_current > MBM_screens) MBM_current = 1; break; } else if ((c == '[' || LYisNonAlnumKeyname(c, LYK_PREV_PAGE)) && MBM_screens > 1) { /* * Previous range, if available. */ if (--MBM_current <= 0) MBM_current = MBM_screens; break; } } } } /* * This function returns TRUE if we have sub-bookmarks defined. Otherwise * (i.e., only the default bookmark file is defined), it returns FALSE. - FM */ BOOLEAN LYHaveSubBookmarks(void) { int i; for (i = 1; i < MBM_V_MAXFILES; i++) { if (non_empty(MBM_A_subbookmark[i])) return (TRUE); } return (FALSE); } /* * This function passes a string to _statusline(), making sure it is at the * bottom of the screen if LYMultiBookmarks is not MBM_OFF, otherwise, letting * it go to the normal statusline position based on the current user mode. We * want to use _statusline() so that any multibyte/CJK characters in the string * will be handled properly. - FM */ void LYMBM_statusline(const char *text) { if (LYMultiBookmarks != MBM_OFF && user_mode == NOVICE_MODE) { LYStatusLine = (LYlines - 1); _statusline(text); LYStatusLine = -1; } else { _statusline(text); } } /* * Check whether we have any visible (non-blank) chars. */ static BOOLEAN havevisible(const char *Title) { BOOLEAN result = FALSE; const char *p = Title; unsigned char c; long unicode; for (; *p; p++) { c = UCH(TOASCII(*p)); if (c > 32 && c < 127) { result = TRUE; break; } if (c <= 32 || c == 127) continue; if (LYHaveCJKCharacterSet || !UCCanUniTranslateFrom(current_char_set)) { result = TRUE; break; } unicode = UCTransToUni(*p, current_char_set); if (unicode == ucNeedMore) continue; if (unicode > 32 && unicode < 127) { result = TRUE; break; } if (unicode <= 32 || unicode == 0xa0 || unicode == 0xad) continue; if (unicode < 0x2000 || unicode >= 0x200f) { result = TRUE; break; } } return (result); } /* * Check whether string have 8 bit chars. */ static BOOLEAN have8bit(const char *Title) { const char *p = Title; for (; *p; p++) { if (UCH(*p) > 127) return (TRUE); } return (FALSE); /* if we came here */ } /* * Ok, title have 8-bit characters and they are in display charset. Bookmarks * is a permanent file. To avoid dependencies from display character set which * may be changed with time we store 8-bit characters as numeric character * reference (NCR), so where the character encoded as unicode number in form of * &#xUUUU; * * To make bookmarks more readable for human (&#xUUUU certainly not) we add a * comment with '7-bit approximation' from the converted string. This is a * valid HTML and bookmarks code. * * We do not want use META charset tag in bookmarks file: it will never be * changed later :-( * * NCR's translation is part of I18N and HTML4.0 supported starting with Lynx * 2.7.2, Netscape 4.0 and MSIE 4.0. Older versions fail. */ static char *title_convert8bit(const char *Title) { const char *p = Title; char *p0; char *q; char *comment = NULL; char *ncr = NULL; char *buf = NULL; int charset_in = current_char_set; int charset_out = UCGetLYhndl_byMIME("us-ascii"); for (; *p; p++) { char temp[2]; LYStrNCpy(temp, p, sizeof(temp) - 1); if (UCH(*temp) <= 127) { StrAllocCat(comment, temp); StrAllocCat(ncr, temp); } else if (charset_out >= 0) { long unicode; char replace_buf[32]; if (UCTransCharStr(replace_buf, (int) sizeof(replace_buf), *temp, charset_in, charset_out, YES) > 0) StrAllocCat(comment, replace_buf); unicode = UCTransToUni(*temp, charset_in); StrAllocCat(ncr, "&#"); sprintf(replace_buf, "%ld", unicode); StrAllocCat(ncr, replace_buf); StrAllocCat(ncr, ";"); } } if (comment != NULL) { /* * Cleanup comment, collapse multiple dashes into one dash, skip '>'. */ for (q = p0 = comment; *p0; p0++) { if (UCH(TOASCII(*p0)) >= 32 && *p0 != '>' && (q == comment || *p0 != '-' || *(q - 1) != '-')) { *q++ = *p0; } } *q = '\0'; /* * valid bookmark should be a single line (no linebreaks!). */ StrAllocCat(buf, ""); StrAllocCat(buf, ncr); FREE(comment); } FREE(ncr); return (buf); } /* * Since this is the "Default Bookmark File", we save it as a global, and as * the first MBM_A_subbookmark entry. */ void set_default_bookmark_page(char *value) { if (value != 0) { if (bookmark_page == 0 || strcmp(bookmark_page, value)) { StrAllocCopy(bookmark_page, value); } StrAllocCopy(BookmarkPage, bookmark_page); StrAllocCopy(MBM_A_subbookmark[0], bookmark_page); StrAllocCopy(MBM_A_subdescript[0], MULTIBOOKMARKS_DEFAULT); } } lynx2-8-8/src/LYBookmark.h000644 023711 023712 00000001337 10166102471 015727 0ustar00dickeylynx000000 000000 #ifndef LYBOOKMARK_H #define LYBOOKMARK_H #ifndef LYSTRUCTS_H #include #endif /* LYSTRUCTS_H */ #ifdef __cplusplus extern "C" { #endif extern BOOLEAN LYHaveSubBookmarks(void); extern const char *get_bookmark_filename(char **name); extern int LYMBM2index(int ch); extern int LYindex2MBM(int n); extern int select_menu_multi_bookmarks(void); extern int select_multi_bookmarks(void); extern void LYMBM_statusline(const char *text); extern void remove_bookmark_link(int cur, char *cur_bookmark_page); extern void save_bookmark_link(const char *address, const char *title); extern void set_default_bookmark_page(char *value); #ifdef __cplusplus } #endif #endif /* LYBOOKMARK_H */ lynx2-8-8/src/LYCgi.c000644 023711 023712 00000046356 12245762550 014703 0ustar00dickeylynx000000 000000 /* * $LynxId: LYCgi.c,v 1.67 2013/11/28 11:35:56 tom Exp $ * Lynx CGI support LYCgi.c * ================ * * Authors * GL George Lindholm * * History * 15 Jun 95 Created as way to provide a lynx based service with * dynamic pages without the need for a http daemon. GL * 27 Jun 95 Added (command line) support. Various cleanup * and bug fixes. GL * 04 Sep 97 Added support for PATH_INFO scripts. JKT * * Bugs * If the called scripts aborts before sending the mime headers then * lynx hangs. * * Should do something about SIGPIPE, (but then it should never happen) * * No support for redirection. Or mime-types. * * Should try and parse for a HTTP 1.1 header in case we are "calling" a * nph- script. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char **env = NULL; /* Environment variables */ static unsigned envc_size = 0; /* Slots in environment array */ static unsigned envc = 0; /* Slots used so far */ static HTList *alloced = NULL; #if defined(LYNXCGI_LINKS) && !defined(__MINGW32__) static char *user_agent = NULL; static char *server_software = NULL; static char *accept_language = NULL; static char *post_len = NULL; #endif /* LYNXCGI_LINKS */ static void add_environment_value(const char *env_value); #define PERROR(msg) CTRACE((tfp, "LYNXCGI: %s: %s\n", msg, LYStrerror(errno))) #define PUTS(buf) (*target->isa->put_block)(target, buf, strlen(buf)) #ifdef LY_FIND_LEAKS static void free_alloced_lynxcgi(void) { void *ptr; while ((ptr = HTList_removeLastObject(alloced)) != NULL) { FREE(ptr); } FREE(alloced); #ifdef LYNXCGI_LINKS FREE(user_agent); FREE(server_software); #endif } #endif /* LY_FIND_LEAKS */ static void remember_alloced(void *ptr) { if (!alloced) { alloced = HTList_new(); #ifdef LY_FIND_LEAKS atexit(free_alloced_lynxcgi); #endif } HTList_addObject(alloced, ptr); } /* * Simple routine for expanding the environment array and adding a value to * it */ static void add_environment_value(const char *env_value) { if (envc == envc_size) { /* Need some more slots */ envc_size += 10; if (env) { env = (char **) realloc(env, sizeof(env[0]) * (envc_size + 2)); /* + terminator and base 0 */ } else { env = (char **) malloc(sizeof(env[0]) * (envc_size + 2)); /* + terminator and base 0 */ remember_alloced(env); } if (env == NULL) { outofmem(__FILE__, "LYCgi"); } assert(env != NULL); } env[envc++] = DeConst(env_value); env[envc] = NULL; /* Make sure it is always properly terminated */ } /* * Add the value of an existing environment variable to those passed on to the * lynxcgi script. */ void add_lynxcgi_environment(const char *variable_name) { char *env_value; env_value = LYGetEnv(variable_name); if (env_value != NULL) { char *add_value = NULL; HTSprintf0(&add_value, "%s=%s", variable_name, env_value); add_environment_value(add_value); remember_alloced(add_value); } } #ifdef __MINGW32__ static int LYLoadCGI(const char *arg, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *sink) { return -1; } #else #ifdef LYNXCGI_LINKS /* * Wrapper for exec_ok(), confirming with user if the link text is not visible * in the status line. */ static BOOL can_exec_cgi(const char *linktext, const char *linkargs) { const char *format = gettext("Do you want to execute \"%s\"?"); char *message = NULL; char *command = NULL; char *p; BOOL result = TRUE; if (!exec_ok(HTLoadedDocumentURL(), linktext, CGI_PATH)) { /* exec_ok gives out msg. */ result = FALSE; } else { StrAllocCopy(command, linktext); if (non_empty(linkargs)) { HTSprintf(&command, " %s", linkargs); } HTUnEscape(command); for (p = command; *p; ++p) if (*p == '+') *p = ' '; HTSprintf0(&message, format, command); result = HTConfirm(message); FREE(message); FREE(command); } return result; } #endif /* LYNXCGI_LINKS */ static int LYLoadCGI(const char *arg, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *sink) { int status = 0; #ifdef LYNXCGI_LINKS #ifndef VMS char *cp; struct stat stat_buf; char *pgm = NULL; /* executable */ char *pgm_args = NULL; /* and its argument(s) */ int statrv; char *orig_pgm = NULL; /* Path up to ? as given, URL-escaped */ char *document_root = NULL; /* Corrected value of DOCUMENT_ROOT */ char *path_info = NULL; /* PATH_INFO extracted from pgm */ char *pgm_buff = NULL; /* PATH_INFO extraction buffer */ char *path_translated; /* From document_root/path_info */ if (isEmpty(arg) || strlen(arg) <= 8) { HTAlert(BAD_REQUEST); status = -2; return (status); } else { if (StrNCmp(arg, "lynxcgi://localhost", 19) == 0) { StrAllocCopy(pgm, arg + 19); } else { StrAllocCopy(pgm, arg + 8); } if ((cp = StrChr(pgm, '?')) != NULL) { /* Need to terminate executable */ *cp++ = '\0'; pgm_args = cp; } } StrAllocCopy(orig_pgm, pgm); if (trimPoundSelector(pgm) != NULL) { /* * Strip a #fragment from path. In this case any pgm_args found above * will also be bogus, since the '?' came after the '#' and is part of * the fragment. Note that we don't handle the case where a '#' * appears after a '?' properly according to URL rules. - kw */ pgm_args = NULL; } HTUnEscape(pgm); /* BEGIN WebSter Mods */ /* If pgm is not stat-able, see if PATH_INFO data is at the end of pgm */ if ((statrv = stat(pgm, &stat_buf)) < 0) { StrAllocCopy(pgm_buff, pgm); while (statrv < 0 || (statrv = stat(pgm_buff, &stat_buf)) < 0) { if ((cp = strrchr(pgm_buff, '/')) != NULL) { *cp = '\0'; statrv = 1; /* force new stat() - kw */ } else { PERROR("strrchr(pgm_buff, '/') returned NULL"); break; } } if (statrv < 0) { /* Did not find PATH_INFO data */ PERROR("stat() of pgm_buff failed"); } else { /* Found PATH_INFO data. Strip it off of pgm and into path_info. */ StrAllocCopy(path_info, pgm + strlen(pgm_buff)); /* The following is safe since pgm_buff was derived from pgm by stripping stuff off its end and by HTUnEscaping, so we know we have enough memory allocated for pgm. Note that pgm_args may still point into that memory, so we cannot reallocate pgm here. - kw */ strcpy(pgm, pgm_buff); CTRACE((tfp, "LYNXCGI: stat() of %s succeeded, path_info=\"%s\".\n", pgm_buff, path_info)); } FREE(pgm_buff); } /* END WebSter Mods */ if (statrv != 0) { /* * Neither the path as given nor any components examined by backing up * were stat()able. - kw */ HTAlert(gettext("Unable to access cgi script")); PERROR("stat() failed"); status = -4; } else #ifdef _WINDOWS /* 1998/01/14 (Wed) 09:16:04 */ #define isExecutable(mode) (mode & (S_IXUSR)) #else #define isExecutable(mode) (mode & (S_IXUSR|S_IXGRP|S_IXOTH)) #endif if (!(S_ISREG(stat_buf.st_mode) && isExecutable(stat_buf.st_mode))) { /* * Not a runnable file, See if we can load it using "file:" code. */ char *new_arg = NULL; /* * But try "file:" only if the file we are looking at is the path as * given (no path_info was extracted), otherwise it will be to * confusing to know just what file is loaded. - kw */ if (path_info) { CTRACE((tfp, "%s is not a file and %s not an executable, giving up.\n", orig_pgm, pgm)); FREE(path_info); FREE(pgm); FREE(orig_pgm); status = -4; return (status); } LYLocalFileToURL(&new_arg, orig_pgm); CTRACE((tfp, "%s is not an executable file, passing the buck.\n", arg)); status = HTLoadFile(new_arg, anAnchor, format_out, sink); FREE(new_arg); } else if (path_info && anAnchor != HTMainAnchor && !(reloading && anAnchor->document) && strcmp(arg, HTLoadedDocumentURL()) && HText_AreDifferent(anAnchor, arg) && HTUnEscape(orig_pgm) && !can_exec_cgi(orig_pgm, "")) { /* * If we have extra path info and are not just reloading the current, * check the full file path (after unescaping) now to catch forbidden * segments. - kw */ status = HT_NOT_LOADED; } else if (no_lynxcgi) { HTUserMsg(CGI_DISABLED); status = HT_NOT_LOADED; } else if (no_bookmark_exec && anAnchor != HTMainAnchor && !(reloading && anAnchor->document) && strcmp(arg, HTLoadedDocumentURL()) && HText_AreDifferent(anAnchor, arg) && HTLoadedDocumentBookmark()) { /* * If we are reloading a lynxcgi document that had already been loaded, * the various checks above should allow it even if no_bookmark_exec is * TRUE an we are not now coming from a bookmark page. - kw */ HTUserMsg(BOOKMARK_EXEC_DISABLED); status = HT_NOT_LOADED; } else if (anAnchor != HTMainAnchor && !(reloading && anAnchor->document) && strcmp(arg, HTLoadedDocumentURL()) && HText_AreDifferent(anAnchor, arg) && !can_exec_cgi(pgm, pgm_args)) { /* * If we are reloading a lynxcgi document that had already been loaded, * the various checks above should allow it even if exec_ok() would * reject it because we are not now coming from a document with a URL * allowed by TRUSTED_LYNXCGI rules. - kw */ status = HT_NOT_LOADED; } else { HTFormat format_in; HTStream *target = NULL; /* Unconverted data */ int fd1[2], fd2[2]; char buf[MAX_LINE]; int pid; #ifdef HAVE_TYPE_UNIONWAIT union wait wstatus; #else int wstatus; #endif fd1[0] = -1; fd1[1] = -1; fd2[0] = -1; fd2[1] = -1; if (anAnchor->isHEAD || keep_mime_headers) { /* Show output as plain text */ format_in = WWW_PLAINTEXT; } else { /* Decode full HTTP response */ format_in = HTAtom_for("www/mime"); } target = HTStreamStack(format_in, format_out, sink, anAnchor); if (!target || target == NULL) { char *tmp = 0; HTSprintf0(&tmp, CANNOT_CONVERT_I_TO_O, HTAtom_name(format_in), HTAtom_name(format_out)); HTAlert(tmp); FREE(tmp); status = HT_NOT_LOADED; } else if (anAnchor->post_data && pipe(fd1) < 0) { HTAlert(CONNECT_SET_FAILED); PERROR("pipe() failed"); status = -3; } else if (pipe(fd2) < 0) { HTAlert(CONNECT_SET_FAILED); PERROR("pipe() failed"); close(fd1[0]); close(fd1[1]); status = -3; } else { static BOOL first_time = TRUE; /* One time setup flag */ if (first_time) { /* Set up static environment variables */ first_time = FALSE; /* Only once */ add_environment_value("REMOTE_HOST=localhost"); add_environment_value("REMOTE_ADDR=127.0.0.1"); HTSprintf0(&user_agent, "HTTP_USER_AGENT=%s/%s libwww/%s", LYNX_NAME, LYNX_VERSION, HTLibraryVersion); add_environment_value(user_agent); HTSprintf0(&server_software, "SERVER_SOFTWARE=%s/%s", LYNX_NAME, LYNX_VERSION); add_environment_value(server_software); } fflush(stdout); fflush(stderr); CTRACE_FLUSH(tfp); if ((pid = fork()) > 0) { /* The good, */ ssize_t chars; off_t total_chars; close(fd2[1]); if (anAnchor->post_data) { ssize_t written; int remaining, total_written = 0; close(fd1[0]); /* We have form data to push across the pipe */ if (TRACE) { CTRACE((tfp, "LYNXCGI: Doing post, content-type '%s'\n", anAnchor->post_content_type)); CTRACE((tfp, "LYNXCGI: Writing:\n")); trace_bstring(anAnchor->post_data); CTRACE((tfp, "----------------------------------\n")); } remaining = BStrLen(anAnchor->post_data); while ((written = write(fd1[1], BStrData(anAnchor->post_data) + total_written, (size_t) remaining)) != 0) { if (written < 0) { #ifdef EINTR if (errno == EINTR) continue; #endif /* EINTR */ #ifdef ERESTARTSYS if (errno == ERESTARTSYS) continue; #endif /* ERESTARTSYS */ PERROR("write() of POST data failed"); break; } CTRACE((tfp, "LYNXCGI: Wrote %d bytes of POST data.\n", (int) written)); total_written += (int) written; remaining -= (int) written; if (remaining == 0) break; } if (remaining != 0) { CTRACE((tfp, "LYNXCGI: %d bytes remain unwritten!\n", remaining)); } close(fd1[1]); } HTReadProgress(total_chars = 0, (off_t) 0); while ((chars = read(fd2[0], buf, sizeof(buf))) != 0) { if (chars < 0) { #ifdef EINTR if (errno == EINTR) continue; #endif /* EINTR */ #ifdef ERESTARTSYS if (errno == ERESTARTSYS) continue; #endif /* ERESTARTSYS */ PERROR("read() of CGI output failed"); break; } total_chars += (int) chars; HTReadProgress(total_chars, (off_t) 0); CTRACE((tfp, "LYNXCGI: Rx: %.*s\n", (int) chars, buf)); (*target->isa->put_block) (target, buf, (int) chars); } if (chars < 0 && total_chars == 0) { status = HT_NOT_LOADED; (*target->isa->_abort) (target, NULL); target = NULL; } else if (chars != 0) { status = HT_PARTIAL_CONTENT; } else { status = HT_LOADED; } #if !HAVE_WAITPID while (wait(&wstatus) != pid) ; /* do nothing */ #else while (-1 == waitpid(pid, &wstatus, 0)) { /* wait for child */ #ifdef EINTR if (errno == EINTR) continue; #endif /* EINTR */ #ifdef ERESTARTSYS if (errno == ERESTARTSYS) continue; #endif /* ERESTARTSYS */ break; } #endif /* !HAVE_WAITPID */ close(fd2[0]); } else if (pid == 0) { /* The Bad, */ char **argv = NULL; int argv_cnt = 3; /* name, one arg and terminator */ char **cur_argv = NULL; int exec_errno; /* Set up output pipe */ close(fd2[0]); dup2(fd2[1], fileno(stdout)); /* Should check success code */ dup2(fd2[1], fileno(stderr)); close(fd2[1]); if (non_empty(language)) { HTSprintf0(&accept_language, "HTTP_ACCEPT_LANGUAGE=%s", language); add_environment_value(accept_language); } if (non_empty(pref_charset)) { cp = NULL; StrAllocCopy(cp, "HTTP_ACCEPT_CHARSET="); StrAllocCat(cp, pref_charset); add_environment_value(cp); } if (anAnchor->post_data && anAnchor->post_content_type) { cp = NULL; StrAllocCopy(cp, "CONTENT_TYPE="); StrAllocCat(cp, anAnchor->post_content_type); add_environment_value(cp); } if (anAnchor->post_data) { /* post script, read stdin */ close(fd1[1]); dup2(fd1[0], fileno(stdin)); close(fd1[0]); /* Build environment variables */ add_environment_value("REQUEST_METHOD=POST"); HTSprintf0(&post_len, "CONTENT_LENGTH=%d", BStrLen(anAnchor->post_data)); add_environment_value(post_len); } else { close(fileno(stdin)); if (anAnchor->isHEAD) { add_environment_value("REQUEST_METHOD=HEAD"); } } /* * Set up argument line, mainly for scripts */ if (pgm_args != NULL) { for (cp = pgm_args; *cp != '\0'; cp++) { if (*cp == '+') { argv_cnt++; } } } argv = (char **) malloc((unsigned) argv_cnt * sizeof(char *)); if (argv == NULL) { outofmem(__FILE__, "LYCgi"); } assert(argv != NULL); cur_argv = argv + 1; /* For argv[0] */ if (pgm_args != NULL) { char *cr; /* Data for a get/search form */ if (is_www_index) { add_environment_value("REQUEST_METHOD=SEARCH"); } else if (!anAnchor->isHEAD && !anAnchor->post_data) { add_environment_value("REQUEST_METHOD=GET"); } cp = NULL; StrAllocCopy(cp, "QUERY_STRING="); StrAllocCat(cp, pgm_args); add_environment_value(cp); /* * Split up arguments into argv array */ cp = pgm_args; cr = cp; while (1) { if (*cp == '\0') { *(cur_argv++) = HTUnEscape(cr); break; } else if (*cp == '+') { *cp++ = '\0'; *(cur_argv++) = HTUnEscape(cr); cr = cp; } cp++; } } else if (!anAnchor->isHEAD && !anAnchor->post_data) { add_environment_value("REQUEST_METHOD=GET"); } *cur_argv = NULL; /* Terminate argv */ argv[0] = pgm; /* Begin WebSter Mods -jkt */ if (LYCgiDocumentRoot != NULL) { /* Add DOCUMENT_ROOT to env */ cp = NULL; StrAllocCopy(cp, "DOCUMENT_ROOT="); StrAllocCat(cp, LYCgiDocumentRoot); add_environment_value(cp); } if (path_info != NULL) { /* Add PATH_INFO to env */ cp = NULL; StrAllocCopy(cp, "PATH_INFO="); StrAllocCat(cp, path_info); add_environment_value(cp); } if (LYCgiDocumentRoot != NULL && path_info != NULL) { /* Construct and add PATH_TRANSLATED to env */ StrAllocCopy(document_root, LYCgiDocumentRoot); LYTrimHtmlSep(document_root); path_translated = document_root; StrAllocCat(path_translated, path_info); cp = NULL; StrAllocCopy(cp, "PATH_TRANSLATED="); StrAllocCat(cp, path_translated); add_environment_value(cp); FREE(path_translated); } /* End WebSter Mods -jkt */ execve(argv[0], argv, env); exec_errno = errno; PERROR("execve failed"); printf("Content-Type: text/plain\r\n\r\n"); if (!anAnchor->isHEAD) { printf("exec of %s failed", pgm); printf(": %s.\r\n", LYStrerror(exec_errno)); } fflush(stdout); fflush(stderr); _exit(1); } else { /* and the Ugly */ HTAlert(CONNECT_FAILED); PERROR("fork() failed"); close(fd1[0]); close(fd1[1]); close(fd2[0]); close(fd2[1]); status = -1; } } if (target != NULL) { (*target->isa->_free) (target); } } FREE(path_info); FREE(pgm); FREE(orig_pgm); #else /* VMS */ HTStream *target; char *buf = 0; target = HTStreamStack(WWW_HTML, format_out, sink, anAnchor); HTSprintf0(&buf, "\n\n%s\n\n\n", gettext("Good Advice")); PUTS(buf); HTSprintf0(&buf, "

        %s

        \n", gettext("Good Advice")); PUTS(buf); HTSprintf0(&buf, "%s %s
        .\n", gettext("this link")); PUTS(buf); HTSprintf0(&buf, "

        %s\n", gettext("It provides state of the art CGI script support.\n")); PUTS(buf); HTSprintf0(&buf, "\n\n"); PUTS(buf); (*target->isa->_free) (target); FREE(buf); status = HT_LOADED; #endif /* VMS */ #else /* LYNXCGI_LINKS */ HTUserMsg(CGI_NOT_COMPILED); status = HT_NOT_LOADED; #endif /* LYNXCGI_LINKS */ (void) arg; (void) anAnchor; (void) format_out; (void) sink; return (status); } #endif /* __MINGW32__ */ #ifdef GLOBALDEF_IS_MACRO #define _LYCGI_C_GLOBALDEF_1_INIT { "lynxcgi", LYLoadCGI, 0 } GLOBALDEF(HTProtocol, LYLynxCGI, _LYCGI_C_GLOBALDEF_1_INIT); #else GLOBALDEF HTProtocol LYLynxCGI = {"lynxcgi", LYLoadCGI, 0}; #endif /* GLOBALDEF_IS_MACRO */ lynx2-8-8/src/LYCgi.h000644 023711 023712 00000000364 10166102471 014663 0ustar00dickeylynx000000 000000 #ifndef LYCGI_H #define LYCGI_H #ifndef HTUTILS_H #include #endif #ifdef __cplusplus extern "C" { #endif extern void add_lynxcgi_environment(const char *variable_name); #ifdef __cplusplus } #endif #endif /* LYGETFILE_H */ lynx2-8-8/src/LYCharSets.c000644 023711 023712 00000115057 12175560333 015705 0ustar00dickeylynx000000 000000 /* * $LynxId: LYCharSets.c,v 1.68 2013/01/04 21:47:16 tom Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include HTkcode kanji_code = NOKANJI; BOOLEAN LYHaveCJKCharacterSet = FALSE; BOOLEAN DisplayCharsetMatchLocale = TRUE; BOOL force_old_UCLYhndl_on_reload = FALSE; int forced_UCLYhdnl; int LYNumCharsets = 0; /* Will be initialized later by UC_Register. */ int current_char_set = -1; /* will be intitialized later in LYMain.c */ int linedrawing_char_set = -1; STRING2PTR p_entity_values = NULL; /* Pointer, for HTML_put_entity() */ /* obsolete and probably not used(???) */ /* will be initialized in HTMLUseCharacterSet */ #ifdef USE_CHARSET_CHOICE charset_subset_t charset_subsets[MAXCHARSETS]; BOOL custom_display_charset = FALSE; BOOL custom_assumed_doc_charset = FALSE; #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN int display_charset_map[MAXCHARSETS]; int assumed_doc_charset_map[MAXCHARSETS]; const char *display_charset_choices[MAXCHARSETS + 1]; const char *assumed_charset_choices[MAXCHARSETS + 1]; int displayed_display_charset_idx; #endif #endif /* USE_CHARSET_CHOICE */ /* * New character sets now declared with UCInit() in UCdomap.c * * INSTRUCTIONS for adding new character sets which do not have * Unicode tables now in UCdomap.h * * * [We hope you need not correct/add old-style mapping below as in ISO_LATIN1[] * or SevenBitApproximations[] any more - it works now via new chartrans * mechanism, but kept for compatibility only: we should cleanup the stuff, * but this is not so easy...] * * Currently we only declare some charset's properties here (such as MIME * names, etc.), it does not include real mapping. * * There is a place marked "Add your new character sets HERE" in this file. * Make up a character set and add it in the same style as the ISO_LATIN1 set * below, giving it a unique name. * * Add the name of the set to LYCharSets. Similarly add the appropriate * information to the tables below: LYchar_set_names, LYCharSet_UC, * LYlowest_eightbit. These 4 tables all MUST have the same order. (And this * is the order you will see in Lynx Options Menu, which is why few * unicode-based charsets are listed here). * */ /* Entity values -- for ISO Latin 1 local representation * * This MUST match exactly the table referred to in the DTD! */ static const char *ISO_Latin1[] = { "\306", /* capital AE diphthong (ligature) (Æ) - AElig */ "\301", /* capital A, acute accent (Á) - Aacute */ "\302", /* capital A, circumflex accent (Â) - Acirc */ "\300", /* capital A, grave accent (À) - Agrave */ "\305", /* capital A, ring - Aring (Å) */ "\303", /* capital A, tilde - Atilde (Ã) */ "\304", /* capital A, dieresis or umlaut mark (Ä) - Auml */ "\307", /* capital C, cedilla - Ccedil (Ç) */ "\320", /* capital Eth or D with stroke (Ð) - Dstrok */ "\320", /* capital Eth, Icelandic (Ð) - ETH */ "\311", /* capital E, acute accent (É) - Eacute */ "\312", /* capital E, circumflex accent (Ê) - Ecirc */ "\310", /* capital E, grave accent (È) - Egrave */ "\313", /* capital E, dieresis or umlaut mark (Ë) - Euml */ "\315", /* capital I, acute accent (Í) - Iacute */ "\316", /* capital I, circumflex accent (Î) - Icirc */ "\314", /* capital I, grave accent (Ì) - Igrave */ "\317", /* capital I, dieresis or umlaut mark (Ï) - Iuml */ "\321", /* capital N, tilde (Ñ) - Ntilde */ "\323", /* capital O, acute accent (Ó) - Oacute */ "\324", /* capital O, circumflex accent (Ô) - Ocirc */ "\322", /* capital O, grave accent (Ò) - Ograve */ "\330", /* capital O, slash (Ø) - Oslash */ "\325", /* capital O, tilde (Õ) - Otilde */ "\326", /* capital O, dieresis or umlaut mark (Ö) - Ouml */ "\336", /* capital THORN, Icelandic (Þ) - THORN */ "\332", /* capital U, acute accent (Ú) - Uacute */ "\333", /* capital U, circumflex accent (Û) - Ucirc */ "\331", /* capital U, grave accent (Ù) - Ugrave */ "\334", /* capital U, dieresis or umlaut mark (Ü) - Uuml */ "\335", /* capital Y, acute accent (Ý) - Yacute */ "\341", /* small a, acute accent (á) - aacute */ "\342", /* small a, circumflex accent (â) - acirc */ "\264", /* spacing acute (´) - acute */ "\346", /* small ae diphthong (ligature) (æ) - aelig */ "\340", /* small a, grave accent (à) - agrave */ "\046", /* ampersand (&) - amp */ "\345", /* small a, ring (å) - aring */ "\343", /* small a, tilde (ã) - atilde */ "\344", /* small a, dieresis or umlaut mark (ä) - auml */ "\246", /* broken vertical bar (¦) - brkbar */ "\246", /* broken vertical bar (¦) - brvbar */ "\347", /* small c, cedilla (ç) - ccedil */ "\270", /* spacing cedilla (¸) - cedil */ "\242", /* cent sign (¢) - cent */ "\251", /* copyright sign (©) - copy */ "\244", /* currency sign (¤) - curren */ "\260", /* degree sign (°) - deg */ "\250", /* spacing dieresis (¨) - die */ "\367", /* division sign (÷) - divide */ "\351", /* small e, acute accent (é) - eacute */ "\352", /* small e, circumflex accent (ê) - ecirc */ "\350", /* small e, grave accent (è) - egrave */ "-", /* dash the width of emsp - emdash */ "\002", /* emsp, em space - not collapsed NEVER CHANGE THIS - emsp */ "-", /* dash the width of ensp - endash */ "\002", /* ensp, en space - not collapsed NEVER CHANGE THIS - ensp */ "\360", /* small eth, Icelandic (ð) - eth */ "\353", /* small e, dieresis or umlaut mark (ë) - euml */ "\275", /* fraction 1/2 (½) - frac12 */ "\274", /* fraction 1/4 (¼) - frac14 */ "\276", /* fraction 3/4 (¾) - frac34 */ "\076", /* greater than (>) - gt */ "\257", /* spacing macron (¯) - hibar */ "\355", /* small i, acute accent (í) - iacute */ "\356", /* small i, circumflex accent (î) - icirc */ "\241", /* inverted exclamation mark (¡) - iexcl */ "\354", /* small i, grave accent (ì) - igrave */ "\277", /* inverted question mark (¿) - iquest */ "\357", /* small i, dieresis or umlaut mark (ï) - iuml */ "\253", /* angle quotation mark, left («) - laquo */ "\074", /* less than (<) - lt */ "\257", /* spacing macron (¯) - macr */ "-", /* dash the width of emsp - mdash */ "\265", /* micro sign (µ) - micro */ "\267", /* middle dot (·) - middot */ "\001", /* nbsp non-breaking space NEVER CHANGE THIS - nbsp */ "-", /* dash the width of ensp - ndash */ "\254", /* negation sign (¬) - not */ "\361", /* small n, tilde (ñ) - ntilde */ "\363", /* small o, acute accent (ó) - oacute */ "\364", /* small o, circumflex accent (ô) - ocirc */ "\362", /* small o, grave accent (ò) - ograve */ "\252", /* feminine ordinal indicator (ª) - ordf */ "\272", /* masculine ordinal indicator (º) - ordm */ "\370", /* small o, slash (ø) - oslash */ "\365", /* small o, tilde (õ) - otilde */ "\366", /* small o, dieresis or umlaut mark (ö) - ouml */ "\266", /* paragraph sign (¶) - para */ "\261", /* plus-or-minus sign (±) - plusmn */ "\243", /* pound sign (£) - pound */ "\042", /* quote '"' (") - quot */ "\273", /* angle quotation mark, right (») - raquo */ "\256", /* circled R registered sign (®) - reg */ "\247", /* section sign (§) - sect */ "\007", /* soft hyphen (­) NEVER CHANGE THIS - shy */ "\271", /* superscript 1 (¹) - sup1 */ "\262", /* superscript 2 (²) - sup2 */ "\263", /* superscript 3 (³) - sup3 */ "\337", /* small sharp s, German (sz ligature) (ß) - szlig */ "\002", /* thin space - not collapsed NEVER CHANGE THIS - thinsp */ "\376", /* small thorn, Icelandic (þ) - thorn */ "\327", /* multiplication sign (×) - times */ "(TM)", /* circled TM trade mark sign (™) - trade */ "\372", /* small u, acute accent (ú) - uacute */ "\373", /* small u, circumflex accent (û) - ucirc */ "\371", /* small u, grave accent (ù) - ugrave */ "\250", /* spacing dieresis (¨) - uml */ "\374", /* small u, dieresis or umlaut mark (ü) - uuml */ "\375", /* small y, acute accent (ý) - yacute */ "\245", /* yen sign (¥) - yen */ "\377", /* small y, dieresis or umlaut mark (ÿ) - yuml */ }; /* Entity values -- 7 bit character approximations * * This MUST match exactly the table referred to in the DTD! */ const char *SevenBitApproximations[] = { "AE", /* capital AE diphthong (ligature) (Æ) - AElig */ "A", /* capital A, acute accent (Á) - Aacute */ "A", /* capital A, circumflex accent (Â) - Acirc */ "A", /* capital A, grave accent (À) - Agrave */ "A", /* capital A, ring - Aring (Å) */ "A", /* capital A, tilde - Atilde (Ã) */ #ifdef LY_UMLAUT "Ae", /* capital A, dieresis or umlaut mark (Ä) - Auml */ #else "A", /* capital A, dieresis or umlaut mark (Ä) - Auml */ #endif /* LY_UMLAUT */ "C", /* capital C, cedilla (Ç) - Ccedil */ "Dj", /* capital D with stroke (Ð) - Dstrok */ "DH", /* capital Eth, Icelandic (Ð) - ETH */ "E", /* capital E, acute accent (É) - Eacute */ "E", /* capital E, circumflex accent (Ê) - Ecirc */ "E", /* capital E, grave accent (È) - Egrave */ "E", /* capital E, dieresis or umlaut mark (Ë) - Euml */ "I", /* capital I, acute accent (Í) - Iacute */ "I", /* capital I, circumflex accent (Î) - Icirc */ "I", /* capital I, grave accent (Ì) - Igrave */ "I", /* capital I, dieresis or umlaut mark (Ï) - Iuml */ "N", /* capital N, tilde - Ntilde (Ñ) */ "O", /* capital O, acute accent (Ó) - Oacute */ "O", /* capital O, circumflex accent (Ô) - Ocirc */ "O", /* capital O, grave accent (Ò) - Ograve */ "O", /* capital O, slash (Ø) - Oslash */ "O", /* capital O, tilde (Õ) - Otilde */ #ifdef LY_UMLAUT "Oe", /* capital O, dieresis or umlaut mark (Ö) - Ouml */ #else "O", /* capital O, dieresis or umlaut mark (Ö) - Ouml */ #endif /* LY_UMLAUT */ "P", /* capital THORN, Icelandic (Þ) - THORN */ "U", /* capital U, acute accent (Ú) - Uacute */ "U", /* capital U, circumflex accent (Û) - Ucirc */ "U", /* capital U, grave accent (Ù) - Ugrave */ #ifdef LY_UMLAUT "Ue", /* capital U, dieresis or umlaut mark (Ü) - Uuml */ #else "U", /* capital U, dieresis or umlaut mark (Ü) - Uuml */ #endif /* LY_UMLAUT */ "Y", /* capital Y, acute accent (Ý) - Yacute */ "a", /* small a, acute accent (á) - aacute */ "a", /* small a, circumflex accent (â) - acirc */ "'", /* spacing acute (´) - acute */ "ae", /* small ae diphthong (ligature) (æ) - aelig */ "`a", /* small a, grave accent (è) - agrave */ "&", /* ampersand (&) - amp */ "a", /* small a, ring (å) - aring */ "a", /* small a, tilde (ã) - atilde */ #ifdef LY_UMLAUT "ae", /* small a, dieresis or umlaut mark (ä) - auml */ #else "a", /* small a, dieresis or umlaut mark (ä) - auml */ #endif /* LY_UMLAUT */ "|", /* broken vertical bar (¦) - brkbar */ "|", /* broken vertical bar (¦) - brvbar */ "c", /* small c, cedilla (ç) - ccedil */ ",", /* spacing cedilla (¸) - cedil */ "-c-", /* cent sign (¢) - cent */ "(c)", /* copyright sign (©) - copy */ "CUR", /* currency sign (¤) - curren */ "DEG", /* degree sign (°) - deg */ "\042", /* spacing dieresis (¨) - die */ "/", /* division sign (÷) - divide */ "e", /* small e, acute accent (é) - eacute */ "e", /* small e, circumflex accent (ê) - ecirc */ "e", /* small e, grave accent (è) - egrave */ "-", /* dash the width of emsp - emdash */ "\002", /* emsp NEVER CHANGE THIS - emsp */ "-", /* dash the width of ensp - endash */ "\002", /* ensp NEVER CHANGE THIS - ensp */ "dh", /* small eth, Icelandic eth (ð) */ "e", /* small e, dieresis or umlaut mark (ë) - euml */ " 1/2", /* fraction 1/2 (½) - frac12 */ " 1/4", /* fraction 1/4 (¼) - frac14 */ " 3/4", /* fraction 3/4 (¾) - frac34 */ ">", /* greater than (>) - gt */ "-", /* spacing macron (¯) - hibar */ "i", /* small i, acute accent (í) - iacute */ "i", /* small i, circumflex accent (î) - icirc */ "!", /* inverted exclamation mark (¡) - iexcl */ "`i", /* small i, grave accent (ì) - igrave */ "?", /* inverted question mark (¿) - iquest */ "i", /* small i, dieresis or umlaut mark (ï) - iuml */ "<<", /* angle quotation mark, left («) - laquo */ "<", /* less than - lt (<) */ "-", /* spacing macron (¯) - macr */ "-", /* dash the width of emsp - mdash */ "u", /* micro sign (µ) - micro */ ".", /* middle dot (·) - middot */ "\001", /* nbsp non-breaking space NEVER CHANGE THIS - nbsp */ "-", /* dash the width of ensp - ndash */ "NOT", /* negation sign (¬) - not */ "n", /* small n, tilde (ñ) - ntilde */ "o", /* small o, acute accent (ó) - oacute */ "o", /* small o, circumflex accent (ô) - ocirc */ "o", /* small o, grave accent (ò) - ograve */ "-a", /* feminine ordinal indicator (ª) - ordf */ "-o", /* masculine ordinal indicator (º) - ordm */ "o", /* small o, slash (ø) - oslash */ "o", /* small o, tilde (õ) - otilde */ #ifdef LY_UMLAUT "oe", /* small o, dieresis or umlaut mark (ö) - ouml */ #else "o", /* small o, dieresis or umlaut mark (ö) - ouml */ #endif /* LY_UMLAUT */ "P:", /* paragraph sign (¶) - para */ "+-", /* plus-or-minus sign (±) - plusmn */ "-L-", /* pound sign (£) - pound */ "\"", /* quote '"' (") - quot */ ">>", /* angle quotation mark, right (») - raquo */ "(R)", /* circled R registered sign (®) - reg */ "S:", /* section sign (§) - sect */ "\007", /* soft hyphen (­) NEVER CHANGE THIS - shy */ "^1", /* superscript 1 (¹) - sup1 */ "^2", /* superscript 2 (²) - sup2 */ "^3", /* superscript 3 (³) - sup3 */ "ss", /* small sharp s, German (sz ligature) (ß) - szlig */ "\002", /* thin space - not collapsed NEVER CHANGE THIS - thinsp */ "p", /* small thorn, Icelandic (þ) - thorn */ "*", /* multiplication sign (×) - times */ "(TM)", /* circled TM trade mark sign (™) - trade */ "u", /* small u, acute accent (ú) - uacute */ "u", /* small u, circumflex accent (û) - ucirc */ "u", /* small u, grave accent (ù) - ugrave */ "\042", /* spacing dieresis (¨) - uml */ #ifdef LY_UMLAUT "ue", /* small u, dieresis or umlaut mark (ü) - uuml */ #else "u", /* small u, dieresis or umlaut mark (ü) - uuml */ #endif /* LY_UMLAUT */ "y", /* small y, acute accent (ý) - yacute */ "YEN", /* yen sign (¥) - yen */ "y", /* small y, dieresis or umlaut mark (ÿ) - yuml */ }; /* * Add your new character sets HERE (but only if you can't construct Unicode * tables for them). - FM */ /* * Add the array name to LYCharSets */ STRING2PTR LYCharSets[MAXCHARSETS] = { ISO_Latin1, /* ISO Latin 1 */ SevenBitApproximations, /* 7 Bit Approximations */ }; /* * Add the name that the user will see below. The order of LYCharSets and * LYchar_set_names MUST be the same */ const char *LYchar_set_names[MAXCHARSETS + 1] = { "Western (ISO-8859-1)", "7 bit approximations (US-ASCII)", (char *) 0 }; /* * Associate additional pieces of info with each of the charsets listed above. * Will be automatically modified (and extended) by charset translations which * are loaded using the chartrans mechanism. Most important piece of info to * put here is a MIME charset name. Used for chartrans (see UCDefs.h). The * order of LYCharSets and LYCharSet_UC MUST be the same. * * Note that most of the charsets added by the new mechanism in src/chrtrans * don't show up here at all. They don't have to. */ LYUCcharset LYCharSet_UC[MAXCHARSETS] = { /* * Zero position placeholder and HTMLGetEntityUCValue() reference. - FM */ {-1, "iso-8859-1", UCT_ENC_8BIT, 0, UCT_REP_IS_LAT1, UCT_CP_IS_LAT1, UCT_R_LAT1, UCT_R_LAT1}, /* * Placeholders for Unicode tables. - FM */ {-1, "us-ascii", UCT_ENC_7BIT, 0, UCT_REP_SUBSETOF_LAT1, UCT_CP_SUBSETOF_LAT1, UCT_R_ASCII, UCT_R_ASCII}, }; /* * Add the code of the the lowest character with the high bit set that can be * directly displayed. The order of LYCharSets and LYlowest_eightbit MUST be * the same. * * (If charset have chartrans unicode table, LYlowest_eightbit will be * verified/modified anyway.) */ int LYlowest_eightbit[MAXCHARSETS] = { 160, /* ISO Latin 1 */ 999, /* 7 bit approximations */ }; /* * Function to set the handling of selected character sets based on the current * LYUseDefaultRawMode value. - FM */ void HTMLSetCharacterHandling(int i) { int chndl = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset); BOOLEAN LYRawMode_flag = LYRawMode; int UCLYhndl_for_unspec_flag = UCLYhndl_for_unspec; if (LYCharSet_UC[i].enc != UCT_ENC_CJK) { HTCJK = NOCJK; kanji_code = NOKANJI; if (i == chndl) LYRawMode = LYUseDefaultRawMode; else LYRawMode = (BOOL) (!LYUseDefaultRawMode); HTPassEightBitNum = (BOOL) ((LYCharSet_UC[i].codepoints & UCT_CP_SUPERSETOF_LAT1) || (LYCharSet_UC[i].like8859 & UCT_R_HIGH8BIT)); if (LYRawMode) { HTPassEightBitRaw = (BOOL) (LYlowest_eightbit[i] <= 160); } else { HTPassEightBitRaw = FALSE; } if (LYRawMode || i == chndl) { HTPassHighCtrlRaw = (BOOL) (LYlowest_eightbit[i] <= 130); } else { HTPassHighCtrlRaw = FALSE; } HTPassHighCtrlNum = FALSE; } else { /* CJK encoding: */ const char *mime = LYCharSet_UC[i].MIMEname; if (!strcmp(mime, "euc-cn")) { HTCJK = CHINESE; kanji_code = EUC; } else if (!strcmp(mime, "euc-jp")) { HTCJK = JAPANESE; kanji_code = EUC; } else if (!strcmp(mime, "shift_jis")) { HTCJK = JAPANESE; kanji_code = SJIS; } else if (!strcmp(mime, "euc-kr")) { HTCJK = KOREAN; kanji_code = EUC; } else if (!strcmp(mime, "big5")) { HTCJK = TAIPEI; kanji_code = EUC; } /* for any CJK: */ if (!LYUseDefaultRawMode) HTCJK = NOCJK; LYRawMode = (BOOL) (IS_CJK_TTY ? TRUE : FALSE); HTPassEightBitRaw = FALSE; HTPassEightBitNum = FALSE; HTPassHighCtrlRaw = (BOOL) (IS_CJK_TTY ? TRUE : FALSE); HTPassHighCtrlNum = FALSE; } /* * Comment for coding below: * UCLYhndl_for_unspec is "current" state with LYRawMode, but * UCAssume_MIMEcharset is independent from LYRawMode: holds the history * and may be changed from 'O'ptions menu only. - LP */ if (LYRawMode) { UCLYhndl_for_unspec = i; /* UCAssume_MIMEcharset not changed! */ } else { if (chndl != i && (LYCharSet_UC[i].enc != UCT_ENC_CJK || LYCharSet_UC[chndl].enc != UCT_ENC_CJK)) { UCLYhndl_for_unspec = chndl; /* fall to UCAssume_MIMEcharset */ } else { UCLYhndl_for_unspec = LATIN1; /* UCAssume_MIMEcharset not changed! */ } } #ifdef USE_SLANG if (LYlowest_eightbit[i] > 191) { /* * Higher than this may output cntrl chars to screen. - KW */ SLsmg_Display_Eight_Bit = 191; } else { SLsmg_Display_Eight_Bit = LYlowest_eightbit[i]; } #endif /* USE_SLANG */ ena_csi(LYlowest_eightbit[current_char_set] > 155); /* some diagnostics */ if (TRACE) { if (LYRawMode_flag != LYRawMode) CTRACE((tfp, "HTMLSetCharacterHandling: LYRawMode changed %s -> %s\n", (LYRawMode_flag ? "ON" : "OFF"), (LYRawMode ? "ON" : "OFF"))); if (UCLYhndl_for_unspec_flag != UCLYhndl_for_unspec) CTRACE((tfp, "HTMLSetCharacterHandling: UCLYhndl_for_unspec changed %d -> %d\n", UCLYhndl_for_unspec_flag, UCLYhndl_for_unspec)); } return; } /* * Function to set HTCJK based on "in" and "out" charsets. */ void Set_HTCJK(const char *inMIMEname, const char *outMIMEname) { /* need not check for synonyms: MIMEname's got from LYCharSet_UC */ if (LYRawMode) { if ((!strcmp(inMIMEname, "euc-jp") || #ifdef EXP_JAPANESEUTF8_SUPPORT !strcmp(inMIMEname, "utf-8") || #endif !strcmp(inMIMEname, "shift_jis")) && (!strcmp(outMIMEname, "euc-jp") || !strcmp(outMIMEname, "shift_jis"))) { HTCJK = JAPANESE; } else if (!strcmp(inMIMEname, "euc-cn") && !strcmp(outMIMEname, "euc-cn")) { HTCJK = CHINESE; } else if (!strcmp(inMIMEname, "big5") && !strcmp(outMIMEname, "big5")) { HTCJK = TAIPEI; } else if (!strcmp(inMIMEname, "euc-kr") && !strcmp(outMIMEname, "euc-kr")) { HTCJK = KOREAN; } else { HTCJK = NOCJK; } } else { HTCJK = NOCJK; } } /* * Function to set the LYDefaultRawMode value based on the selected character * set. - FM * * Currently unused: the default value so obvious that LYUseDefaultRawMode * utilized directly by someone's mistake. - LP */ static void HTMLSetRawModeDefault(int i) { LYDefaultRawMode = (BOOL) (LYCharSet_UC[i].enc == UCT_ENC_CJK); return; } /* * Function to set the LYUseDefaultRawMode value based on the selected * character set and the current LYRawMode value. - FM */ void HTMLSetUseDefaultRawMode(int i, int modeflag) { if (LYCharSet_UC[i].enc != UCT_ENC_CJK) { int chndl = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset); if (i == chndl) LYUseDefaultRawMode = (BOOLEAN) modeflag; else LYUseDefaultRawMode = (BOOL) (!modeflag); } else /* CJK encoding: */ LYUseDefaultRawMode = (BOOLEAN) modeflag; return; } /* * Function to set the LYHaveCJKCharacterSet value based on the selected * character set. - FM */ static void HTMLSetHaveCJKCharacterSet(int i) { LYHaveCJKCharacterSet = (BOOL) (LYCharSet_UC[i].enc == UCT_ENC_CJK); return; } /* * Function to set the DisplayCharsetMatchLocale value based on the selected * character set. It is used in UPPER8 for 8bit case-insensitive search by * matching def7_uni.tbl images. - LP */ static void HTMLSetDisplayCharsetMatchLocale(int i) { BOOLEAN match; if (LYHaveCJKCharacterSet) { /* * We have no intention to pass CJK via UCTransChar if that happened. * Let someone from CJK correct this if necessary. */ DisplayCharsetMatchLocale = TRUE; /* old-style */ return; } else if (strncasecomp(LYCharSet_UC[i].MIMEname, "cp", 2) || strncasecomp(LYCharSet_UC[i].MIMEname, "windows", 7)) { /* * Assume dos/windows displays usually on remote terminal, hence it * rarely matches locale. (In fact, MS Windows codepoints locale are * never seen on UNIX). */ match = FALSE; } else { match = TRUE; /* guess, but see below */ #if !defined(LOCALE) if (LYCharSet_UC[i].enc != UCT_ENC_UTF8) /* * Leave true for utf-8 display - the code doesn't deal very well * with this case. - kw */ match = FALSE; #else if (UCForce8bitTOUPPER) { /* * Force disable locale (from lynx.cfg) */ match = FALSE; } #endif } DisplayCharsetMatchLocale = match; return; } /* * lynx 2.8/2.7.2(and more early) compatibility code: "human-readable" charset * names changes with time so we map that history names to MIME here to get old * lynx.cfg and (especially) .lynxrc always recognized. Please update this * table when you change "fullname" of any present charset. */ typedef struct _names_pairs { const char *fullname; const char *MIMEname; } names_pairs; /* *INDENT-OFF* */ static const names_pairs OLD_charset_names[] = { {"ISO Latin 1", "iso-8859-1"}, {"ISO Latin 2", "iso-8859-2"}, {"WinLatin1 (cp1252)", "windows-1252"}, {"DEC Multinational", "dec-mcs"}, {"Macintosh (8 bit)", "macintosh"}, {"NeXT character set", "next"}, {"KOI8-R Cyrillic", "koi8-r"}, {"Chinese", "euc-cn"}, {"Japanese (EUC)", "euc-jp"}, {"Japanese (SJIS)", "shift_jis"}, {"Korean", "euc-kr"}, {"Taipei (Big5)", "big5"}, {"Vietnamese (VISCII)", "viscii"}, {"7 bit approximations", "us-ascii"}, {"Transparent", "x-transparent"}, {"DosLatinUS (cp437)", "cp437"}, {"IBM PC character set", "cp437"}, {"DosLatin1 (cp850)", "cp850"}, {"IBM PC codepage 850", "cp850"}, {"DosLatin2 (cp852)", "cp852"}, {"PC Latin2 CP 852", "cp852"}, {"DosCyrillic (cp866)", "cp866"}, {"DosArabic (cp864)", "cp864"}, {"DosGreek (cp737)", "cp737"}, {"DosBaltRim (cp775)", "cp775"}, {"DosGreek2 (cp869)", "cp869"}, {"DosHebrew (cp862)", "cp862"}, {"WinLatin2 (cp1250)", "windows-1250"}, {"WinCyrillic (cp1251)", "windows-1251"}, {"WinGreek (cp1253)", "windows-1253"}, {"WinHebrew (cp1255)", "windows-1255"}, {"WinArabic (cp1256)", "windows-1256"}, {"WinBaltRim (cp1257)", "windows-1257"}, {"ISO Latin 3", "iso-8859-3"}, {"ISO Latin 4", "iso-8859-4"}, {"ISO 8859-5 Cyrillic", "iso-8859-5"}, {"ISO 8859-6 Arabic", "iso-8859-6"}, {"ISO 8859-7 Greek", "iso-8859-7"}, {"ISO 8859-8 Hebrew", "iso-8859-8"}, {"ISO-8859-8-I", "iso-8859-8"}, {"ISO-8859-8-E", "iso-8859-8"}, {"ISO 8859-9 (Latin 5)", "iso-8859-9"}, {"ISO 8859-10", "iso-8859-10"}, {"UNICODE UTF 8", "utf-8"}, {"RFC 1345 w/o Intro", "mnemonic+ascii+0"}, {"RFC 1345 Mnemonic", "mnemonic"}, {NULL, NULL}, /* terminated with NULL */ }; /* *INDENT-ON* */ /* * lynx 2.8/2.7.2 compatibility code: read "character_set" parameter from * lynx.cfg and .lynxrc in both MIME name and "human-readable" name (old and * new style). Returns -1 if not recognized. */ int UCGetLYhndl_byAnyName(char *value) { int i; if (value == NULL) return -1; LYTrimTrailing(value); CTRACE((tfp, "UCGetLYhndl_byAnyName(%s)\n", value)); /* search by name */ for (i = 0; (i < MAXCHARSETS && LYchar_set_names[i]); i++) { if (!strcmp(value, LYchar_set_names[i])) { return i; /* OK */ } } /* search by old name from 2.8/2.7.2 version */ for (i = 0; (OLD_charset_names[i].fullname); i++) { if (!strcmp(value, OLD_charset_names[i].fullname)) { return UCGetLYhndl_byMIME(OLD_charset_names[i].MIMEname); /* OK */ } } return UCGetLYhndl_byMIME(value); /* by MIME */ } /* * Entity names -- Ordered by ISO Latin 1 value. * --------------------------------------------- * For conversions of DECIMAL escaped entities. * Must be in order of ascending value. */ static const char *LYEntityNames[] = { /* NAME DECIMAL VALUE */ "nbsp", /* 160, non breaking space */ "iexcl", /* 161, inverted exclamation mark */ "cent", /* 162, cent sign */ "pound", /* 163, pound sign */ "curren", /* 164, currency sign */ "yen", /* 165, yen sign */ "brvbar", /* 166, broken vertical bar, (brkbar) */ "sect", /* 167, section sign */ "uml", /* 168, spacing dieresis */ "copy", /* 169, copyright sign */ "ordf", /* 170, feminine ordinal indicator */ "laquo", /* 171, angle quotation mark, left */ "not", /* 172, negation sign */ "shy", /* 173, soft hyphen */ "reg", /* 174, circled R registered sign */ "hibar", /* 175, spacing macron */ "deg", /* 176, degree sign */ "plusmn", /* 177, plus-or-minus sign */ "sup2", /* 178, superscript 2 */ "sup3", /* 179, superscript 3 */ "acute", /* 180, spacing acute (96) */ "micro", /* 181, micro sign */ "para", /* 182, paragraph sign */ "middot", /* 183, middle dot */ "cedil", /* 184, spacing cedilla */ "sup1", /* 185, superscript 1 */ "ordm", /* 186, masculine ordinal indicator */ "raquo", /* 187, angle quotation mark, right */ "frac14", /* 188, fraction 1/4 */ "frac12", /* 189, fraction 1/2 */ "frac34", /* 190, fraction 3/4 */ "iquest", /* 191, inverted question mark */ "Agrave", /* 192, capital A, grave accent */ "Aacute", /* 193, capital A, acute accent */ "Acirc", /* 194, capital A, circumflex accent */ "Atilde", /* 195, capital A, tilde */ "Auml", /* 196, capital A, dieresis or umlaut mark */ "Aring", /* 197, capital A, ring */ "AElig", /* 198, capital AE diphthong (ligature) */ "Ccedil", /* 199, capital C, cedilla */ "Egrave", /* 200, capital E, grave accent */ "Eacute", /* 201, capital E, acute accent */ "Ecirc", /* 202, capital E, circumflex accent */ "Euml", /* 203, capital E, dieresis or umlaut mark */ "Igrave", /* 204, capital I, grave accent */ "Iacute", /* 205, capital I, acute accent */ "Icirc", /* 206, capital I, circumflex accent */ "Iuml", /* 207, capital I, dieresis or umlaut mark */ "ETH", /* 208, capital Eth, Icelandic (or Latin2 Dstrok) */ "Ntilde", /* 209, capital N, tilde */ "Ograve", /* 210, capital O, grave accent */ "Oacute", /* 211, capital O, acute accent */ "Ocirc", /* 212, capital O, circumflex accent */ "Otilde", /* 213, capital O, tilde */ "Ouml", /* 214, capital O, dieresis or umlaut mark */ "times", /* 215, multiplication sign */ "Oslash", /* 216, capital O, slash */ "Ugrave", /* 217, capital U, grave accent */ "Uacute", /* 218, capital U, acute accent */ "Ucirc", /* 219, capital U, circumflex accent */ "Uuml", /* 220, capital U, dieresis or umlaut mark */ "Yacute", /* 221, capital Y, acute accent */ "THORN", /* 222, capital THORN, Icelandic */ "szlig", /* 223, small sharp s, German (sz ligature) */ "agrave", /* 224, small a, grave accent */ "aacute", /* 225, small a, acute accent */ "acirc", /* 226, small a, circumflex accent */ "atilde", /* 227, small a, tilde */ "auml", /* 228, small a, dieresis or umlaut mark */ "aring", /* 229, small a, ring */ "aelig", /* 230, small ae diphthong (ligature) */ "ccedil", /* 231, small c, cedilla */ "egrave", /* 232, small e, grave accent */ "eacute", /* 233, small e, acute accent */ "ecirc", /* 234, small e, circumflex accent */ "euml", /* 235, small e, dieresis or umlaut mark */ "igrave", /* 236, small i, grave accent */ "iacute", /* 237, small i, acute accent */ "icirc", /* 238, small i, circumflex accent */ "iuml", /* 239, small i, dieresis or umlaut mark */ "eth", /* 240, small eth, Icelandic */ "ntilde", /* 241, small n, tilde */ "ograve", /* 242, small o, grave accent */ "oacute", /* 243, small o, acute accent */ "ocirc", /* 244, small o, circumflex accent */ "otilde", /* 245, small o, tilde */ "ouml", /* 246, small o, dieresis or umlaut mark */ "divide", /* 247, division sign */ "oslash", /* 248, small o, slash */ "ugrave", /* 249, small u, grave accent */ "uacute", /* 250, small u, acute accent */ "ucirc", /* 251, small u, circumflex accent */ "uuml", /* 252, small u, dieresis or umlaut mark */ "yacute", /* 253, small y, acute accent */ "thorn", /* 254, small thorn, Icelandic */ "yuml", /* 255, small y, dieresis or umlaut mark */ }; /* * Function to return the entity names of ISO-8859-1 8-bit characters. - FM */ const char *HTMLGetEntityName(UCode_t code) { #define IntValue code int MaxValue = (TABLESIZE(LYEntityNames) - 1); if (IntValue < 0 || IntValue > MaxValue) { return ""; } return LYEntityNames[IntValue]; } /* * Function to return the UCode_t (long int) value for entity names. It * returns 0 if not found. * * unicode_entities[] handles all the names from old style entities[] too. * Lynx now calls unicode_entities[] only through this function: * HTMLGetEntityUCValue(). Note, we need not check for special characters here * in function or even before it, we should check them *after* invoking this * function, see put_special_unicodes() in SGML.c. * * In the future we will try to isolate all calls to entities[] in favor of new * unicode-based chartrans scheme. - LP */ UCode_t HTMLGetEntityUCValue(const char *name) { #include UCode_t value = 0; size_t i, high, low; int diff = 0; size_t number_of_unicode_entities = TABLESIZE(unicode_entities); /* * Make sure we have a non-zero length name. - FM */ if (isEmpty(name)) return (value); /* * Try UC_entity_info unicode_entities[]. */ for (low = 0, high = number_of_unicode_entities; high > low; diff < 0 ? (low = i + 1) : (high = i)) { /* * Binary search. */ i = (low + (high - low) / 2); diff = AS_cmp(unicode_entities[i].name, name); /* Case sensitive! */ if (diff == 0) { value = unicode_entities[i].code; break; } } return (value); } /* * Original comment - * Assume these are Microsoft code points, inflicted on us by FrontPage. - FM * * MS FrontPage uses syntax like ™ in 128-159 range and doesn't follow * Unicode standards for this area. Windows-1252 codepoints are assumed here. * * However see - * http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#character-encodings-0 */ UCode_t LYcp1252ToUnicode(UCode_t code) { if ((code == 1) || (code > 127 && code < 160)) { switch (code) { case 1: /* * WHITE SMILING FACE */ code = 0x263a; break; case 128: /* * EURO currency sign */ code = 0x20ac; break; case 130: /* * SINGLE LOW-9 QUOTATION MARK (sbquo) */ code = 0x201a; break; case 131: /* * LATIN SMALL LETTER F WITH HOOK */ code = 0x192; break; case 132: /* * DOUBLE LOW-9 QUOTATION MARK (bdquo) */ code = 0x201e; break; case 133: /* * HORIZONTAL ELLIPSIS (hellip) */ code = 0x2026; break; case 134: /* * DAGGER (dagger) */ code = 0x2020; break; case 135: /* * DOUBLE DAGGER (Dagger) */ code = 0x2021; break; case 136: /* * MODIFIER LETTER CIRCUMFLEX ACCENT */ code = 0x2c6; break; case 137: /* * PER MILLE SIGN (permil) */ code = 0x2030; break; case 138: /* * LATIN CAPITAL LETTER S WITH CARON */ code = 0x160; break; case 139: /* * SINGLE LEFT-POINTING ANGLE QUOTATION MARK (lsaquo) */ code = 0x2039; break; case 140: /* * LATIN CAPITAL LIGATURE OE */ code = 0x152; break; case 142: /* * LATIN CAPITAL LETTER Z WITH CARON */ code = 0x17d; break; case 145: /* * LEFT SINGLE QUOTATION MARK (lsquo) */ code = 0x2018; break; case 146: /* * RIGHT SINGLE QUOTATION MARK (rsquo) */ code = 0x2019; break; case 147: /* * LEFT DOUBLE QUOTATION MARK (ldquo) */ code = 0x201c; break; case 148: /* * RIGHT DOUBLE QUOTATION MARK (rdquo) */ code = 0x201d; break; case 149: /* * BULLET (bull) */ code = 0x2022; break; case 150: /* * EN DASH (ndash) */ code = 0x2013; break; case 151: /* * EM DASH (mdash) */ code = 0x2014; break; case 152: /* * SMALL TILDE (tilde) */ code = 0x02dc; break; case 153: /* * TRADE MARK SIGN (trade) */ code = 0x2122; break; case 154: /* * LATIN SMALL LETTER S WITH CARON */ code = 0x161; break; case 155: /* * SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (rsaquo) */ code = 0x203a; break; case 156: /* * LATIN SMALL LIGATURE OE */ code = 0x153; break; case 158: /* * LATIN SMALL LETTER Z WITH CARON */ code = 0x17e; break; case 159: /* * LATIN CAPITAL LETTER Y WITH DIAERESIS */ code = 0x178; break; default: /* * Undefined (by convention, use the replacement character). */ code = 0xfffd; break; } } return code; } /* * Function to select a character set and then set the character handling and * LYHaveCJKCharacterSet flag. - FM */ void HTMLUseCharacterSet(int i) { HTMLSetRawModeDefault(i); p_entity_values = LYCharSets[i]; HTMLSetCharacterHandling(i); /* set LYRawMode and CJK attributes */ HTMLSetHaveCJKCharacterSet(i); HTMLSetDisplayCharsetMatchLocale(i); return; } /* * Initializer, calls initialization function for the CHARTRANS handling. - KW */ int LYCharSetsDeclared(void) { UCInit(); return UCInitialized; } #ifdef USE_CHARSET_CHOICE void init_charset_subsets(void) { int i, n; int cur_display = 0; int cur_assumed = 0; /* add them to displayed values */ charset_subsets[UCLYhndl_for_unspec].hide_assumed = FALSE; charset_subsets[current_char_set].hide_display = FALSE; #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN /*all this stuff is for supporting old menu screen... */ for (i = 0; i < LYNumCharsets; ++i) { if (charset_subsets[i].hide_display == FALSE) { n = cur_display++; if (i == current_char_set) displayed_display_charset_idx = n; display_charset_map[n] = i; display_charset_choices[n] = LYchar_set_names[i]; } if (charset_subsets[i].hide_assumed == FALSE) { n = cur_assumed++; assumed_doc_charset_map[n] = i; assumed_charset_choices[n] = LYCharSet_UC[i].MIMEname; charset_subsets[i].assumed_idx = n; } display_charset_choices[cur_display] = NULL; assumed_charset_choices[cur_assumed] = NULL; } #endif } #endif /* USE_CHARSET_CHOICE */ lynx2-8-8/src/LYCharSets.h000644 023711 023712 00000010411 11716074571 015702 0ustar00dickeylynx000000 000000 /* * $LynxId: LYCharSets.h,v 1.34 2012/02/10 18:43:40 tom Exp $ */ #ifndef LYCHARSETS_H #define LYCHARSETS_H #ifndef HTUTILS_H #include #endif #include #ifndef UCMAP_H #include #endif /* !UCMAP_H */ #include #ifdef __cplusplus extern "C" { #endif extern BOOL HTPassEightBitRaw; extern BOOL HTPassEightBitNum; extern BOOL HTPassHighCtrlRaw; extern BOOL HTPassHighCtrlNum; extern BOOLEAN LYHaveCJKCharacterSet; extern BOOLEAN DisplayCharsetMatchLocale; extern HTkcode kanji_code; /* * currently active character set (internal handler) */ extern int current_char_set; /* * character set where it is safe to draw lines on boxes. */ extern int linedrawing_char_set; /* * Initializer, calls initialization function for the * CHARTRANS handling. - KW */ extern int LYCharSetsDeclared(void); extern STRING2PTR LYCharSets[]; extern const char *SevenBitApproximations[]; extern STRING2PTR p_entity_values; extern const char *LYchar_set_names[]; /* Full name, not MIME */ extern int LYlowest_eightbit[]; extern int LYNumCharsets; extern LYUCcharset LYCharSet_UC[]; extern int UCGetLYhndl_byAnyName(char *value); extern void HTMLSetCharacterHandling(int i); extern void HTMLSetUseDefaultRawMode(int i, int modeflag); extern void HTMLUseCharacterSet(int i); extern UCode_t HTMLGetEntityUCValue(const char *name); extern void Set_HTCJK(const char *inMIMEname, const char *outMIMEname); extern const char *HTMLGetEntityName(UCode_t code); UCode_t LYcp1252ToUnicode(UCode_t code); /* * HTMLGetEntityName calls LYEntityNames for iso-8859-1 entity names only. * This is an obsolete technique but widely used in the code. Note that * unicode number in general may have several equivalent entity names because * of synonyms. */ extern BOOL force_old_UCLYhndl_on_reload; extern int forced_UCLYhdnl; #ifndef USE_CHARSET_CHOICE # define ALL_CHARSETS_IN_O_MENU_SCREEN 1 #endif #ifdef USE_CHARSET_CHOICE typedef struct { BOOL hide_display; /* if FALSE, show in "display-charset" menu */ BOOL hide_assumed; /* if FALSE, show in "assumed-charset" menu */ #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN int assumed_idx; /* only this field is needed */ #endif } charset_subset_t; /* each element corresponds to charset in LYCharSets */ extern charset_subset_t charset_subsets[]; /* all zeros by default - i.e., all charsets allowed */ /* * true if the charset choices for display charset were requested by user via * lynx.cfg. It will remain FALSE if no "display_charset_choice" settings were * encountered in lynx.cfg */ extern BOOL custom_display_charset; /* similar to custom_display_charset */ extern BOOL custom_assumed_doc_charset; #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN /* this stuff is initialized after reading lynx.cfg and .lynxrc */ /* * These arrays map index of charset shown in menu to the index in LYCharsets[] */ extern int display_charset_map[]; extern int assumed_doc_charset_map[]; /* these arrays are NULL terminated */ extern const char *display_charset_choices[]; extern const char *assumed_charset_choices[]; extern int displayed_display_charset_idx; #endif /* this will be called after lynx.cfg and .lynxrc are read */ extern void init_charset_subsets(void); #endif /* USE_CHARSET_CHOICE */ #if !defined(NO_AUTODETECT_DISPLAY_CHARSET) # ifdef __EMX__ # define CAN_AUTODETECT_DISPLAY_CHARSET # ifdef EXP_CHARTRANS_AUTOSWITCH # define CAN_SWITCH_DISPLAY_CHARSET # endif # endif #endif #ifdef CAN_AUTODETECT_DISPLAY_CHARSET extern int auto_display_charset; #endif #ifdef CAN_SWITCH_DISPLAY_CHARSET enum switch_display_charset_t { SWITCH_DISPLAY_CHARSET_MAYBE, SWITCH_DISPLAY_CHARSET_REALLY, SWITCH_DISPLAY_CHARSET_RESIZE }; extern int Switch_Display_Charset(int ord, enum switch_display_charset_t really); extern int Find_Best_Display_Charset(int ord); extern char *charsets_directory; extern char *charset_switch_rules; extern int switch_display_charsets; extern int auto_other_display_charset; extern int codepages[2]; extern int real_charsets[2]; /* Non "auto-" charsets for the codepages */ #endif #ifdef __cplusplus } #endif #endif /* LYCHARSETS_H */ lynx2-8-8/src/LYCharUtils.c000644 023711 023712 00000252705 12245762550 016074 0ustar00dickeylynx000000 000000 /* * $LynxId: LYCharUtils.c,v 1.127 2013/11/28 11:17:59 tom Exp $ * * Functions associated with LYCharSets.c and the Lynx version of HTML.c - FM * ========================================================================== */ #include #include #define Lynx_HTML_Handler #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Used for nested lists. - FM */ int OL_CONTINUE = -29999; /* flag for whether CONTINUE is set */ int OL_VOID = -29998; /* flag for whether a count is set */ static size_t count_char(const char *value, int ch) { const char *found; size_t result = 0; while ((*value != '\0') && (found = StrChr(value, ch)) != NULL) { ++result; value = (found + 1); } return result; } /* * This function converts any ampersands in a pre-allocated string to "&". * If brackets is TRUE, it also converts any angle-brackets to "<" or ">". */ void LYEntify(char **in_out, int brackets) { char *source = *in_out; char *target; char *result = NULL; size_t count_AMPs = 0; size_t count_LTs = 0; size_t count_GTs = 0; #ifdef CJK_EX enum _state { S_text, S_esc, S_dollar, S_paren, S_nonascii_text, S_dollar_paren } state = S_text; int in_sjis = 0; #endif if (non_empty(source)) { count_AMPs = count_char(*in_out, '&'); if (brackets) { count_LTs = count_char(*in_out, '<'); count_GTs = count_char(*in_out, '>'); } if (count_AMPs != 0 || count_LTs != 0 || count_GTs != 0) { target = typecallocn(char, (strlen(*in_out) + (4 * count_AMPs) + (3 * count_LTs) + (3 * count_GTs) + 1)); if ((result = target) == NULL) outofmem(__FILE__, "LYEntify"); for (source = *in_out; *source; source++) { #ifdef CJK_EX if (IS_CJK_TTY) { switch (state) { case S_text: if (*source == '\033') { state = S_esc; *target++ = *source; continue; } break; case S_esc: if (*source == '$') { state = S_dollar; } else if (*source == '(') { state = S_paren; } else { state = S_text; } *target++ = *source; continue; case S_dollar: if (*source == '@' || *source == 'B' || *source == 'A') { state = S_nonascii_text; } else if (*source == '(') { state = S_dollar_paren; } else { state = S_text; } *target++ = *source; continue; case S_dollar_paren: if (*source == 'C') { state = S_nonascii_text; } else { state = S_text; } *target++ = *source; continue; case S_paren: if (*source == 'B' || *source == 'J' || *source == 'T') { state = S_text; } else if (*source == 'I') { state = S_nonascii_text; } else if (*source == '\033') { state = S_esc; } *target++ = *source; continue; case S_nonascii_text: if (*source == '\033') state = S_esc; *target++ = *source; continue; default: break; } if (*(source + 1) != '\0' && (IS_EUC(UCH(*source), UCH(*(source + 1))) || IS_SJIS(UCH(*source), UCH(*(source + 1)), in_sjis) || IS_BIG5(UCH(*source), UCH(*(source + 1))))) { *target++ = *source++; *target++ = *source; continue; } } #endif switch (*source) { case '&': *target++ = '&'; *target++ = 'a'; *target++ = 'm'; *target++ = 'p'; *target++ = ';'; break; case '<': if (brackets) { *target++ = '&'; *target++ = 'l'; *target++ = 't'; *target++ = ';'; break; } /* FALLTHRU */ case '>': if (brackets) { *target++ = '&'; *target++ = 'g'; *target++ = 't'; *target++ = ';'; break; } /* FALLTHRU */ default: *target++ = *source; break; } } *target = '\0'; FREE(*in_out); *in_out = result; } } } /* * Callers to LYEntifyTitle/LYEntifyValue do not look at the 'target' param. * Optimize things a little by avoiding the memory allocation if not needed, * as is usually the case. */ static BOOL MustEntify(const char *source) { BOOL result; #ifdef CJK_EX if (IS_CJK_TTY && StrChr(source, '\033') != 0) { result = TRUE; } else #endif { size_t length = strlen(source); size_t reject = strcspn(source, "<&>"); result = (BOOL) (length != reject); } return result; } /* * Wrappers for LYEntify() which do not assume that the source was allocated, * e.g., output from gettext(). */ const char *LYEntifyTitle(char **target, const char *source) { const char *result = 0; if (MustEntify(source)) { StrAllocCopy(*target, source); LYEntify(target, TRUE); result = *target; } else { result = source; } return result; } const char *LYEntifyValue(char **target, const char *source) { const char *result = 0; if (MustEntify(source)) { StrAllocCopy(*target, source); LYEntify(target, FALSE); result = *target; } else { result = source; } return result; } /* * This function trims characters <= that of a space (32), * including HT_NON_BREAK_SPACE (1) and HT_EN_SPACE (2), * but not ESC, from the heads of strings. - FM */ void LYTrimHead(char *str) { const char *s = str; if (isEmpty(s)) return; while (*s && WHITE(*s) && UCH(*s) != UCH(CH_ESC)) /* S/390 -- gil -- 1669 */ s++; if (s > str) { char *ns = str; while (*s) { *ns++ = *s++; } *ns = '\0'; } } /* * This function trims characters <= that of a space (32), * including HT_NON_BREAK_SPACE (1), HT_EN_SPACE (2), and * ESC from the tails of strings. - FM */ void LYTrimTail(char *str) { int i; if (isEmpty(str)) return; i = (int) strlen(str) - 1; while (i >= 0) { if (WHITE(str[i])) str[i] = '\0'; else break; i--; } } /* * This function should receive a pointer to the start * of a comment. It returns a pointer to the end ('>') * character of comment, or it's best guess if the comment * is invalid. - FM */ char *LYFindEndOfComment(char *str) { char *cp, *cp1; enum comment_state { start1, start2, end1, end2 } state; if (str == NULL) /* * We got NULL, so return NULL. - FM */ return NULL; if (StrNCmp(str, "' as terminator for comments" ), PARSE_FUN( "homepage", 4|NEED_FUNCTION_ARG, homepage_fun, "=URL\nset homepage separate from start page" ), PARSE_SET( "html5_charsets", 4|TOGGLE_ARG, html5_charsets, "toggles use of HTML5 charset replacements" ), PARSE_SET( "image_links", 4|TOGGLE_ARG, clickable_images, "toggles inclusion of links for all images" ), PARSE_STR( "index", 4|NEED_LYSTRING_ARG, indexfile, "=URL\nset the default index file to URL" ), PARSE_SET( "ismap", 4|TOGGLE_ARG, LYNoISMAPifUSEMAP, "toggles inclusion of ISMAP links when client-side\nMAPs are present" ), #ifdef USE_JUSTIFY_ELTS PARSE_SET( "justify", 4|SET_ARG, ok_justify, "do justification of text" ), #endif PARSE_INT( "link", 4|NEED_INT_ARG, crawl_count, "=NUMBER\nstarting count for lnk#.dat files produced by -crawl" ), PARSE_SET( "list_inline", 4|TOGGLE_ARG, dump_links_inline, "with -dump, forces it to show links inline with text" ), PARSE_SET( "listonly", 4|TOGGLE_ARG, dump_links_only, "with -dump, forces it to show only the list of links" ), PARSE_SET( "localhost", 4|SET_ARG, local_host_only, "disable URLs that point to remote hosts" ), #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) PARSE_SET( "locexec", 4|SET_ARG, local_exec_on_local_files, "enable local program execution from local files only" ), #endif /* EXEC_LINKS || EXEC_SCRIPTS */ #if defined(USE_COLOR_STYLE) PARSE_STR( "lss", 2|NEED_LYSTRING_ARG, lynx_lss_file2, "=FILENAME\nspecifies a lynx.lss file other than the default" ), #endif PARSE_FUN( "mime_header", 4|FUNCTION_ARG, mime_header_fun, "include mime headers and force source dump" ), PARSE_SET( "minimal", 4|TOGGLE_ARG, minimal_comments, "toggles minimal versus valid comment parsing" ), #ifdef EXP_NESTED_TABLES PARSE_SET( "nested_tables", 4|TOGGLE_ARG, nested_tables, "toggles nested-tables logic" ), #endif #ifndef DISABLE_NEWS PARSE_FUN( "newschunksize", 4|NEED_FUNCTION_ARG, newschunksize_fun, "=NUMBER\nnumber of articles in chunked news listings" ), PARSE_FUN( "newsmaxchunk", 4|NEED_FUNCTION_ARG, newsmaxchunk_fun, "=NUMBER\nmaximum news articles in listings before chunking" ), #endif #if USE_BLAT_MAILER PARSE_SET( "noblat", 4|TOGGLE_ARG, mail_is_blat, "select mail tool (`"THIS_BLAT_MAIL"' ==> `"SYSTEM_MAIL"')" ), #endif PARSE_FUN( "nobold", 4|FUNCTION_ARG, nobold_fun, "disable bold video-attribute" ), PARSE_FUN( "nobrowse", 4|FUNCTION_ARG, nobrowse_fun, "disable directory browsing" ), PARSE_SET( "nocc", 4|SET_ARG, LYNoCc, "disable Cc: prompts for self copies of mailings" ), PARSE_FUN( "nocolor", 4|FUNCTION_ARG, nocolor_fun, "turn off color support" ), #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) PARSE_SET( "noexec", 4|UNSET_ARG, local_exec, "disable local program execution (DEFAULT)" ), #endif /* EXEC_LINKS || EXEC_SCRIPTS */ PARSE_SET( "nofilereferer", 4|SET_ARG, no_filereferer, "disable transmission of Referer headers for file URLs" ), PARSE_SET( "nolist", 4|SET_ARG, no_list, "disable the link list feature in dumps" ), PARSE_SET( "nolog", 4|UNSET_ARG, error_logging, "disable mailing of error messages to document owners" ), PARSE_SET( "nomargins", 4|SET_ARG, no_margins, "disable the right/left margins in the default\nstyle-sheet" ), PARSE_FUN( "nomore", 4|FUNCTION_ARG, nomore_fun, "disable -more- string in statusline messages" ), #if defined(HAVE_SIGACTION) && defined(SIGWINCH) PARSE_SET( "nonrestarting_sigwinch", 4|SET_ARG, LYNonRestartingSIGWINCH, "\nmake window size change handler non-restarting" ), #endif /* HAVE_SIGACTION */ PARSE_SET( "nonumbers", 4|SET_ARG, no_numbers, "disable the link/form numbering feature in dumps" ), PARSE_FUN( "nopause", 4|FUNCTION_ARG, nopause_fun, "disable forced pauses for statusline messages" ), PARSE_SET( "noprint", 4|SET_ARG, no_print, "disable some print functions, like -restrictions=print" ), PARSE_SET( "noredir", 4|SET_ARG, no_url_redirection, "don't follow Location: redirection" ), PARSE_SET( "noreferer", 4|SET_ARG, LYNoRefererHeader, "disable transmission of Referer headers" ), PARSE_FUN( "noreverse", 4|FUNCTION_ARG, noreverse_fun, "disable reverse video-attribute" ), #ifdef SOCKS PARSE_SET( "nosocks", 2|UNSET_ARG, socks_flag, "don't use SOCKS proxy for this session" ), #endif PARSE_SET( "nostatus", 4|SET_ARG, no_statusline, "disable the miscellaneous information messages" ), PARSE_SET( "notitle", 4|SET_ARG, no_title, "disable the title at the top of each page" ), PARSE_FUN( "nounderline", 4|FUNCTION_ARG, nounderline_fun, "disable underline video-attribute" ), #ifdef MISC_EXP PARSE_FUN( "nozap", 4|FUNCTION_ARG, nozap_fun, "=DURATION (\"initially\" or \"full\") disable checks for 'z' key" ), #endif PARSE_SET( "number_fields", 4|SET_ARG, number_fields, "force numbering of links as well as form input fields" ), PARSE_SET( "number_links", 4|SET_ARG, number_links, "force numbering of links" ), #ifdef DISP_PARTIAL PARSE_SET( "partial", 4|TOGGLE_ARG, display_partial_flag, "toggles display partial pages while downloading" ), PARSE_INT( "partial_thres", 4|NEED_INT_ARG, partial_threshold, "[=NUMBER]\nnumber of lines to render before repainting display\n\ with partial-display logic" ), #endif #ifndef DISABLE_FTP PARSE_SET( "passive-ftp", 4|TOGGLE_ARG, ftp_passive, "toggles passive ftp connection" ), #endif PARSE_FUN( "pauth", 4|NEED_FUNCTION_ARG, pauth_fun, "=id:pw\nauthentication information for protected proxy server" ), PARSE_SET( "popup", 4|UNSET_ARG, LYUseDefSelPop, "toggles handling of single-choice SELECT options via\n\ popup windows or as lists of radio buttons" ), PARSE_FUN( "post_data", 2|FUNCTION_ARG, post_data_fun, "user data for post forms, read from stdin,\n\ terminated by '---' on a line" ), PARSE_SET( "preparsed", 4|SET_ARG, LYPreparsedSource, "show parsed text/html with -source and in source view\n\ to visualize how lynx behaves with invalid HTML" ), #ifdef USE_PRETTYSRC PARSE_SET( "prettysrc", 4|SET_ARG, LYpsrc, "do syntax highlighting and hyperlink handling in source\nview" ), #endif PARSE_SET( "print", 4|UNSET_ARG, no_print, "enable print functions (DEFAULT), opposite of -noprint" ), PARSE_SET( "pseudo_inlines", 4|TOGGLE_ARG, pseudo_inline_alts, "toggles pseudo-ALTs for inlines with no ALT string" ), PARSE_SET( "raw", 4|UNSET_ARG, LYUseDefaultRawMode, "toggles default setting of 8-bit character translations\n\ or CJK mode for the startup character set" ), PARSE_SET( "realm", 4|SET_ARG, check_realm, "restricts access to URLs in the starting realm" ), PARSE_INT( "read_timeout", 4|NEED_INT_ARG, reading_timeout, "=N\nset the N-second read-timeout" ), PARSE_SET( "reload", 4|SET_ARG, reloading, "flushes the cache on a proxy server\n(only the first document affected)" ), PARSE_FUN( "restrictions", 4|FUNCTION_ARG, restrictions_fun, "=[options]\nuse -restrictions to see list" ), PARSE_SET( "resubmit_posts", 4|TOGGLE_ARG, LYresubmit_posts, "toggles forced resubmissions (no-cache) of forms with\n\ method POST when the documents they returned are sought\n\ with the PREV_DOC command or from the History List" ), PARSE_SET( "rlogin", 4|UNSET_ARG, rlogin_ok, "disable rlogins" ), #if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401 PARSE_FUN( "scrsize", 4|NEED_FUNCTION_ARG, scrsize_fun, "=width,height\nsize of window" ), #endif #ifdef USE_SCROLLBAR PARSE_SET( "scrollbar", 4|TOGGLE_ARG, LYShowScrollbar, "toggles showing scrollbar" ), PARSE_SET( "scrollbar_arrow", 4|TOGGLE_ARG, LYsb_arrow, "toggles showing arrows at ends of the scrollbar" ), #endif PARSE_FUN( "selective", 4|FUNCTION_ARG, selective_fun, "require .www_browsable files to browse directories" ), #ifdef USE_SESSIONS PARSE_STR( "session", 2|NEED_LYSTRING_ARG, session_file, "=FILENAME\nresumes from specified file on startup and\n\ saves session to that file on exit" ), PARSE_STR( "sessionin", 2|NEED_LYSTRING_ARG, sessionin_file, "=FILENAME\nresumes session from specified file" ), PARSE_STR( "sessionout", 2|NEED_LYSTRING_ARG, sessionout_file, "=FILENAME\nsaves session to specified file" ), #endif /* USE_SESSIONS */ PARSE_SET( "short_url", 4|SET_ARG, long_url_ok, "enables examination of beginning and end of long URL in\nstatus line" ), PARSE_SET( "show_cfg", 1|SET_ARG, show_cfg, "Show `LYNX.CFG' setting" ), PARSE_SET( "show_cursor", 4|TOGGLE_ARG, LYUseDefShoCur, "toggles hiding of the cursor in the lower right corner" ), #ifdef USE_READPROGRESS PARSE_SET( "show_rate", 4|TOGGLE_ARG, LYShowTransferRate, "toggles display of transfer rate" ), #endif PARSE_SET( "soft_dquotes", 4|TOGGLE_ARG, soft_dquotes, "toggles emulation of the old Netscape and Mosaic\n\ bug which treated '>' as a co-terminator for\ndouble-quotes and tags" ), PARSE_FUN( "source", 4|FUNCTION_ARG, source_fun, "dump the source of the first file to stdout and exit" ), PARSE_SET( "stack_dump", 4|SET_ARG, stack_dump, "disable SIGINT cleanup handler" ), PARSE_SET( "startfile_ok", 4|SET_ARG, startfile_ok, "allow non-http startfile and homepage with -validate" ), PARSE_SET( "stderr", 4|SET_ARG, dump_to_stderr, "write warning messages to standard error when -dump\nor -source is used" ), PARSE_SET( "stdin", 4|SET_ARG, startfile_stdin, "read startfile from standard input" ), #ifdef SYSLOG_REQUESTED_URLS PARSE_STR( "syslog", 4|NEED_LYSTRING_ARG, syslog_txt, "=text\ninformation for syslog call" ), PARSE_SET( "syslog-urls", 4|SET_ARG, syslog_requested_urls, "log requested URLs with syslog" ), #endif PARSE_SET( "tagsoup", 4|SET_ARG, DTD_recovery, "use TagSoup rather than SortaSGML parser" ), PARSE_SET( "telnet", 4|UNSET_ARG, telnet_ok, "disable telnets" ), PARSE_STR( "term", 4|NEED_STRING_ARG, terminal, "=TERM\nset terminal type to TERM" ), #ifdef _WINDOWS PARSE_INT( "timeout", 4|INT_ARG, lynx_timeout, "=NUMBER\nset TCP/IP timeout" ), #endif PARSE_SET( "tlog", 2|TOGGLE_ARG, LYUseTraceLog, "toggles use of a Lynx Trace Log for the current\nsession" ), #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION PARSE_SET( "tna", 4|SET_ARG, textfields_activation_option, "turn on \"Textfields Need Activation\" mode" ), #endif #ifndef NO_LYNX_TRACE PARSE_SET( "trace", 1|SET_ARG, WWW_TraceFlag, "turns on Lynx trace mode" ), PARSE_INT( "trace_mask", 1|INT_ARG, WWW_TraceMask, "customize Lynx trace mode" ), #endif PARSE_FUN( "traversal", 4|FUNCTION_ARG, traversal_fun, "traverse all http links derived from startfile" ), PARSE_SET( "trim_input_fields", 2|SET_ARG, LYtrimInputFields, "\ntrim input text/textarea fields in forms" ), PARSE_SET( "underline_links",4|TOGGLE_ARG, LYUnderlineLinks, "toggles use of underline/bold attribute for links" ), PARSE_SET( "underscore", 4|TOGGLE_ARG, use_underscore, "toggles use of _underline_ format in dumps" ), PARSE_SET( "unique_urls", 4|TOGGLE_ARG, unique_urls, "toggles use of unique-urls setting for -dump and -listonly options" ), #if defined(USE_MOUSE) PARSE_SET( "use_mouse", 4|SET_ARG, LYUseMouse, "turn on mouse support" ), #endif PARSE_STR( "useragent", 4|NEED_LYSTRING_ARG, LYUserAgent, "=Name\nset alternate Lynx User-Agent header" ), PARSE_SET( "validate", 2|SET_ARG, LYValidate, "accept only http URLs (meant for validation)\n\ implies more restrictions than -anonymous, but\n\ goto is allowed for http and https" ), PARSE_SET( "verbose", 4|TOGGLE_ARG, verbose_img, "toggles [LINK], [IMAGE] and [INLINE] comments\n\ with filenames of these images" ), PARSE_FUN( "version", 1|FUNCTION_ARG, version_fun, "print Lynx version information" ), PARSE_SET( "vikeys", 4|SET_ARG, vi_keys, "enable vi-like key movement" ), #ifdef __DJGPP__ PARSE_SET( "wdebug", 4|TOGGLE_ARG, watt_debug, "enables Waterloo tcp/ip packet debug. Prints to watt\ndebugfile" ), #endif /* __DJGPP__ */ PARSE_FUN( "width", 4|NEED_FUNCTION_ARG, width_fun, "=NUMBER\nscreen width for formatting of dumps (default is 80)" ), #ifndef NO_DUMP_WITH_BACKSPACES PARSE_SET( "with_backspaces", 4|SET_ARG, with_backspaces, "emit backspaces in output if -dumping or -crawling\n(like 'man' does)" ), #endif PARSE_SET( "xhtml-parsing", 4|SET_ARG, LYxhtml_parsing, "enable XHTML 1.0 parsing" ), PARSE_NIL }; /* *INDENT-ON* */ static void print_help_strings(const char *name, const char *help, const char *value, int option) { int pad; int c; int first; int field_width = 20; pad = field_width - (2 + option + (int) strlen(name)); fprintf(stdout, " %s%s", option ? "-" : "", name); if (*help != '=') { pad--; while (pad > 0) { fputc(' ', stdout); pad--; } fputc(' ', stdout); /* at least one space */ first = 0; } else { first = pad; } if (StrChr(help, '\n') == 0) { fprintf(stdout, "%s", help); } else { while ((c = *help) != 0) { if (c == '\n') { if ((pad = --first) < 0) { pad = field_width; } else { c = ' '; } fputc(c, stdout); while (pad--) fputc(' ', stdout); } else { fputc(c, stdout); } help++; first--; } } if (value) printf(" (%s)", value); fputc('\n', stdout); } static void print_help_and_exit(int exit_status) { Config_Type *p; if (pgm == NULL) pgm = "lynx"; SetOutputMode(O_TEXT); fprintf(stdout, gettext("USAGE: %s [options] [file]\n"), pgm); fprintf(stdout, gettext("Options are:\n")); #ifdef VMS print_help_strings("", "receive the arguments from stdin (enclose\n\ in double-quotes (\"-\") on VMS)", NULL, TRUE); #else print_help_strings("", "receive options and arguments from stdin", NULL, TRUE); #endif /* VMS */ for (p = Arg_Table; p->name != 0; p++) { char temp[LINESIZE], *value = temp; ParseUnionPtr q = ParseUnionOf(p); switch (p->type & ARG_TYPE_MASK) { case TOGGLE_ARG: case SET_ARG: strcpy(temp, *(q->set_value) ? "on" : "off"); break; case UNSET_ARG: strcpy(temp, *(q->set_value) ? "off" : "on"); break; case INT_ARG: sprintf(temp, "%d", *(q->int_value)); break; case TIME_ARG: sprintf(temp, SECS_FMT, (double) Secs2SECS(*(q->int_value))); break; case STRING_ARG: if ((value = *(q->str_value)) != 0 && !*value) value = 0; break; default: value = 0; break; } print_help_strings(p->name, p->help_string, value, TRUE); } SetOutputMode(O_BINARY); exit_immediately(exit_status); } /* * This function performs a string comparison on two strings a and b. a is * assumed to be an ordinary null terminated string, but b may be terminated * by an '=', '+' or '-' character. If terminated by '=', *c will be pointed * to the character following the '='. If terminated by '+' or '-', *c will * be pointed to that character. (+/- added for toggle processing - BL.) * If a and b match, it returns 1. Otherwise 0 is returned. */ static int arg_eqs_parse(const char *a, char *b, char **c) { int result = -1; *c = NULL; while (result < 0) { if ((*a != *b) || (*a == 0) || (*b == 0)) { if (*a == 0) { switch (*b) { case '\t': /* embedded blank when reading stdin */ case ' ': *c = LYSkipBlanks(b); result = 1; break; case '=': case ':': *c = b + 1; result = 1; break; case '-': #if OPTNAME_ALLOW_DASHES if (isalpha(UCH(b[1]))) { result = 0; break; } #endif /* FALLTHRU */ case '+': *c = b; result = 1; break; case 0: result = 1; break; default: result = 0; break; } } else { #if OPTNAME_ALLOW_DASHES if (!(*a == '_' && *b == '-')) #endif result = 0; } } a++; b++; } return result; } #define is_true(s) (*s == '1' || *s == '+' || !strcasecomp(s, "on") || !strcasecomp(s, "true")) #define is_false(s) (*s == '0' || *s == '-' || !strcasecomp(s, "off") || !strcasecomp(s, "false")) /* * Parse an option. * argv[] points to the beginning of the unprocessed options. * mask is used to select certain options which must be processed * before others. * countp (if nonnull) points to an index into argv[], which is updated * to reflect option values which are also parsed. */ static BOOL parse_arg(char **argv, unsigned mask, int *countp) { Config_Type *p; char *arg_name; #if EXTENDED_STARTFILE_RECALL static BOOLEAN no_options_further = FALSE; /* set to TRUE after '--' argument */ static int nof_index = 0; /* set the index of -- argument */ #endif arg_name = argv[0]; CTRACE((tfp, "parse_arg(arg_name=%s, mask=%u, count=%d)\n", arg_name, mask, countp ? *countp : -1)); #if EXTENDED_STARTFILE_RECALL if (mask == (unsigned) ((countp != 0) ? 0 : 1)) { no_options_further = FALSE; /* want to reset nonoption when beginning scan for --stdin */ if (nonoption != 0) { FREE(nonoption); } } #endif /* * Check for a command line startfile. - FM */ if (*arg_name != '-' #if EXTENDED_OPTION_LOGIC || ((no_options_further == TRUE) && (countp != 0) && (nof_index < (*countp))) #endif ) { #if EXTENDED_STARTFILE_RECALL /* * On the last pass (mask==4), check for cases where we may want to * provide G)oto history for multiple startfiles. */ if (mask == 4) { if (nonoption != 0) { LYEnsureAbsoluteURL(&nonoption, "NONOPTION", FALSE); HTAddGotoURL(nonoption); FREE(nonoption); } StrAllocCopy(nonoption, arg_name); } #endif StrAllocCopy(startfile, arg_name); LYEscapeStartfile(&startfile); #ifdef _WINDOWS /* 1998/01/14 (Wed) 20:11:17 */ HTUnEscape(startfile); { char *q = startfile; while (*q++) { if (*q == '|') *q = ':'; } } #endif CTRACE((tfp, "parse_arg startfile:%s\n", startfile)); return (BOOL) (countp != 0); } #if EXTENDED_OPTION_LOGIC if (strcmp(arg_name, "--") == 0) { no_options_further = TRUE; nof_index = countp ? *countp : -1; return TRUE; } #endif /* lose the first '-' character */ arg_name++; /* * Skip any lone "-" arguments, because we've loaded the stdin input into * an HTList structure for special handling. - FM */ if (*arg_name == 0) return TRUE; /* allow GNU-style options with -- prefix */ if (*arg_name == '-') ++arg_name; CTRACE((tfp, "parse_arg lookup(%s)\n", arg_name)); p = Arg_Table; while (p->name != 0) { ParseUnionPtr q = ParseUnionOf(p); ParseFunc fun; char *next_arg = NULL; char *temp_ptr = NULL; if ((p->name[0] != *arg_name) || (0 == arg_eqs_parse(p->name, arg_name, &next_arg))) { p++; continue; } if (p->type & NEED_NEXT_ARG) { if (next_arg == 0) { next_arg = argv[1]; if ((countp != 0) && (next_arg != 0)) (*countp)++; } CTRACE((tfp, "...arg:%s\n", NONNULL(next_arg))); } /* ignore option if it's not our turn */ if (((unsigned) (p->type) & mask) == 0) { CTRACE((tfp, "...skip (mask %u/%d)\n", mask, p->type & 7)); return FALSE; } switch (p->type & ARG_TYPE_MASK) { case TOGGLE_ARG: /* FALLTHRU */ case SET_ARG: /* FALLTHRU */ case UNSET_ARG: if (q->set_value != 0) { if (next_arg == 0) { switch (p->type & ARG_TYPE_MASK) { case TOGGLE_ARG: *(q->set_value) = (BOOL) !(*(q->set_value)); break; case SET_ARG: *(q->set_value) = TRUE; break; case UNSET_ARG: *(q->set_value) = FALSE; break; } } else if (is_true(next_arg)) { *(q->set_value) = TRUE; } else if (is_false(next_arg)) { *(q->set_value) = FALSE; } /* deliberately ignore anything else - BL */ } break; case FUNCTION_ARG: fun = q->fun_value; if (0 != fun) { if (-1 == (*fun) (next_arg)) { } } break; case LYSTRING_ARG: if ((q->str_value != 0) && (next_arg != 0)) StrAllocCopy(*(q->str_value), next_arg); break; case INT_ARG: if ((q->int_value != 0) && (next_arg != 0)) *(q->int_value) = (int) strtol(next_arg, &temp_ptr, 0); break; case TIME_ARG: if ((q->int_value != 0) && (next_arg != 0)) { float ival; if (1 == LYscanFloat(next_arg, &ival)) { *(q->int_value) = (int) SECS2Secs(ival); } } break; case STRING_ARG: if ((q->str_value != 0) && (next_arg != 0)) *(q->str_value) = next_arg; break; } Old_DTD = DTD_recovery; /* BOOL != int */ return TRUE; } if (pgm == 0) pgm = "LYNX"; fprintf(stderr, gettext("%s: Invalid Option: %s\n"), pgm, argv[0]); print_help_and_exit(-1); return FALSE; } #ifndef VMS static void FatalProblem(int sig) { /* * Ignore further interrupts. - mhc: 11/2/91 */ #ifndef NOSIGHUP (void) signal(SIGHUP, SIG_IGN); #endif /* NOSIGHUP */ (void) signal(SIGTERM, SIG_IGN); (void) signal(SIGINT, SIG_IGN); #ifndef __linux__ #ifdef SIGBUS (void) signal(SIGBUS, SIG_IGN); #endif /* ! SIGBUS */ #endif /* !__linux__ */ (void) signal(SIGSEGV, SIG_IGN); (void) signal(SIGILL, SIG_IGN); /* * Flush all messages. - FM */ fflush(stderr); fflush(stdout); /* * Deal with curses, if on, and clean up. - FM */ if (LYOutOfMemory && LYCursesON) { LYSleepAlert(); } cleanup_sig(0); #ifndef __linux__ #ifdef SIGBUS signal(SIGBUS, SIG_DFL); #endif /* SIGBUS */ #endif /* !__linux__ */ signal(SIGSEGV, SIG_DFL); signal(SIGILL, SIG_DFL); /* * Issue appropriate messages and abort or exit. - FM */ if (LYOutOfMemory == FALSE) { fprintf(stderr, "\r\n\ A Fatal error has occurred in %s Ver. %s\r\n", LYNX_NAME, LYNX_VERSION); fprintf(stderr, "\r\n\ Please notify your system administrator to confirm a bug, and\r\n\ if confirmed, to notify the lynx-dev list. Bug reports should\r\n\ have concise descriptions of the command and/or URL which causes\r\n\ the problem, the operating system name with version number, the\r\n\ TCPIP implementation, and any other relevant information.\r\n"); if (!(sig == 0 && LYNoCore)) { fprintf(stderr, "\r\n\ Do NOT mail the core file if one was generated.\r\n"); } if (sig != 0) { fprintf(stderr, "\r\n\ Lynx now exiting with signal: %d\r\n\r\n", sig); #ifdef WIN_EX /* 1998/08/09 (Sun) 09:58:25 */ { char *msg; switch (sig) { case SIGABRT: msg = "SIGABRT"; break; case SIGFPE: msg = "SIGFPE"; break; case SIGILL: msg = "SIGILL"; break; case SIGSEGV: msg = "SIGSEGV"; break; default: msg = "Not-def"; break; } fprintf(stderr, "signal code = %s\n", msg); } #endif } /* * Exit and possibly dump core. */ if (LYNoCore) { exit_immediately(EXIT_FAILURE); } abort(); } else { LYOutOfMemory = FALSE; printf("\r\n%s\r\n\r\n", MEMORY_EXHAUSTED_ABORT); fflush(stdout); /* * Exit without dumping core. */ exit_immediately(EXIT_FAILURE); } } #endif /* !VMS */ lynx2-8-8/src/LYMainLoop.c000644 023711 023712 00000654003 12245762550 015711 0ustar00dickeylynx000000 000000 /* * $LynxId: LYMainLoop.c,v 1.230 2013/11/28 11:20:34 tom Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SESSIONS #include #endif #ifdef KANJI_CODE_OVERRIDE #include #endif #define LinkIsTextarea(linkNumber) \ (links[linkNumber].type == WWW_FORM_LINK_TYPE && \ links[linkNumber].l_form->type == F_TEXTAREA_TYPE) #define LinkIsTextLike(linkNumber) \ (links[linkNumber].type == WWW_FORM_LINK_TYPE && \ F_TEXTLIKE(links[linkNumber].l_form->type)) #ifdef KANJI_CODE_OVERRIDE char *str_kcode(HTkcode code) { char *p; static char buff[8]; if (current_char_set == TRANSPARENT) { p = "THRU"; } else if (!LYRawMode) { p = "RAW"; } else { switch (code) { case NOKANJI: p = "AUTO"; break; case EUC: p = "EUC+"; break; case SJIS: p = "SJIS"; break; case JIS: p = " JIS"; break; default: p = " ???"; break; } } if (no_table_center) { buff[0] = '!'; strcpy(buff + 1, p); } else { strcpy(buff, p); } return buff; } #endif #ifdef WIN_EX static char *str_sjis(char *to, char *from) { if (!LYRawMode) { strcpy(to, from); #ifdef KANJI_CODE_OVERRIDE } else if (last_kcode == EUC) { EUC_TO_SJIS(from, to); } else if (last_kcode == SJIS) { strcpy(to, from); #endif } else { TO_SJIS((unsigned char *) from, (unsigned char *) to); } return to; } static void set_ws_title(char *str) { SetConsoleTitle(str); } #endif /* WIN_EX */ #if defined(USE_EXTERNALS) || defined(WIN_EX) #include #endif #ifdef __EMX__ #include #endif #ifdef DIRED_SUPPORT #include #include #endif /* DIRED_SUPPORT */ #include #include /* two constants: */ HTLinkType *HTInternalLink = 0; HTAtom *WWW_SOURCE = 0; #define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) \ ((track_internal_links && \ (!curdoc.internal_link || are_phys_different(p,n))) || \ are_different(p,n)) #define NO_INTERNAL_OR_DIFFERENT(c,n) \ (track_internal_links || are_different(c,n)) static void exit_immediately_with_error_message(int state, int first_file); static void status_link(char *curlink_name, int show_more, int show_indx); static void show_main_statusline(const LinkInfo curlink, int for_what); static void form_noviceline(int); static int are_different(DocInfo *doc1, DocInfo *doc2); static int are_phys_different(DocInfo *doc1, DocInfo *doc2); #define FASTTAB static int sametext(char *een, char *twee) { if (een && twee) return (strcmp(een, twee) == 0); return TRUE; } HTList *Goto_URLs = NULL; /* List of Goto URLs */ char *LYRequestTitle = NULL; /* newdoc.title in calls to getfile() */ char *LYRequestReferer = NULL; /* Referer, may be set in getfile() */ static bstring *prev_target = NULL; #ifdef DISP_PARTIAL BOOLEAN display_partial = FALSE; /* could be enabled in HText_new() */ int NumOfLines_partial = 0; /* number of lines displayed in partial mode */ #endif static int Newline = 0; static DocInfo newdoc; static DocInfo curdoc; static char *traversal_host = NULL; static char *traversal_link_to_add = NULL; static char *owner_address = NULL; /* Holds the responsible owner's address */ static char *ownerS_address = NULL; /* Holds owner's address during source fetch */ #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION static BOOL textinput_activated = FALSE; #else #define textinput_activated TRUE /* a current text input is always active */ #endif #ifdef INACTIVE_INPUT_STYLE_VH BOOL textinput_redrawn = FALSE; /*must be public since used in LYhighlight(..) */ #endif #ifdef LY_FIND_LEAKS /* * Function for freeing allocated mainloop() variables. - FM */ static void free_mainloop_variables(void) { LYFreeDocInfo(&newdoc); LYFreeDocInfo(&curdoc); #ifdef USE_COLOR_STYLE FREE(curdoc.style); FREE(newdoc.style); #endif FREE(traversal_host); FREE(traversal_link_to_add); FREE(owner_address); FREE(ownerS_address); #ifdef DIRED_SUPPORT clear_tags(); reset_dired_menu(); #endif /* DIRED_SUPPORT */ FREE(WWW_Download_File); /* LYGetFile.c/HTFWriter.c */ FREE(LYRequestReferer); return; } #endif /* LY_FIND_LEAKS */ #ifndef NO_LYNX_TRACE static void TracelogOpenFailed(void) { WWW_TraceFlag = FALSE; if (LYCursesON) { HTUserMsg(TRACELOG_OPEN_FAILED); } else { fprintf(stderr, "%s\n", TRACELOG_OPEN_FAILED); exit_immediately(EXIT_FAILURE); } } static BOOLEAN LYReopenTracelog(BOOLEAN *trace_flag_ptr) { CTRACE((tfp, "\nTurning off TRACE for fetch of log.\n")); LYCloseTracelog(); if ((LYTraceLogFP = LYAppendToTxtFile(LYTraceLogPath)) == NULL) { TracelogOpenFailed(); return FALSE; } if (TRACE) { WWW_TraceFlag = FALSE; *trace_flag_ptr = TRUE; } return TRUE; } static void turn_trace_back_on(BOOLEAN *trace_flag_ptr) { if (*trace_flag_ptr == TRUE) { WWW_TraceFlag = TRUE; *trace_flag_ptr = FALSE; fprintf(tfp, "Turning TRACE back on.\n\n"); } } #else #define LYReopenTracelog(flag) TRUE #define turn_trace_back_on(flag) /*nothing */ #endif /* NO_LYNX_TRACE */ FILE *TraceFP(void) { #ifndef NO_LYNX_TRACE if (LYTraceLogFP != 0) { return LYTraceLogFP; } #endif /* NO_LYNX_TRACE */ return stderr; } BOOLEAN LYOpenTraceLog(void) { #ifndef NO_LYNX_TRACE if (TRACE && LYUseTraceLog && LYTraceLogFP == NULL) { /* * If we can't open it for writing, give up. Otherwise, on VMS close * it, delete it and any versions from previous sessions so they don't * accumulate, and open it again. - FM */ if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) { TracelogOpenFailed(); return FALSE; } #ifdef VMS LYCloseTracelog(); HTSYS_remove(LYTraceLogPath); if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) { TracelogOpenFailed(); return FALSE; } #endif /* VMS */ fflush(stdout); fflush(stderr); fprintf(tfp, "\t\t%s (%s)\n\n", LYNX_TRACELOG_TITLE, LYNX_VERSION); /* * If TRACE is on, indicate whether the anonymous restrictions are set. * - FM, LP, kw * * This is only a summary for convenience - it doesn't take the case of * individual -restrictions= options into account. - kw */ if (LYValidate) { if (LYRestricted && had_restrictions_default) { CTRACE((tfp, "Validate and some anonymous restrictions are set.\n")); } else if (had_restrictions_default) { CTRACE((tfp, "Validate restrictions set, restriction \"default\" was given.\n")); } else if (LYRestricted) { CTRACE((tfp, "Validate restrictions set, additional anonymous restrictions ignored.\n")); } else { CTRACE((tfp, "Validate restrictions are set.\n")); } /* But none of the above can actually happen, since there should * never be a Trace Log with -validate. If it appears in a log * file something went wrong! */ } else if (LYRestricted) { if (had_restrictions_all) { CTRACE((tfp, "Anonymous restrictions set, restriction \"all\" was given.\n")); } else { CTRACE((tfp, "Anonymous restrictions are set.\n")); } } else if (had_restrictions_all && had_restrictions_default) { CTRACE((tfp, "Restrictions \"all\" and \"default\" were given.\n")); } else if (had_restrictions_default) { CTRACE((tfp, "Restriction \"default\" was given.\n")); } else if (had_restrictions_all) { CTRACE((tfp, "\"all\" restrictions are set.\n")); } } #endif /* NO_LYNX_TRACE */ return TRUE; } void LYCloseTracelog(void) { #ifndef NO_LYNX_TRACE if (LYTraceLogFP != 0) { fflush(stdout); fflush(stderr); fclose(LYTraceLogFP); LYTraceLogFP = 0; } #endif /* NO_LYNX_TRACE */ } void handle_LYK_TRACE_TOGGLE(void) { #ifndef NO_LYNX_TRACE WWW_TraceFlag = (BOOLEAN) !WWW_TraceFlag; if (LYOpenTraceLog()) HTUserMsg(WWW_TraceFlag ? TRACE_ON : TRACE_OFF); #else HTUserMsg(TRACE_DISABLED); #endif /* NO_LYNX_TRACE */ } void LYSetNewline(int value) { Newline = value; } #define LYSetNewline(value) Newline = value int LYGetNewline(void) { return Newline; } #define LYGetNewline() Newline void LYChgNewline(int adjust) { LYSetNewline(Newline + adjust); } #define LYChgNewline(adjust) Newline += (adjust) #ifdef USE_SOURCE_CACHE static BOOLEAN from_source_cache = FALSE; /* * Like HTreparse_document(), but also set the flag. */ static BOOLEAN reparse_document(void) { BOOLEAN result; from_source_cache = TRUE; /* set for LYMainLoop_pageDisplay() */ if ((result = HTreparse_document()) != FALSE) { from_source_cache = TRUE; /* set for mainloop refresh */ } else { from_source_cache = FALSE; } return result; } #endif /* USE_SOURCE_CACHE */ /* * Prefer reparsing if we can, but reload if we must - to force regeneration * of the display. */ static BOOLEAN reparse_or_reload(int *cmd) { #ifdef USE_SOURCE_CACHE if (reparse_document()) { return FALSE; } #endif *cmd = LYK_RELOAD; return TRUE; } /* * Functions for setting the current address */ static void set_address(DocInfo *doc, const char *address) { StrAllocCopy(doc->address, address); } static void copy_address(DocInfo *dst, DocInfo *src) { StrAllocCopy(dst->address, src->address); } static void free_address(DocInfo *doc) { FREE(doc->address); } static void move_address(DocInfo *dst, DocInfo *src) { copy_address(dst, src); free_address(src); } #ifdef DISP_PARTIAL /* * This is for traversal call from within partial mode in LYUtils.c * and HTFormat.c It simply calls HText_pageDisplay() but utilizes * LYMainLoop.c static variables to manage proper newline position * in case of #fragment */ BOOL LYMainLoop_pageDisplay(int line_num) { const char *pound; int prev_newline = LYGetNewline(); /* * Override Newline with a new value if user scrolled the document while * loading (in LYUtils.c). */ LYSetNewline(line_num); #ifdef USE_SOURCE_CACHE /* * reparse_document() acts on 'curdoc' which always on top of the * history stack: no need to resolve #fragment position since * we already know it (curdoc.line). * So bypass here. Sorry for possible confusion... */ if (!from_source_cache) #endif /* * If the requested URL has the #fragment, and we are not popped * from the history stack, and have not scrolled the document yet - * we should calculate correct newline position for the fragment. * (This is a bit suboptimal since HTFindPoundSelector() traverse * anchors list each time, so we have a quadratic complexity * and may load CPU in a worst case). */ if (display_partial && newdoc.line == 1 && line_num == 1 && prev_newline == 1 && (pound = findPoundSelector(newdoc.address)) && *pound && *(pound + 1)) { if (HTFindPoundSelector(pound + 1)) { /* HTFindPoundSelector will initialize www_search_result */ LYSetNewline(www_search_result); } else { LYSetNewline(prev_newline); /* restore ??? */ return NO; /* no repaint */ } } HText_pageDisplay(LYGetNewline(), prev_target->str); return YES; } #endif /* DISP_PARTIAL */ static BOOL set_curdoc_link(int nextlink) { BOOL result = FALSE; if (curdoc.link != nextlink && nextlink >= 0 && nextlink < nlinks) { if (curdoc.link >= 0 && curdoc.link < nlinks) { LYhighlight(FALSE, curdoc.link, prev_target->str); result = TRUE; } curdoc.link = nextlink; } return result; } /* * Setup newdoc to jump to the given line. * * FIXME: prefer to also jump to the link given in a URL fragment, but the * interface of getfile() does not provide that ability yet. */ static void goto_line(int nextline) { int n; int old_link = newdoc.link; newdoc.link = 0; for (n = 0; n < nlinks; ++n) { if (nextline == links[n].anchor_line_num + 1) { CTRACE((tfp, "top_of_screen %d\n", HText_getTopOfScreen() + 1)); CTRACE((tfp, "goto_line(%d) -> link %d -> %d\n", nextline, old_link, n)); newdoc.link = n; break; } } } #ifdef USE_MOUSE static void set_curdoc_link_by_mouse(int nextlink) { if (set_curdoc_link(nextlink)) { LYhighlight(TRUE, nextlink, prev_target->str); LYmsec_delay(20); } } #else #define set_curdoc_link_by_mouse(nextlink) set_curdoc_link(nextlink) #endif static int do_change_link(void) { #ifdef USE_MOUSE /* Is there a mouse-clicked link waiting? */ int mouse_tmp = get_mouse_link(); /* If yes, use it as the link */ if (mouse_tmp != -1) { if (mouse_tmp < 0 || mouse_tmp >= nlinks) { char *msgtmp = NULL; HTSprintf0(&msgtmp, gettext("Internal error: Invalid mouse link %d!"), mouse_tmp); HTAlert(msgtmp); FREE(msgtmp); return (-1); /* indicates unexpected error */ } set_curdoc_link_by_mouse(mouse_tmp); } #endif /* USE_MOUSE */ return (0); /* indicates OK */ } #ifdef DIRED_SUPPORT #define DIRED_UNCACHE_1 if (LYAutoUncacheDirLists < 1) /*nothing*/ ;\ else HTuncache_current_document() #define DIRED_UNCACHE_2 if (LYAutoUncacheDirLists < 2) /*nothing*/ ;\ else HTuncache_current_document() #endif /* DIRED_SUPPORT */ static void do_check_goto_URL(bstring **user_input, char **old_user_input, BOOLEAN *force_load) { static BOOLEAN always = TRUE; /* *INDENT-OFF* */ static struct { const char *name; BOOLEAN *flag; } table[] = { { STR_FILE_URL, &no_file_url }, { STR_FILE_URL, &no_goto_file }, { STR_LYNXEXEC, &no_goto_lynxexec }, { STR_LYNXPROG, &no_goto_lynxprog }, { STR_LYNXCGI, &no_goto_lynxcgi }, { STR_CSO_URL, &no_goto_cso }, { STR_FINGER_URL, &no_goto_finger }, { STR_FTP_URL, &no_goto_ftp }, { STR_GOPHER_URL, &no_goto_gopher }, { STR_HTTP_URL, &no_goto_http }, { STR_HTTPS_URL, &no_goto_https }, { STR_MAILTO_URL, &no_goto_mailto }, { STR_RLOGIN_URL, &no_goto_rlogin }, { STR_TELNET_URL, &no_goto_telnet }, { STR_TN3270_URL, &no_goto_tn3270 }, { STR_WAIS_URL, &no_goto_wais }, #ifndef DISABLE_BIBP { STR_BIBP_URL, &no_goto_bibp }, #endif #ifndef DISABLE_NEWS { STR_NEWS_URL, &no_goto_news }, { STR_NNTP_URL, &no_goto_nntp }, { STR_SNEWS_URL, &no_goto_snews }, #endif #ifdef EXEC_LINKS { STR_LYNXEXEC, &local_exec_on_local_files }, { STR_LYNXPROG, &local_exec_on_local_files }, #endif /* EXEC_LINKS */ { STR_LYNXCFG, &no_goto_configinfo }, { STR_LYNXCFLAGS, &no_goto_configinfo }, { STR_LYNXCOOKIE, &always }, #ifdef USE_CACHEJAR { STR_LYNXCACHE, &always }, #endif { STR_LYNXDIRED, &always }, { STR_LYNXDOWNLOAD, &always }, { STR_LYNXOPTIONS, &always }, { STR_LYNXPRINT, &always }, }; /* *INDENT-ON* */ unsigned n; BOOLEAN found = FALSE; /* allow going to anchors */ if ((*user_input)->str[0] == '#') { if ((*user_input)->str[1] && HTFindPoundSelector((*user_input)->str + 1)) { /* HTFindPoundSelector will initialize www_search_result, so we do nothing else. */ HTAddGotoURL((*user_input)->str); trimPoundSelector(curdoc.address); StrAllocCat(curdoc.address, (*user_input)->str); } } else { /* * If it's not a URL then make it one. */ StrAllocCopy(*old_user_input, (*user_input)->str); LYEnsureAbsoluteURL(old_user_input, "", TRUE); BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); for (n = 0; n < TABLESIZE(table); n++) { if (*(table[n].flag) && !StrNCmp((*user_input)->str, table[n].name, strlen(table[n].name))) { found = TRUE; HTUserMsg2(GOTO_XXXX_DISALLOWED, table[n].name); break; } } if (found) { ; } else if (LYValidate && !isHTTP_URL((*user_input)->str) && !isHTTPS_URL((*user_input)->str)) { HTUserMsg(GOTO_NON_HTTP_DISALLOWED); } else { set_address(&newdoc, (*user_input)->str); newdoc.isHEAD = FALSE; /* * Might be an anchor in the same doc from a POST form. If so, * dont't free the content. -- FM */ if (are_different(&curdoc, &newdoc)) { /* * Make a name for this new URL. */ StrAllocCopy(newdoc.title, gettext("A URL specified by the user")); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.safe = FALSE; newdoc.internal_link = FALSE; *force_load = TRUE; #ifdef DIRED_SUPPORT if (lynx_edit_mode) { DIRED_UNCACHE_2; } #endif /* DIRED_SUPPORT */ } LYUserSpecifiedURL = TRUE; HTAddGotoURL(newdoc.address); } } } /* returns FALSE if user cancelled input or URL was invalid, TRUE otherwise */ static BOOL do_check_recall(int ch, bstring **user_input, char **old_user_input, int URLTotal, int *URLNum, RecallType recall, BOOLEAN *FirstURLRecall) { char *cp; BOOL ret = FALSE; if (*old_user_input == 0) StrAllocCopy(*old_user_input, ""); for (;;) { #ifdef WIN_EX /* 1998/10/11 (Sun) 10:41:05 */ int len = strlen((*user_input)->str); if (len >= 3) { if (len < MAX_LINE - 1 && LYIsHtmlSep((*user_input)->str[len - 3]) && LYIsDosDrive((*user_input)->str + len - 2)) LYAddPathSep0((*user_input)->str); } else if (len == 2 && (*user_input)->str[1] == ':') { if (LYIsDosDrive((*user_input)->str)) { LYAddPathSep0((*user_input)->str); } else { HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE, (*user_input)->str); BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); ret = FALSE; break; } } #endif /* * Get rid of leading spaces (and any other spaces). */ LYTrimAllStartfile((*user_input)->str); if (isBEmpty(*user_input) && !(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) { BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); HTInfoMsg(CANCELLED); ret = FALSE; break; } if (recall && ch == UPARROW_KEY) { if (*FirstURLRecall) { /* * Use last URL in the list. - FM */ *FirstURLRecall = FALSE; *URLNum = 0; } else { /* * Go back to the previous URL in the list. - FM */ *URLNum += 1; } if (*URLNum >= URLTotal) /* * Roll around to the last URL in the list. - FM */ *URLNum = 0; if ((cp = (char *) HTList_objectAt(Goto_URLs, *URLNum)) != NULL) { BStrCopy0((*user_input), cp); if (goto_buffer && **old_user_input && !strcmp(*old_user_input, (*user_input)->str)) { _statusline(EDIT_CURRENT_GOTO); } else if ((goto_buffer && URLTotal == 2) || (!goto_buffer && URLTotal == 1)) { _statusline(EDIT_THE_PREV_GOTO); } else { _statusline(EDIT_A_PREV_GOTO); } if ((ch = LYgetBString(user_input, FALSE, 0, recall)) < 0) { /* * User cancelled the Goto via ^G. Restore * user_input and break. - FM */ BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); HTInfoMsg(CANCELLED); ret = FALSE; break; } continue; } } else if (recall && ch == DNARROW_KEY) { if (*FirstURLRecall) { /* * Use the first URL in the list. - FM */ *FirstURLRecall = FALSE; *URLNum = URLTotal - 1; } else { /* * Advance to the next URL in the list. - FM */ *URLNum -= 1; } if (*URLNum < 0) /* * Roll around to the first URL in the list. - FM */ *URLNum = URLTotal - 1; if ((cp = (char *) HTList_objectAt(Goto_URLs, *URLNum)) != NULL) { BStrCopy0((*user_input), cp); if (goto_buffer && **old_user_input && !strcmp(*old_user_input, (*user_input)->str)) { _statusline(EDIT_CURRENT_GOTO); } else if ((goto_buffer && URLTotal == 2) || (!goto_buffer && URLTotal == 1)) { _statusline(EDIT_THE_PREV_GOTO); } else { _statusline(EDIT_A_PREV_GOTO); } if ((ch = LYgetBString(user_input, FALSE, 0, recall)) < 0) { /* * User cancelled the Goto via ^G. Restore * user_input and break. - FM */ BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); HTInfoMsg(CANCELLED); ret = FALSE; break; } continue; } } else { ret = TRUE; break; } } return ret; } static void do_cleanup_after_delete(void) { HTuncache_current_document(); move_address(&newdoc, &curdoc); newdoc.line = curdoc.line; if (curdoc.link == nlinks - 1) { /* * We deleted the last link on the page. - FM */ newdoc.link = curdoc.link - 1; } else { newdoc.link = curdoc.link; } } static int find_link_near_col(int col, int delta) { int i; for (i = curdoc.link; delta > 0 ? (i < nlinks) : (i >= 0); i += delta) { if ((links[i].ly - links[curdoc.link].ly) * delta > 0) { int cy = links[i].ly, best = -1, dist = 1000000; while ((delta > 0 ? (i < nlinks) : (i >= 0)) && cy == links[i].ly) { int cx = links[i].lx; const char *text = LYGetHiliteStr(i, 0); if (text != NULL) cx += (int) strlen(text) / 2; cx -= col; if (cx < 0) cx = -cx; if (cx < dist) { dist = cx; best = i; } i += delta; } return (best); } } return (-1); } /* * This is a special feature to traverse every http link derived from startfile * and check for errors or create crawl output files. Only URL's that begin * with "traversal_host" are searched - this keeps the search from crossing to * other servers (a feature, not a bug!). */ static int DoTraversal(int c, BOOLEAN *crawl_ok) { BOOLEAN rlink_rejected = FALSE; BOOLEAN rlink_exists; BOOLEAN rlink_allowed; rlink_exists = (BOOL) (nlinks > 0 && links[curdoc.link].type != WWW_FORM_LINK_TYPE && links[curdoc.link].lname != NULL); if (rlink_exists) { rlink_rejected = lookup_reject(links[curdoc.link].lname); if (!rlink_rejected && traversal_host && links[curdoc.link].lname) { if (!isLYNXIMGMAP(links[curdoc.link].lname)) { rlink_allowed = (BOOL) !StrNCmp(traversal_host, links[curdoc.link].lname, strlen(traversal_host)); } else { rlink_allowed = (BOOL) !StrNCmp(traversal_host, links[curdoc.link].lname + LEN_LYNXIMGMAP, strlen(traversal_host)); } } else { rlink_allowed = FALSE; } } else { rlink_allowed = FALSE; } if (rlink_exists && rlink_allowed) { if (lookup_link(links[curdoc.link].lname)) { if (more_links || (curdoc.link > -1 && curdoc.link < nlinks - 1)) { c = DNARROW_KEY; } else { if (STREQ(curdoc.title, "Entry into main screen") || (nhist <= 0)) { if (!dump_output_immediately) { cleanup(); exit_immediately(EXIT_FAILURE); } c = -1; } else { c = LTARROW_KEY; } } } else { StrAllocCopy(traversal_link_to_add, links[curdoc.link].lname); if (!isLYNXIMGMAP(traversal_link_to_add)) *crawl_ok = TRUE; c = RTARROW_KEY; } } else { /* no good right link, so only down and left arrow ok */ if (rlink_exists /* && !rlink_rejected */ ) /* uncomment in previous line to avoid duplicates - kw */ add_to_reject_list(links[curdoc.link].lname); if (more_links || (curdoc.link > -1 && curdoc.link < nlinks - 1)) { c = DNARROW_KEY; } else { /* * curdoc.title doesn't always work, so bail out if the history * list is empty. */ if (STREQ(curdoc.title, "Entry into main screen") || (nhist <= 0)) { if (!dump_output_immediately) { cleanup(); exit_immediately(EXIT_FAILURE); } c = -1; } else { c = LTARROW_KEY; } } } CTRACE((tfp, "DoTraversal(%d:%d) -> %s\n", nlinks > 0 ? curdoc.link : 0, nlinks, LYKeycodeToString(c, FALSE))); return c; } static BOOLEAN check_history(void) { const char *base; if (!curdoc.post_data) /* * Normal case - List Page is not associated with post data. - kw */ return TRUE; if (nhist > 0 && !LYresubmit_posts && HDOC(nhist - 1).post_data && BINEQ(curdoc.post_data, HDOC(nhist - 1).post_data) && (base = HText_getContentBase()) != 0) { char *text = !isLYNXIMGMAP(HDOC(nhist - 1).address) ? HDOC(nhist - 1).address : HDOC(nhist - 1).address + LEN_LYNXIMGMAP; if (!StrNCmp(base, text, strlen(base))) { /* * Normal case - as best as we can check, the document at the top * of the history stack seems to be the document the List Page is * about (or a LYNXIMGMAP derived from it), and LYresubmit_posts is * not set, so don't prompt here. If we actually have to repeat a * POST because, against expectations, the underlying document * isn't cached any more, HTAccess will prompt for confirmation, * unless we had LYK_NOCACHE -kw */ return TRUE; } } return FALSE; } static int handle_LYK_ACTIVATE(int *c, int cmd GCC_UNUSED, BOOLEAN *try_internal GCC_UNUSED, BOOLEAN *refresh_screen, BOOLEAN *force_load, int real_cmd) { if (do_change_link() == -1) { LYforce_no_cache = FALSE; reloading = FALSE; return 1; /* mouse stuff was confused, ignore - kw */ } if (nlinks > 0) { if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) { #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION if (real_cmd == LYK_ACTIVATE && textfields_need_activation && F_TEXTLIKE(links[curdoc.link].l_form->type)) { textinput_activated = TRUE; show_main_statusline(links[curdoc.link], FOR_INPUT); textfields_need_activation = textfields_activation_option; return 0; } #endif /* * Don't try to submit forms with bad actions. - FM */ if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) { /* * Do nothing if it's disabled. - FM */ if (links[curdoc.link].l_form->disabled == YES) { HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } /* * Make sure we have an action. - FM */ if (isEmpty(links[curdoc.link].l_form->submit_action)) { HTUserMsg(NO_FORM_ACTION); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } /* * Check for no_mail if the form action is a mailto URL. - FM */ if (links[curdoc.link].l_form->submit_method == URL_MAIL_METHOD && no_mail) { HTAlert(FORM_MAILTO_DISALLOWED); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } /* * Make sure this isn't a spoof in an account with restrictions * on file URLs. - FM */ if (no_file_url && isFILE_URL(links[curdoc.link].l_form->submit_action)) { HTAlert(FILE_ACTIONS_DISALLOWED); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } /* * Make sure this isn't a spoof attempt via an internal URL. - * FM */ if (isLYNXCOOKIE(links[curdoc.link].l_form->submit_action) || isLYNXCACHE(links[curdoc.link].l_form->submit_action) || #ifdef DIRED_SUPPORT #ifdef OK_PERMIT (isLYNXDIRED(links[curdoc.link].l_form->submit_action) && (no_dired_support || strncasecomp((links[curdoc.link].l_form->submit_action + 10), "//PERMIT_LOCATION", 17) || !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS))) || #else isLYNXDIRED(links[curdoc.link].l_form->submit_action) || #endif /* OK_PERMIT */ #endif /* DIRED_SUPPORT */ isLYNXDOWNLOAD(links[curdoc.link].l_form->submit_action) || isLYNXHIST(links[curdoc.link].l_form->submit_action) || isLYNXEDITMAP(links[curdoc.link].l_form->submit_action) || isLYNXKEYMAP(links[curdoc.link].l_form->submit_action) || isLYNXIMGMAP(links[curdoc.link].l_form->submit_action) || isLYNXPRINT(links[curdoc.link].l_form->submit_action) || isLYNXEXEC(links[curdoc.link].l_form->submit_action) || isLYNXPROG(links[curdoc.link].l_form->submit_action)) { HTAlert(SPECIAL_ACTION_DISALLOWED); CTRACE((tfp, "LYMainLoop: Rejected '%s'\n", links[curdoc.link].l_form->submit_action)); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } #ifdef NOTDEFINED /* We're disabling form inputs instead of using this. - FM */ /* * Check for enctype and let user know we don't yet support * multipart/form-data - FM */ if (links[curdoc.link].l_form->submit_enctype) { if (!strcmp(links[curdoc.link].l_form->submit_enctype, "multipart/form-data")) { HTAlert(gettext("Enctype multipart/form-data not yet supported! Cannot submit.")); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } } #endif /* NOTDEFINED */ if (check_realm) { LYPermitURL = TRUE; } if (no_filereferer == TRUE && isFILE_URL(curdoc.address)) { LYNoRefererForThis = TRUE; } if (links[curdoc.link].l_form->submit_method != URL_MAIL_METHOD) { StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0)); } } /* * Normally we don't get here for text input fields, but it can * happen as a result of mouse positioning. In that case the * statusline will not have updated info, so update it now. - kw */ if (F_TEXTLIKE(links[curdoc.link].l_form->type)) { show_formlink_statusline(links[curdoc.link].l_form, (real_cmd == LYK_NOCACHE || real_cmd == LYK_DOWNLOAD || real_cmd == LYK_HEAD || (real_cmd == LYK_MOUSE_SUBMIT && !textinput_activated)) ? FOR_PANEL : FOR_INPUT); if (user_mode == NOVICE_MODE && textinput_activated && (real_cmd == LYK_ACTIVATE || real_cmd == LYK_MOUSE_SUBMIT)) { form_noviceline(FormIsReadonly(links[curdoc.link].l_form)); } } *c = change_form_link(curdoc.link, &newdoc, refresh_screen, FALSE, (real_cmd == LYK_MOUSE_SUBMIT || real_cmd == LYK_NOCACHE || real_cmd == LYK_DOWNLOAD || real_cmd == LYK_HEAD)); if (*c != LKC_DONE || *refresh_screen) { /* * Cannot have been a submit field for which newdoc was filled * in. - kw */ if ((links[curdoc.link].l_form->type == F_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) && links[curdoc.link].l_form->submit_method != URL_MAIL_METHOD) { /* * Try to undo change of newdoc.title done above. */ if (HText_getTitle()) { StrAllocCopy(newdoc.title, HText_getTitle()); } else if (curdoc.title) { StrAllocCopy(newdoc.title, curdoc.title); } } } else { if (HTOutputFormat == HTAtom_for("www/download") && newdoc.post_data != NULL && newdoc.safe == FALSE) { if ((HText_POSTReplyLoaded(&newdoc) == TRUE) && HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) { HTInfoMsg(CANCELLED); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; copy_address(&newdoc, &curdoc); StrAllocCopy(newdoc.title, curdoc.title); BStrCopy(newdoc.post_data, curdoc.post_data); StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type); StrAllocCopy(newdoc.bookmark, curdoc.bookmark); newdoc.isHEAD = curdoc.isHEAD; newdoc.safe = curdoc.safe; newdoc.internal_link = curdoc.internal_link; return 0; } } /* * Moved here from earlier to only apply when it should. * Anyway, why should realm checking be overridden for form * submissions, this seems to be an unnecessary loophole?? But * that's the way it was, maybe there is some reason. However, * at least make sure this doesn't weaken restrictions implied * by -validate! * - kw 1999-05-25 */ if (check_realm && !LYValidate) { LYPermitURL = TRUE; } } if (*c == LKC_DONE) { *c = DO_NOTHING; } else if (*c == 23) { *c = DO_NOTHING; *refresh_screen = TRUE; } else { /* Avoid getting stuck with repeatedly calling * handle_LYK_ACTIVATE(), instead of calling change_form_link() * directly from mainloop(), for text input fields. - kw */ switch (LKC_TO_C(*c)) { case '\n': case '\r': default: if ((real_cmd == LYK_ACTIVATE || real_cmd == LYK_MOUSE_SUBMIT) && F_TEXTLIKE(links[curdoc.link].l_form->type) && textinput_activated) { return 3; } break; } } return 2; } else { /* * Not a forms link. * * Make sure this isn't a spoof in an account with restrictions on * file URLs. - FM */ if (no_file_url && isFILE_URL(links[curdoc.link].lname)) { if (!isFILE_URL(curdoc.address) && !((isLYNXEDITMAP(curdoc.address) || isLYNXKEYMAP(curdoc.address) || isLYNXCOOKIE(curdoc.address) || isLYNXCACHE(curdoc.address)) && !StrNCmp(links[curdoc.link].lname, helpfilepath, strlen(helpfilepath)))) { HTAlert(FILE_SERVED_LINKS_DISALLOWED); reloading = FALSE; return 0; } else if (curdoc.bookmark != NULL) { HTAlert(FILE_BOOKMARKS_DISALLOWED); reloading = FALSE; return 0; } } /* * Make sure this isn't a spoof attempt via an internal URL in a * non-internal document. - FM */ if ((isLYNXCOOKIE(links[curdoc.link].lname) && (strcmp(NonNull(curdoc.title), COOKIE_JAR_TITLE) || !isLYNXCOOKIE(curdoc.address))) || #ifdef USE_CACHEJAR (isLYNXCACHE(links[curdoc.link].lname) && (strcmp(NonNull(curdoc.title), CACHE_JAR_TITLE) || !isLYNXCACHE(curdoc.address))) || #endif #ifdef DIRED_SUPPORT (isLYNXDIRED(links[curdoc.link].lname) && !LYIsUIPage(curdoc.address, UIP_DIRED_MENU) && !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) && #ifdef OK_INSTALL !LYIsUIPage(curdoc.address, UIP_INSTALL) && #endif /* OK_INSTALL */ !LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) || #endif /* DIRED_SUPPORT */ (isLYNXDOWNLOAD(links[curdoc.link].lname) && !LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS)) || (isLYNXHIST(links[curdoc.link].lname) && !LYIsUIPage(curdoc.address, UIP_HISTORY) && !LYIsUIPage(curdoc.address, UIP_LIST_PAGE) && !LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) || (isLYNXPRINT(links[curdoc.link].lname) && !LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS))) { HTAlert(SPECIAL_VIA_EXTERNAL_DISALLOWED); HTOutputFormat = WWW_PRESENT; LYforce_no_cache = FALSE; reloading = FALSE; return 0; } #ifdef USE_EXTERNALS if (run_external(links[curdoc.link].lname, TRUE)) { *refresh_screen = TRUE; return 0; } #endif /* USE_EXTERNALS */ /* * Follow a normal link or anchor. */ set_address(&newdoc, links[curdoc.link].lname); StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0)); /* * For internal links, retain POST content if present. If we are * on the List Page, prevent pushing it on the history stack. * Otherwise set try_internal to signal that the top of the loop * should attempt to reposition directly, without calling getfile. * - kw */ if (track_internal_links) { /* * Might be an internal link anchor in the same doc. If so, take * the try_internal shortcut if we didn't fall through from * LYK_NOCACHE. - kw */ newdoc.internal_link = (links[curdoc.link].type == WWW_INTERN_LINK_TYPE); if (newdoc.internal_link) { /* * Special case of List Page document with an internal link * indication, which may really stand for an internal link * within the document the List Page is about. - kw */ if (LYIsListpageTitle(NonNull(curdoc.title)) && (LYIsUIPage(curdoc.address, UIP_LIST_PAGE) || LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) { if (check_history()) { LYinternal_flag = TRUE; } else { HTLastConfirmCancelled(); /* reset flag */ if (!confirm_post_resub(newdoc.address, newdoc.title, ((LYresubmit_posts && HText_POSTReplyLoaded(&newdoc)) ? 1 : 2), 2)) { if (HTLastConfirmCancelled() || (LYresubmit_posts && cmd != LYK_NOCACHE && !HText_POSTReplyLoaded(&newdoc))) { /* cancel the whole thing */ LYforce_no_cache = FALSE; reloading = FALSE; copy_address(&newdoc, &curdoc); StrAllocCopy(newdoc.title, curdoc.title); newdoc.internal_link = curdoc.internal_link; HTInfoMsg(CANCELLED); return 1; } else if (LYresubmit_posts && cmd != LYK_NOCACHE) { /* If LYresubmit_posts is set, and the answer was No, and the key wasn't NOCACHE, and we have a cached copy, then use it. - kw */ LYforce_no_cache = FALSE; } else { /* if No, but not ^C or ^G, drop * the post data. Maybe the link * wasn't meant to be internal after * all, here we can recover from that * assumption. - kw */ LYFreePostData(&newdoc); newdoc.internal_link = FALSE; HTAlert(DISCARDING_POST_DATA); } } } /* * Don't push the List Page if we follow an internal link * given by it. - kw */ free_address(&curdoc); } else if (cmd != LYK_NOCACHE) { *try_internal = TRUE; } if (!(LYresubmit_posts && newdoc.post_data)) LYinternal_flag = TRUE; /* We still set force_load so that history pushing * etc. will be done. - kw */ *force_load = TRUE; return 1; } else { /* * Free POST content if not an internal link. - kw */ LYFreePostData(&newdoc); } } /* * Might be an anchor in the same doc from a POST form. If so, * don't free the content. -- FM */ if (are_different(&curdoc, &newdoc)) { LYFreePostData(&newdoc); FREE(newdoc.bookmark); if (isLYNXMESSAGES(newdoc.address)) LYforce_no_cache = TRUE; } if (!no_jump && lynxjumpfile && curdoc.address && !strcmp(lynxjumpfile, curdoc.address)) { LYJumpFileURL = TRUE; LYUserSpecifiedURL = TRUE; } else if ((curdoc.title && (LYIsUIPage(curdoc.address, UIP_HISTORY) || !strcmp(curdoc.title, HISTORY_PAGE_TITLE))) || curdoc.bookmark != NULL || (lynxjumpfile && curdoc.address && !strcmp(lynxjumpfile, curdoc.address))) { LYUserSpecifiedURL = TRUE; } else if (no_filereferer == TRUE && curdoc.address != NULL && isFILE_URL(curdoc.address)) { LYNoRefererForThis = TRUE; } newdoc.link = 0; *force_load = TRUE; /* force MainLoop to reload */ #ifdef USE_PRETTYSRC psrc_view = FALSE; /* we get here if link is not internal */ #endif #if defined(DIRED_SUPPORT) && !defined(__DJGPP__) if (lynx_edit_mode) { DIRED_UNCACHE_2; /* * Unescaping any slash chars in the URL, but avoid double * unescaping and too-early unescaping of other chars. - KW */ HTUnEscapeSome(newdoc.address, "/"); /* avoid stripping final slash for root dir - kw */ if (strcasecomp(newdoc.address, "file://localhost/")) strip_trailing_slash(newdoc.address); } #endif /* DIRED_SUPPORT && !__DJGPP__ */ if (isLYNXCOOKIE(curdoc.address) || isLYNXCACHE(curdoc.address)) { HTuncache_current_document(); } } } return 0; } /* * If the given form link does not point to the requested type, search for * the first link belonging to the form which does. If there are none, * return null. */ #define SameFormAction(form,submit) \ ((submit) \ ? (F_SUBMITLIKE((form)->type)) \ : ((form)->type == F_RESET_TYPE)) static FormInfo *FindFormAction(FormInfo * given, BOOLEAN submit) { FormInfo *result = NULL; FormInfo *fi; int i; if (given == NULL) { HTAlert(LINK_NOT_IN_FORM); } else if (SameFormAction(given, submit)) { result = given; } else { for (i = 0; i < nlinks; i++) { if ((fi = links[i].l_form) != 0 && fi->number == given->number && (SameFormAction(fi, submit))) { result = fi; break; } } } return result; } static FormInfo *MakeFormAction(FormInfo * given, BOOLEAN submit) { FormInfo *result = 0; if (given != 0) { result = typecalloc(FormInfo); if (result == NULL) outofmem(__FILE__, "MakeFormAction"); *result = *given; if (submit) { if (result->submit_action == 0) { PerFormInfo *pfi = HText_PerFormInfo(result->number); *result = pfi->data; } result->type = F_SUBMIT_TYPE; } else { result->type = F_RESET_TYPE; } result->number = given->number; } return result; } static void handle_LYK_SUBMIT(int cur, DocInfo *doc, BOOLEAN *refresh_screen) { FormInfo *form = FindFormAction(links[cur].l_form, TRUE); FormInfo *make = NULL; char *save_submit_action = NULL; if (form == 0) { make = MakeFormAction(links[cur].l_form, TRUE); form = make; } StrAllocCopy(save_submit_action, form->submit_action); form->submit_action = HTPrompt(EDIT_SUBMIT_URL, form->submit_action); if (isEmpty(form->submit_action) || (!isLYNXCGI(form->submit_action) && StrNCmp(form->submit_action, "http", 4))) { HTUserMsg(FORM_ACTION_NOT_HTTP_URL); } else { HTInfoMsg(SUBMITTING_FORM); HText_SubmitForm(form, doc, form->name, form->value); *refresh_screen = TRUE; } StrAllocCopy(form->submit_action, save_submit_action); FREE(make); } static void handle_LYK_RESET(int cur, BOOLEAN *refresh_screen) { FormInfo *form = FindFormAction(links[cur].l_form, FALSE); FormInfo *make = NULL; if (form == 0) { make = MakeFormAction(links[cur].l_form, FALSE); form = make; } if (form != 0) { HTInfoMsg(RESETTING_FORM); HText_ResetForm(form); *refresh_screen = TRUE; FREE(make); } } #ifdef USE_ADDRLIST_PAGE static BOOLEAN handle_LYK_ADDRLIST(int *cmd) { /* * Don't do if already viewing list addresses page. */ if (LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) { /* * Already viewing list page, so get out. */ *cmd = LYK_PREV_DOC; return TRUE; } /* * Print address list page to file. */ if (showlist(&newdoc, FALSE) < 0) return FALSE; StrAllocCopy(newdoc.title, ADDRLIST_PAGE_TITLE); /* * showlist will set newdoc's other fields. It may leave post_data intact * so the list can be used to follow internal links in the current document * even if it is a POST response. - kw */ if (LYValidate || check_realm) { LYPermitURL = TRUE; StrAllocCopy(lynxlistfile, newdoc.address); } return FALSE; } #endif /* USE_ADDRLIST_PAGE */ static void handle_LYK_ADD_BOOKMARK(BOOLEAN *refresh_screen, int *old_c, int real_c) { int c; if (LYValidate) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(BOOKMARKS_DISABLED); } return; } if (!LYIsUIPage(curdoc.address, UIP_HISTORY) && !LYIsUIPage(curdoc.address, UIP_SHOWINFO) && !LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS) && #ifdef DIRED_SUPPORT !LYIsUIPage(curdoc.address, UIP_DIRED_MENU) && !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) && !LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS) && #endif /* DIRED_SUPPORT */ !LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS) && !isLYNXCOOKIE(curdoc.address) && !isLYNXCACHE(curdoc.address) && !LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU) && ((nlinks <= 0) || (links[curdoc.link].lname != NULL && !isLYNXHIST(links[curdoc.link].lname) && !isLYNXPRINT(links[curdoc.link].lname) && !isLYNXDIRED(links[curdoc.link].lname) && !isLYNXDOWNLOAD(links[curdoc.link].lname) && !isLYNXCOOKIE(links[curdoc.link].lname) && !isLYNXCACHE(links[curdoc.link].lname) && !isLYNXPRINT(links[curdoc.link].lname)))) { if (nlinks > 0) { if (curdoc.post_data == NULL && curdoc.bookmark == NULL && !LYIsUIPage(curdoc.address, UIP_LIST_PAGE) && !LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE) && !LYIsUIPage(curdoc.address, UIP_VLINKS)) { /* * The document doesn't have POST content, and is not a * bookmark file, nor is the list or visited links page, so we * can save either that or the link. - FM */ _statusline(BOOK_D_L_OR_CANCEL); if ((c = LYgetch_single()) == 'D') { save_bookmark_link(curdoc.address, curdoc.title); *refresh_screen = TRUE; /* MultiBookmark support */ goto check_add_bookmark_to_self; } } else { if (LYMultiBookmarks == MBM_OFF && curdoc.bookmark != NULL && strstr(curdoc.address, (*bookmark_page == '.' ? (bookmark_page + 1) : bookmark_page)) != NULL) { /* * If multiple bookmarks are disabled, offer the L)ink or * C)ancel, but with wording which indicates that the link * already exists in this bookmark file. - FM */ _statusline(MULTIBOOKMARKS_SELF); } else if (curdoc.post_data != NULL && links[curdoc.link].type == WWW_INTERN_LINK_TYPE) { /* * Internal link, and document has POST content. */ HTUserMsg(NOBOOK_POST_FORM); return; } else { /* * Only offer the link in a document with POST content, or * if the current document is a bookmark file and multiple * bookmarks are enabled. - FM */ _statusline(BOOK_L_OR_CANCEL); } c = LYgetch_single(); } if (c == 'L') { if (curdoc.post_data != NULL && links[curdoc.link].type == WWW_INTERN_LINK_TYPE) { /* * Internal link, and document has POST content. */ HTUserMsg(NOBOOK_POST_FORM); return; } /* * User does want to save the link. - FM */ if (links[curdoc.link].type != WWW_FORM_LINK_TYPE) { save_bookmark_link(links[curdoc.link].lname, LYGetHiliteStr(curdoc.link, 0)); *refresh_screen = TRUE; /* MultiBookmark support */ } else { HTUserMsg(NOBOOK_FORM_FIELD); return; } } else { return; } } else if (curdoc.post_data != NULL) { /* * No links, and document has POST content. - FM */ HTUserMsg(NOBOOK_POST_FORM); return; } else if (curdoc.bookmark != NULL) { /* * It's a bookmark file from which all of the links were deleted. * - FM */ HTUserMsg(BOOKMARKS_NOLINKS); return; } else { _statusline(BOOK_D_OR_CANCEL); if (LYgetch_single() == 'D') { save_bookmark_link(curdoc.address, curdoc.title); *refresh_screen = TRUE; /* MultiBookmark support */ } else { return; } } check_add_bookmark_to_self: if (curdoc.bookmark && BookmarkPage && !strcmp(curdoc.bookmark, BookmarkPage)) { HTuncache_current_document(); move_address(&newdoc, &curdoc); StrAllocCopy(newdoc.bookmark, curdoc.bookmark); newdoc.line = curdoc.line; newdoc.link = curdoc.link; newdoc.internal_link = FALSE; } } else { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NOBOOK_HSML); } } } static void handle_LYK_CLEAR_AUTH(int *old_c, int real_c) { if (*old_c != real_c) { *old_c = real_c; if (HTConfirm(CLEAR_ALL_AUTH_INFO)) { FREE(authentication_info[0]); FREE(authentication_info[1]); FREE(proxyauth_info[0]); FREE(proxyauth_info[1]); HTClearHTTPAuthInfo(); #ifndef DISABLE_NEWS HTClearNNTPAuthInfo(); #endif #ifndef DISABLE_FTP HTClearFTPPassword(); #endif HTUserMsg(AUTH_INFO_CLEARED); } else { HTUserMsg(CANCELLED); } } } static int handle_LYK_COMMAND(bstring **user_input) { LYKeymapCode ch; Kcmd *mp; char *src, *tmp; BStrCopy0((*user_input), ""); _statusline(": "); if (LYgetBString(user_input, FALSE, 0, RECALL_CMD) >= 0) { src = LYSkipBlanks((*user_input)->str); tmp = LYSkipNonBlanks(src); *tmp = 0; ch = ((mp = LYStringToKcmd(src)) != 0) ? mp->code : LYK_UNKNOWN; CTRACE((tfp, "LYK_COMMAND(%s.%s) = %d\n", src, tmp, (int) ch)); if (ch == 0) { return *src ? -1 : 0; } /* FIXME: reuse the rest of the buffer for parameters */ return ch; } return 0; } static void handle_LYK_COMMENT(BOOLEAN *refresh_screen, char **owner_address_p, int *old_c, int real_c) { int c; if (!*owner_address_p && strncasecomp(curdoc.address, "http", 4)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_OWNER); } } else if (no_mail) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(MAIL_DISALLOWED); } } else { if (HTConfirmDefault(CONFIRM_COMMENT, NO)) { if (!*owner_address_p) { /* * No owner defined, so make a guess and and offer it to the * user. - FM */ char *address = NULL; char *temp = HTParse(curdoc.address, "", PARSE_PATH); char *cp; if (temp != NULL) { HTUnEscape(temp); if (LYIsTilde(*temp) && strlen(temp) > 1) { /* * It's a ~user URL so guess user@host. - FM */ if ((cp = StrChr((temp + 1), '/')) != NULL) *cp = '\0'; StrAllocCopy(address, STR_MAILTO_URL); StrAllocCat(address, (temp + 1)); StrAllocCat(address, "@"); } FREE(temp); } if (address == NULL) /* * Wasn't a ~user URL so guess WebMaster@host. - FM */ StrAllocCopy(address, "mailto:WebMaster@"); temp = HTParse(curdoc.address, "", PARSE_HOST); StrAllocCat(address, temp); HTSprintf0(&temp, NO_OWNER_USE, address); c = HTConfirmDefault(temp, NO); FREE(temp); if (c == YES) { StrAllocCopy(*owner_address_p, address); FREE(address); } else { FREE(address); return; } } if (is_url(*owner_address_p) != MAILTO_URL_TYPE) { /* * The address is a URL. Just follow the link. */ set_address(&newdoc, *owner_address_p); newdoc.internal_link = FALSE; } else { /* * The owner_address is a mailto: URL. */ const char *kp = HText_getRevTitle(); const char *id = HText_getMessageID(); char *tmptitle = NULL; if (!kp && HTMainAnchor) { kp = HTAnchor_subject(HTMainAnchor); if (non_empty(kp)) { if (strncasecomp(kp, "Re: ", 4)) { StrAllocCopy(tmptitle, "Re: "); StrAllocCat(tmptitle, kp); kp = tmptitle; } } } if (StrChr(*owner_address_p, ':') != NULL) /* * Send a reply. The address is after the colon. */ reply_by_mail(StrChr(*owner_address_p, ':') + 1, curdoc.address, NonNull(kp), id); else reply_by_mail(*owner_address_p, curdoc.address, NonNull(kp), id); FREE(tmptitle); *refresh_screen = TRUE; /* to force a showpage */ } } } } #ifdef USE_CACHEJAR static BOOLEAN handle_LYK_CACHE_JAR(int *cmd) { /* * Don't do this if already viewing cache jar. */ if (!isLYNXCACHE(curdoc.address)) { set_address(&newdoc, STR_LYNXCACHE "/"); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; LYforce_no_cache = TRUE; if (LYValidate || check_realm) { LYPermitURL = TRUE; } } else { /* * If already in the cache jar, get out. */ *cmd = LYK_PREV_DOC; return TRUE; } return FALSE; } #endif /* USE_CACHEJAR */ static BOOLEAN handle_LYK_COOKIE_JAR(int *cmd) { /* * Don't do if already viewing the cookie jar. */ if (!isLYNXCOOKIE(curdoc.address)) { set_address(&newdoc, "LYNXCOOKIE:/"); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; LYforce_no_cache = TRUE; if (LYValidate || check_realm) { LYPermitURL = TRUE; } } else { /* * If already in the cookie jar, get out. */ *cmd = LYK_PREV_DOC; return TRUE; } return FALSE; } #if defined(DIRED_SUPPORT) static void handle_LYK_CREATE(void) { if (lynx_edit_mode && !no_dired_support) { if (local_create(&curdoc) > 0) { DIRED_UNCACHE_1; move_address(&newdoc, &curdoc); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.line = curdoc.line; newdoc.link = curdoc.link > -1 ? curdoc.link : 0; LYclear(); } } } #endif /* DIRED_SUPPORT */ static void handle_LYK_DEL_BOOKMARK(BOOLEAN *refresh_screen, int *old_c, int real_c) { if (curdoc.bookmark != NULL) { if (HTConfirmDefault(CONFIRM_BOOKMARK_DELETE, NO) != YES) return; remove_bookmark_link(links[curdoc.link].anchor_number - 1, curdoc.bookmark); } else { /* behave like REFRESH for backward compatibility */ *refresh_screen = TRUE; if (*old_c != real_c) { *old_c = real_c; lynx_force_repaint(); } return; } do_cleanup_after_delete(); } #if defined(DIRED_SUPPORT) || defined(VMS) static void handle_LYK_DIRED_MENU(BOOLEAN *refresh_screen, int *old_c GCC_UNUSED, int real_c GCC_UNUSED) { #ifdef VMS char *cp, *temp = 0; const char *test = HTGetProgramPath(ppCSWING); /* * Check if the CSwing Directory/File Manager is available. Will be * disabled if CSWING path is NULL, zero-length, or "none" (case * insensitive), if no_file_url was set via the file_url restriction, if * no_goto_file was set for the anonymous account, or if HTDirAccess was * set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the -nobrowse or -selective * switches. - FM */ if (isEmpty(test) || !strcasecomp(test, "none") || no_file_url || no_goto_file || HTDirAccess == HT_DIR_FORBID || HTDirAccess == HT_DIR_SELECTIVE) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(DFM_NOT_AVAILABLE); } return; } /* * If we are viewing a local directory listing or a local file which is not * temporary, invoke CSwing with the URL's directory converted to VMS path * specs and passed as the argument, so we start up CSwing positioned on * that node of the directory tree. Otherwise, pass the current default * directory as the argument. - FM */ if (LYisLocalFile(curdoc.address) && strncasecomp(curdoc.address, lynx_temp_space, strlen(lynx_temp_space))) { /* * We are viewing a local directory or a local file which is not * temporary. - FM */ struct stat stat_info; cp = HTParse(curdoc.address, "", PARSE_PATH | PARSE_PUNCTUATION); HTUnEscape(cp); if (HTStat(cp, &stat_info) == -1) { CTRACE((tfp, "mainloop: Can't stat %s\n", cp)); FREE(cp); HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING)); *refresh_screen = TRUE; /* redisplay */ } else { char *VMSdir = NULL; if (S_ISDIR(stat_info.st_mode)) { /* * We're viewing a local directory. Make that the CSwing * argument. - FM */ LYAddPathSep(&cp); StrAllocCopy(VMSdir, HTVMS_name("", cp)); FREE(cp); } else { /* * We're viewing a local file. Make its directory the CSwing * argument. - FM */ StrAllocCopy(VMSdir, HTVMS_name("", cp)); FREE(cp); if ((cp = strrchr(VMSdir, ']')) != NULL) { *(cp + 1) = '\0'; } else if ((cp = strrchr(VMSdir, ':')) != NULL) { *(cp + 1) = '\0'; } } HTSprintf0(&temp, "%s %s", HTGetProgramPath(ppCSWING), VMSdir); FREE(VMSdir); /* * Uncache the current document in case we change, move, or delete * it during the CSwing session. - FM */ /* could use DIRED_UNCACHE_1 but it's currently only defined for dired - kw */ HTuncache_current_document(); move_address(&newdoc, &curdoc); StrAllocCopy(newdoc.title, NonNull(curdoc.title)); StrAllocCopy(newdoc.bookmark, curdoc.bookmark); newdoc.line = curdoc.line; newdoc.link = curdoc.link; } } else { /* * We're not viewing a local directory or file. Pass CSwing the * current default directory as an argument and don't uncache the * current document. - FM */ HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING)); *refresh_screen = TRUE; /* redisplay */ } stop_curses(); LYSystem(temp); start_curses(); FREE(temp); #else /* * Don't do if not allowed or already viewing the menu. */ if (lynx_edit_mode && !no_dired_support && !LYIsUIPage(curdoc.address, UIP_DIRED_MENU) && strcmp(NonNull(curdoc.title), DIRED_MENU_TITLE)) { dired_options(&curdoc, &newdoc.address); *refresh_screen = TRUE; /* redisplay */ } #endif /* VMS */ } #endif /* defined(DIRED_SUPPORT) || defined(VMS) */ static int handle_LYK_DOWNLOAD(int *cmd, int *old_c, int real_c) { /* * Don't do if both download and disk_save are restricted. */ if (LYValidate || (no_download && !override_no_download && no_disk_save)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(DOWNLOAD_DISABLED); } return 0; } /* * Don't do if already viewing download options page. */ if (LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS)) return 0; if (do_change_link() == -1) return 1; /* mouse stuff was confused, ignore - kw */ if (nlinks > 0) { if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) { if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) { if (links[curdoc.link].l_form->submit_method == URL_MAIL_METHOD) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_MAILTO_ACTION); } return 0; } if (isLYNXOPTIONS(links[curdoc.link].l_form->submit_action)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_SPECIAL); } return 0; } HTOutputFormat = HTAtom_for("www/download"); LYforce_no_cache = TRUE; *cmd = LYK_ACTIVATE; return 2; } if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_INPUT); } } else if (isLYNXCOOKIE(curdoc.address)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_COOKIES); } } else if (LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_PRINT_OP); } #ifdef DIRED_SUPPORT } else if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_UPLOAD_OP); } } else if (LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_PERMIT_OP); } } else if (lynx_edit_mode && !no_dired_support && !strstr(links[curdoc.link].lname, "/SugFile=")) { /* * Don't bother making a /tmp copy of the local file. */ static DocInfo temp; copy_address(&temp, &newdoc); set_address(&newdoc, links[curdoc.link].lname); if (LYdownload_options(&newdoc.address, links[curdoc.link].lname) < 0) copy_address(&newdoc, &temp); else newdoc.internal_link = FALSE; LYFreeDocInfo(&temp); #endif /* DIRED_SUPPORT */ } else if (LYIsUIPage(curdoc.address, UIP_HISTORY) && isLYNXHIST(links[curdoc.link].lname)) { int number = atoi(links[curdoc.link].lname + LEN_LYNXHIST); if (number >= nhist || number < 0) { HTUserMsg(NO_DOWNLOAD_SPECIAL); return 0; } if ((HDOC(number).post_data != NULL && HDOC(number).safe != TRUE) && HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) { HTInfoMsg(CANCELLED); return 0; } /* * OK, we download from history page, restore URL from stack. */ copy_address(&newdoc, &HDOC(number)); StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0)); StrAllocCopy(newdoc.bookmark, HDOC(number).bookmark); LYFreePostData(&newdoc); if (HDOC(number).post_data) BStrCopy(newdoc.post_data, HDOC(number).post_data); if (HDOC(number).post_content_type) StrAllocCopy(newdoc.post_content_type, HDOC(number).post_content_type); newdoc.isHEAD = HDOC(number).isHEAD; newdoc.safe = HDOC(number).safe; newdoc.internal_link = FALSE; newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0; HTOutputFormat = HTAtom_for("www/download"); LYUserSpecifiedURL = TRUE; /* * Force the document to be reloaded. */ LYforce_no_cache = TRUE; } else if (!StrNCmp(links[curdoc.link].lname, "data:", 5)) { if (*old_c != real_c) { *old_c = real_c; HTAlert(UNSUPPORTED_DATA_URL); } } else if (isLYNXCOOKIE(links[curdoc.link].lname) || isLYNXCACHE(links[curdoc.link].lname) || isLYNXDIRED(links[curdoc.link].lname) || isLYNXDOWNLOAD(links[curdoc.link].lname) || isLYNXPRINT(links[curdoc.link].lname) || isLYNXOPTIONS(links[curdoc.link].lname) || isLYNXHIST(links[curdoc.link].lname) || /* handled above if valid - kw */ /* @@@ should next two be downloadable? - kw */ isLYNXHIST(links[curdoc.link].lname) || isLYNXCFLAGS(links[curdoc.link].lname) || isLYNXEXEC(links[curdoc.link].lname) || isLYNXPROG(links[curdoc.link].lname)) { HTUserMsg(NO_DOWNLOAD_SPECIAL); } else if (isMAILTO_URL(links[curdoc.link].lname)) { HTUserMsg(NO_DOWNLOAD_MAILTO_LINK); /* * From here on we could have a remote host, so check if that's * allowed. * * We copy all these checks from getfile() to LYK_DOWNLOAD here * because LYNXDOWNLOAD:// will NOT be pushing the previous * document into the history stack so preserve getfile() from * returning a wrong status (NULLFILE). */ } else if (local_host_only && !(LYisLocalHost(links[curdoc.link].lname) || LYisLocalAlias(links[curdoc.link].lname))) { HTUserMsg(ACCESS_ONLY_LOCALHOST); } else { /* Not a forms, options or history link */ /* * Follow a normal link or anchor. Note that if it's an anchor * within the same document, entire document will be downloaded. */ set_address(&newdoc, links[curdoc.link].lname); StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0)); /* * Might be an internal link in the same doc from a POST form. If * so, don't free the content. - kw */ if (track_internal_links) { if (links[curdoc.link].type != WWW_INTERN_LINK_TYPE) { LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; } } else { /* * Might be an anchor in the same doc from a POST form. If so, * don't free the content. -- FM */ if (are_different(&curdoc, &newdoc)) { LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; } } newdoc.internal_link = FALSE; newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0; HTOutputFormat = HTAtom_for("www/download"); /* * Force the document to be reloaded. */ LYforce_no_cache = TRUE; } } else if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_DOWNLOAD_CHOICE); } return 0; } static void handle_LYK_DOWN_xxx(int *old_c, int real_c, int scroll_by) { int i; if (more_text) { LYChgNewline(scroll_by); if (nlinks > 0 && curdoc.link > -1 && links[curdoc.link].ly > scroll_by) { newdoc.link = curdoc.link; for (i = 0; links[i].ly <= scroll_by; i++) --newdoc.link; } } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_END); } } static void handle_LYK_DOWN_HALF(int *old_c, int real_c) { handle_LYK_DOWN_xxx(old_c, real_c, display_lines / 2); } static void handle_LYK_DOWN_LINK(int *follow_col, int *old_c, int real_c) { if (curdoc.link < (nlinks - 1)) { /* more links? */ int newlink; if (*follow_col == -1) { const char *text = LYGetHiliteStr(curdoc.link, 0); *follow_col = links[curdoc.link].lx; if (text != NULL) *follow_col += (int) strlen(text) / 2; } newlink = find_link_near_col(*follow_col, 1); if (newlink > -1) { set_curdoc_link(newlink); } else if (more_text) { /* next page */ LYChgNewline(display_lines); } else if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_LINKS_BELOW); return; } } else if (more_text) { /* next page */ LYChgNewline(display_lines); } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_END); } } static void handle_LYK_DOWN_TWO(int *old_c, int real_c) { handle_LYK_DOWN_xxx(old_c, real_c, 2); } static int handle_LYK_DWIMEDIT(int *cmd, int *old_c, int real_c) { #ifdef TEXTAREA_AUTOEXTEDIT /* * If we're in a forms TEXTAREA, invoke the editor on *its* contents, * rather than attempting to edit the html source document. KED */ if (nlinks > 0 && LinkIsTextarea(curdoc.link)) { *cmd = LYK_EDITTEXTAREA; return 2; } /* * If we're in a forms TEXT type, tell user the request is bogus (though in * reality, without this trap, if the document with the TEXT field is * local, the editor *would* be invoked on the source .html file; eg, the * o(ptions) form tempfile). * * [This is done to avoid possible user confusion, due to auto invocation * of the editor on the TEXTAREA's contents via the above if() statement.] */ if (nlinks > 0 && links[curdoc.link].type == WWW_FORM_LINK_TYPE && links[curdoc.link].l_form->type == F_TEXT_TYPE) { HTUserMsg(CANNOT_EDIT_FIELD); return 1; } if (no_editor) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(ANYEDIT_DISABLED); } return 1; } #endif /* TEXTAREA_AUTOEXTEDIT */ return 0; } static int handle_LYK_ECGOTO(int *ch, bstring **user_input, char **old_user_input, int *old_c, int real_c) { if (no_goto && !LYValidate) { /* * Go to not allowed. - FM */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(GOTO_DISALLOWED); } return 0; } #ifdef DIRED_SUPPORT if (LYIsUIPage(curdoc.address, UIP_DIRED_MENU) || LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) || LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) { /* * Disallow editing of File Management URLs. - FM */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED); } return 0; } #endif /* DIRED_SUPPORT */ /* * Save the current user_input string, and load the current * document's address. */ StrAllocCopy(*old_user_input, (*user_input)->str); BStrCopy0((*user_input), curdoc.address); /* * Warn the user if the current document has POST data associated with it. * - FM */ if (curdoc.post_data) HTAlert(CURRENT_DOC_HAS_POST_DATA); /* * Offer the current document's URL for editing. - FM */ _statusline(EDIT_CURDOC_URL); if (((*ch = LYgetBString(user_input, FALSE, 0, RECALL_URL)) >= 0) && !isBEmpty(*user_input) && strcmp((*user_input)->str, curdoc.address)) { LYTrimAllStartfile((*user_input)->str); if (!isBEmpty(*user_input)) { return 2; } } /* * User cancelled via ^G, a full deletion, or not modifying the URL. - FM */ HTInfoMsg(CANCELLED); BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); return 0; } static void handle_LYK_EDIT(int *old_c, int real_c) { #ifdef DIRED_SUPPORT char *cp; char *tp = NULL; struct stat dir_info; #endif /* DIRED_SUPPORT */ if (no_editor) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(EDIT_DISABLED); } } #ifdef DIRED_SUPPORT /* * Allow the user to edit the link rather than curdoc in edit mode. */ else if (lynx_edit_mode && non_empty(editor) && !no_dired_support) { if (nlinks > 0) { cp = links[curdoc.link].lname; if (is_url(cp) == FILE_URL_TYPE) { cp = HTfullURL_toFile(cp); StrAllocCopy(tp, cp); FREE(cp); if (stat(tp, &dir_info) == -1) { HTAlert(NO_STATUS); } else { if (S_ISREG(dir_info.st_mode)) { StrAllocCopy(tp, links[curdoc.link].lname); HTUnEscapeSome(tp, "/"); if (edit_current_file(tp, curdoc.link, -1)) { DIRED_UNCACHE_1; move_address(&newdoc, &curdoc); #ifdef NO_SEEK_OLD_POSITION /* * Go to top of file. */ newdoc.line = 1; newdoc.link = 0; #else /* * Seek old position, which probably changed. */ newdoc.line = curdoc.line; newdoc.link = curdoc.link; #endif /* NO_SEEK_OLD_POSITION */ LYclear(); /* clear the screen */ } } } FREE(tp); } } } #endif /* DIRED_SUPPORT */ else if (non_empty(editor)) { if (edit_current_file(newdoc.address, curdoc.link, LYGetNewline())) { HTuncache_current_document(); LYforce_no_cache = TRUE; /*force reload of document */ free_address(&curdoc); /* so it doesn't get pushed */ #ifdef NO_SEEK_OLD_POSITION /* * Go to top of file. */ newdoc.line = 1; newdoc.link = 0; #else /* * Seek old position, which probably changed. */ newdoc.line = curdoc.line; newdoc.link = curdoc.link; #endif /* NO_SEEK_OLD_POSITION */ LYclear(); /* clear the screen */ } } else { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_EDITOR); } } } static void handle_LYK_DWIMHELP(const char **cshelpfile) { /* * Currently a help file different from the main 'helpfile' is shown only * if current link is a text input form field. - kw */ if (curdoc.link >= 0 && curdoc.link < nlinks && !FormIsReadonly(links[curdoc.link].l_form) && LinkIsTextLike(curdoc.link)) { *cshelpfile = STR_LYNXEDITMAP; } } static void handle_LYK_EDITMAP(int *old_c, int real_c) { if (*old_c != real_c) { *old_c = real_c; set_address(&newdoc, STR_LYNXEDITMAP); StrAllocCopy(newdoc.title, CURRENT_EDITMAP_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; #if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) /* * Remember whether we are in dired menu so we can display the right * keymap. */ if (!no_dired_support) { prev_lynx_edit_mode = lynx_edit_mode; } #endif /* DIRED_SUPPORT && OK_OVERRIDE */ LYforce_no_cache = TRUE; } } static void handle_LYK_EDIT_TEXTAREA(BOOLEAN *refresh_screen, int *old_c, int real_c) { if (no_editor) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(ANYEDIT_DISABLED); } } else if (isEmpty(editor)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_EDITOR); } } else if (LinkIsTextarea(curdoc.link)) { /* * if the current link is in a form TEXTAREA, it requires handling * for the possible multiple lines. */ /* stop screen */ stop_curses(); (void) HText_EditTextArea(&links[curdoc.link]); /* * TODO: * Move cursor "n" lines from the current line to position it on the * 1st trailing blank line in the now edited TEXTAREA. If the target * line/ anchor requires us to scroll up/down, position the target in * the approximate center of the screen. */ /* curdoc.link += n; */ /* works, except for page crossing, */ /* damnit; why is nothing ever easy */ /* start screen */ start_curses(); *refresh_screen = TRUE; } else if (LinkIsTextLike(curdoc.link)) { /* * other text fields are single-line */ stop_curses(); HText_EditTextField(&links[curdoc.link]); start_curses(); *refresh_screen = TRUE; } else { HTInfoMsg(NOT_IN_TEXTAREA_NOEDIT); } } static int handle_LYK_ELGOTO(int *ch, bstring **user_input, char **old_user_input, int *old_c, int real_c) { if (no_goto && !LYValidate) { /* * Go to not allowed. - FM */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(GOTO_DISALLOWED); } return 0; } if (!(nlinks > 0 && curdoc.link > -1) || (links[curdoc.link].type == WWW_FORM_LINK_TYPE && links[curdoc.link].l_form->type != F_SUBMIT_TYPE && links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE && links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE)) { /* * No links on page, or not a normal link or form submit button. - FM */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NOT_ON_SUBMIT_OR_LINK); } return 0; } if ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) && (isEmpty(links[curdoc.link].l_form->submit_action))) { /* * Form submit button with no ACTION defined. - FM */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_FORM_ACTION); } return 0; } #ifdef DIRED_SUPPORT if (isLYNXDIRED(links[curdoc.link].lname) || LYIsUIPage(curdoc.address, UIP_DIRED_MENU) || LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) || LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) { /* * Disallow editing of File Management URLs. - FM */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED); } return 0; } #endif /* DIRED_SUPPORT */ /* * Save the current user_input string, and load the current link's * address. - FM */ StrAllocCopy(*old_user_input, (*user_input)->str); BStrCopy0((*user_input), ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) ? links[curdoc.link].l_form->submit_action : links[curdoc.link].lname)); /* * Offer the current link's URL for editing. - FM */ _statusline(EDIT_CURLINK_URL); if (((*ch = LYgetBString(user_input, FALSE, 0, RECALL_URL)) >= 0) && !isBEmpty(*user_input) && strcmp((*user_input)->str, ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) ? links[curdoc.link].l_form->submit_action : links[curdoc.link].lname))) { LYTrimAllStartfile((*user_input)->str); if (!isBEmpty(*user_input)) { return 2; } } /* * User cancelled via ^G, a full deletion, or not modifying the URL. - FM */ HTInfoMsg(CANCELLED); BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); return 0; } #ifdef USE_EXTERNALS static void handle_LYK_EXTERN_LINK(BOOLEAN *refresh_screen) { if ((nlinks > 0) && (links[curdoc.link].lname != NULL)) { run_external(links[curdoc.link].lname, FALSE); *refresh_screen = TRUE; } } static void handle_LYK_EXTERN_PAGE(BOOLEAN *refresh_screen) { if (curdoc.address != NULL) { run_external(curdoc.address, FALSE); *refresh_screen = TRUE; } } #endif static BOOLEAN handle_LYK_FASTBACKW_LINK(int *cmd, int *old_c, int real_c) { int samepage = 0, nextlink = curdoc.link; int res; BOOLEAN code = FALSE; if (nlinks > 1) { /* * If in textarea, move to first link or textarea group before it if * there is one on this screen. - kw */ if (LinkIsTextarea(curdoc.link)) { int thisgroup = links[curdoc.link].l_form->number; char *thisname = links[curdoc.link].l_form->name; if (curdoc.link > 0 && !(LinkIsTextarea(0) && links[0].l_form->number == thisgroup && sametext(links[0].l_form->name, thisname))) { do nextlink--; while (LinkIsTextarea(nextlink) && links[nextlink].l_form->number == thisgroup && sametext(links[nextlink].l_form->name, thisname)); samepage = 1; } else if (!more_text && LYGetNewline() == 1 && (LinkIsTextarea(0) && links[0].l_form->number == thisgroup && sametext(links[0].l_form->name, thisname)) && !(LinkIsTextarea(nlinks - 1) && links[nlinks - 1].l_form->number == thisgroup && sametext(links[nlinks - 1].l_form->name, thisname))) { nextlink = nlinks - 1; samepage = 1; } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) { nextlink = 0; samepage = 1; } } else if (curdoc.link > 0) { nextlink--; samepage = 1; } else if (!more_text && LYGetNewline() == 1) { nextlink = nlinks - 1; samepage = 1; } } if (samepage) { /* * If the link as determined so far is part of a group of textarea * fields, try to use the first of them that's on the screen instead. * - kw */ if (nextlink > 0 && LinkIsTextarea(nextlink)) { int thisgroup = links[nextlink].l_form->number; char *thisname = links[nextlink].l_form->name; if (LinkIsTextarea(0) && links[0].l_form->number == thisgroup && sametext(links[0].l_form->name, thisname)) { nextlink = 0; } else while (nextlink > 1 && LinkIsTextarea(nextlink - 1) && links[nextlink - 1].l_form->number == thisgroup && sametext(links[nextlink - 1].l_form->name, thisname)) { nextlink--; } } set_curdoc_link(nextlink); } else if (LYGetNewline() > 1 && /* need a previous page */ (res = HTGetLinkOrFieldStart(curdoc.link, &Newline, &newdoc.link, -1, TRUE)) != NO) { if (res == LINK_DO_ARROWUP) { /* * It says we should use the normal PREV_LINK mechanism, so we'll * do that. - kw */ if (nlinks > 0) curdoc.link = 0; *cmd = LYK_PREV_LINK; code = TRUE; } else { LYChgNewline(1); /* our line counting starts with 1 not 0 */ } } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(NO_LINKS_ABOVE); } return code; } static void handle_LYK_FASTFORW_LINK(int *old_c, int real_c) { int samepage = 0, nextlink = curdoc.link; if (nlinks > 1) { /* * If in textarea, move to first link or field after it if there is one * on this screen. - kw */ if (LinkIsTextarea(curdoc.link)) { int thisgroup = links[curdoc.link].l_form->number; char *thisname = links[curdoc.link].l_form->name; if (curdoc.link < nlinks - 1 && !(LinkIsTextarea(nlinks - 1) && links[nlinks - 1].l_form->number == thisgroup && sametext(links[nlinks - 1].l_form->name, thisname))) { do nextlink++; while (LinkIsTextarea(nextlink) && links[nextlink].l_form->number == thisgroup && sametext(links[nextlink].l_form->name, thisname)); samepage = 1; } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) { nextlink = 0; samepage = 1; } } else if (curdoc.link < nlinks - 1) { nextlink++; samepage = 1; } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) { nextlink = 0; samepage = 1; } } if (samepage) { set_curdoc_link(nextlink); } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) { /* * At the bottom of list and there is only one page. Move to the top * link on the page. */ set_curdoc_link(0); } else if (more_text && /* need a later page */ HTGetLinkOrFieldStart(curdoc.link, &Newline, &newdoc.link, 1, TRUE) != NO) { LYChgNewline(1); /* our line counting starts with 1 not 0 */ /* nothing more to do here */ } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(NO_LINKS_BELOW); } return; } static void handle_LYK_FIRST_LINK(void) { int i = curdoc.link; for (;;) { if (--i < 0 || links[i].ly != links[curdoc.link].ly) { set_curdoc_link(i + 1); break; } } } static BOOLEAN handle_LYK_GOTO(int *ch, bstring **user_input, char **old_user_input, RecallType * recall, int *URLTotal, int *URLNum, BOOLEAN *FirstURLRecall, int *old_c, int real_c) { if (no_goto && !LYValidate) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(GOTO_DISALLOWED); } return FALSE; } StrAllocCopy(*old_user_input, (*user_input)->str); if (!goto_buffer) BStrCopy0((*user_input), ""); *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0); if (goto_buffer && !isBEmpty(*user_input)) { *recall = ((*URLTotal > 1) ? RECALL_URL : NORECALL); *URLNum = 0; *FirstURLRecall = FALSE; } else { *recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL); *URLNum = *URLTotal; *FirstURLRecall = TRUE; } /* * Ask the user. */ _statusline(URL_TO_OPEN); if ((*ch = LYgetBString(user_input, FALSE, 0, *recall)) < 0) { /* * User cancelled the Goto via ^G. Restore user_input and * break. - FM */ BStrCopy0((*user_input), *old_user_input); FREE(*old_user_input); HTInfoMsg(CANCELLED); return FALSE; } return TRUE; } static void handle_LYK_GROW_TEXTAREA(BOOLEAN *refresh_screen) { /* * See if the current link is in a form TEXTAREA. */ if (LinkIsTextarea(curdoc.link)) { HText_ExpandTextarea(&links[curdoc.link], TEXTAREA_EXPAND_SIZE); *refresh_screen = TRUE; } else { HTInfoMsg(NOT_IN_TEXTAREA); } } static BOOLEAN handle_LYK_HEAD(int *cmd) { int c; if (nlinks > 0 && (links[curdoc.link].type != WWW_FORM_LINK_TYPE || links[curdoc.link].l_form->type == F_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE || links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE)) { /* * We have links, and the current link is a normal link or a form's * submit button. - FM */ _statusline(HEAD_D_L_OR_CANCEL); c = LYgetch_single(); if (c == 'D') { char *scheme = !isLYNXIMGMAP(curdoc.address) ? curdoc.address : curdoc.address + LEN_LYNXIMGMAP; if (LYCanDoHEAD(scheme) != TRUE) { HTUserMsg(DOC_NOT_HTTP_URL); } else { /* * Check if this is a reply from a POST, and if so, seek * confirmation if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) { HTInfoMsg(CANCELLED); } else { HEAD_request = TRUE; LYforce_no_cache = TRUE; StrAllocCopy(newdoc.title, curdoc.title); if (HTLoadedDocumentIsHEAD()) { HText_setNoCache(HTMainText); free_address(&curdoc); } else { StrAllocCat(newdoc.title, " - HEAD"); } } } } else if (c == 'L') { if (links[curdoc.link].type != WWW_FORM_LINK_TYPE && StrNCmp(links[curdoc.link].lname, "http", 4) && StrNCmp(links[curdoc.link].lname, "LYNXIMGMAP:http", 15) && LYCanDoHEAD(links[curdoc.link].lname) != TRUE && (links[curdoc.link].type != WWW_INTERN_LINK_TYPE || !curdoc.address || StrNCmp(curdoc.address, "http", 4))) { HTUserMsg(LINK_NOT_HTTP_URL); } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE && FormIsReadonly(links[curdoc.link].l_form)) { HTUserMsg(FORM_ACTION_DISABLED); } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE && links[curdoc.link].l_form->submit_action != 0 && !isLYNXCGI(links[curdoc.link].l_form->submit_action) && StrNCmp(links[curdoc.link].l_form->submit_action, "http", 4)) { HTUserMsg(FORM_ACTION_NOT_HTTP_URL); } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE && links[curdoc.link].l_form->submit_method == URL_POST_METHOD && HTConfirm(CONFIRM_POST_LINK_HEAD) == FALSE) { HTInfoMsg(CANCELLED); } else { HEAD_request = TRUE; LYforce_no_cache = TRUE; *cmd = LYK_ACTIVATE; return TRUE; } } } else { /* * We can offer only this document for a HEAD request. Check if this * is a reply from a POST, and if so, seek confirmation if the safe * element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) { HTInfoMsg(CANCELLED); } else { if (nlinks > 0) { /* * The current link is a non-submittable form link, so prompt * the user to make it clear that the HEAD request would be for * the current document, not the form link. - FM */ _statusline(HEAD_D_OR_CANCEL); c = LYgetch_single(); } else { /* * No links, so we can just assume that the user wants a HEAD * request for the current document. - FM */ c = 'D'; } if (c == 'D') { char *scheme = !isLYNXIMGMAP(curdoc.address) ? curdoc.address : curdoc.address + LEN_LYNXIMGMAP; /* * The user didn't cancel, so check if a HEAD request is * appropriate for the current document. - FM */ if (LYCanDoHEAD(scheme) != TRUE) { HTUserMsg(DOC_NOT_HTTP_URL); } else { HEAD_request = TRUE; LYforce_no_cache = TRUE; StrAllocCopy(newdoc.title, curdoc.title); if (HTLoadedDocumentIsHEAD()) { HText_setNoCache(HTMainText); free_address(&curdoc); } else { StrAllocCat(newdoc.title, " - HEAD"); } } } } } return FALSE; } static void handle_LYK_HELP(const char **cshelpfile) { char *my_value = NULL; if (*cshelpfile == NULL) *cshelpfile = helpfile; StrAllocCopy(my_value, *cshelpfile); LYEnsureAbsoluteURL(&my_value, *cshelpfile, FALSE); if (!STREQ(curdoc.address, my_value)) { /* * Set the filename. */ set_address(&newdoc, my_value); /* * Make a name for this help file. */ StrAllocCopy(newdoc.title, gettext("Help Screen")); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; } FREE(my_value); *cshelpfile = NULL; /* reset pointer - kw */ } static void handle_LYK_HISTORICAL(void) { #ifdef USE_SOURCE_CACHE if (!HTcan_reparse_document()) { #endif /* * Check if this is a reply from a POST, and if so, seek confirmation * of reload if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) { HTInfoMsg(WILL_NOT_RELOAD_DOC); } else { HText_setNoCache(HTMainText); move_address(&newdoc, &curdoc); newdoc.line = curdoc.line; newdoc.link = curdoc.link; } #ifdef USE_SOURCE_CACHE } /* end if no bypass */ #endif historical_comments = (BOOLEAN) !historical_comments; if (minimal_comments) { HTAlert(historical_comments ? HISTORICAL_ON_MINIMAL_OFF : HISTORICAL_OFF_MINIMAL_ON); } else { HTAlert(historical_comments ? HISTORICAL_ON_VALID_OFF : HISTORICAL_OFF_VALID_ON); } #ifdef USE_SOURCE_CACHE (void) reparse_document(); #endif return; } static BOOLEAN handle_LYK_HISTORY(int ForcePush) { if (curdoc.title && !LYIsUIPage(curdoc.address, UIP_HISTORY)) { /* * Don't do this if already viewing history page. * * Push the current file so that the history list contains the current * file for printing purposes. Pop the file afterwards to prevent * multiple copies. */ if (TRACE && !LYUseTraceLog && LYCursesON) { LYHideCursor(); /* make sure cursor is down */ #ifdef USE_SLANG LYaddstr("\n"); #endif /* USE_SLANG */ LYrefresh(); } LYpush(&curdoc, ForcePush); /* * Print history options to file. */ if (showhistory(&newdoc.address) < 0) { LYpop(&curdoc); return TRUE; } LYRegisterUIPage(newdoc.address, UIP_HISTORY); StrAllocCopy(newdoc.title, HISTORY_PAGE_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; newdoc.link = 1; /*@@@ bypass "recent statusline messages" link */ free_address(&curdoc); /* so it doesn't get pushed */ if (LYValidate || check_realm) { LYPermitURL = TRUE; } return TRUE; } /* end if StrNCmp */ return FALSE; } static BOOLEAN handle_LYK_IMAGE_TOGGLE(int *cmd) { clickable_images = (BOOLEAN) !clickable_images; HTUserMsg(clickable_images ? CLICKABLE_IMAGES_ON : CLICKABLE_IMAGES_OFF); return reparse_or_reload(cmd); } static void handle_LYK_INDEX(int *old_c, int real_c) { /* * Make sure we are not in the index already. */ if (!STREQ(curdoc.address, indexfile)) { if (indexfile[0] == '\0') { /* no defined index */ if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_INDEX_FILE); } } else { #ifdef KANJI_CODE_OVERRIDE if (HTCJK == JAPANESE) { last_kcode = NOKANJI; /* AUTO */ } #endif #ifdef USE_PROGRAM_DIR if (is_url(indexfile) == 0) { char *tmp = NULL; HTSprintf0(&tmp, "%s\\%s", program_dir, indexfile); FREE(indexfile); LYLocalFileToURL(&indexfile, tmp); FREE(tmp); } #endif set_address(&newdoc, indexfile); StrAllocCopy(newdoc.title, gettext("System Index")); /* name it */ LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; } /* end else */ } /* end if */ } static void handle_LYK_INDEX_SEARCH(BOOLEAN *force_load, int ForcePush, int *old_c, int real_c) { if (is_www_index) { /* * Perform a database search. * * do_www_search will try to go out and get the document. If it * returns TRUE, a new document was returned and is named in the * newdoc.address. */ newdoc.isHEAD = FALSE; newdoc.safe = FALSE; if (do_www_search(&newdoc) == NORMAL) { /* * Yah, the search succeeded. */ if (TRACE && !LYUseTraceLog && LYCursesON) { /* * Make sure cursor is down. */ LYHideCursor(); #ifdef USE_SLANG LYaddstr("\n"); #endif /* USE_SLANG */ LYrefresh(); } LYpush(&curdoc, ForcePush); /* * Make the curdoc.address the newdoc.address so that getfile * doesn't try to get the newdoc.address. Since we have already * gotten it. */ copy_address(&curdoc, &newdoc); BStrCopy(newdoc.post_data, curdoc.post_data); StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type); newdoc.internal_link = FALSE; curdoc.line = -1; LYSetNewline(0); } else if (use_this_url_instead != NULL) { /* * Got back a redirecting URL. Check it out. */ HTUserMsg2(WWW_USING_MESSAGE, use_this_url_instead); /* * Make a name for this URL. */ StrAllocCopy(newdoc.title, "A URL specified by redirection"); set_address(&newdoc, use_this_url_instead); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; FREE(use_this_url_instead); *force_load = TRUE; } else { /* * Yuk, the search failed. Restore the old file. */ copy_address(&newdoc, &curdoc); BStrCopy(newdoc.post_data, curdoc.post_data); StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type); StrAllocCopy(newdoc.bookmark, curdoc.bookmark); newdoc.isHEAD = curdoc.isHEAD; newdoc.safe = curdoc.safe; newdoc.internal_link = curdoc.internal_link; } } else if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NOT_ISINDEX); } } static BOOLEAN handle_LYK_INFO(int *cmd) { /* * Don't do if already viewing info page. */ if (!LYIsUIPage(curdoc.address, UIP_SHOWINFO)) { if (do_change_link() != -1 && LYShowInfo(&curdoc, &newdoc, owner_address) >= 0) { LYRegisterUIPage(newdoc.address, UIP_SHOWINFO); StrAllocCopy(newdoc.title, SHOWINFO_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; LYforce_no_cache = TRUE; if (LYValidate || check_realm) LYPermitURL = TRUE; } } else { /* * If already in info page, get out. */ *cmd = LYK_PREV_DOC; return TRUE; } return FALSE; } static BOOLEAN handle_LYK_INLINE_TOGGLE(int *cmd) { pseudo_inline_alts = (BOOLEAN) !pseudo_inline_alts; HTUserMsg(pseudo_inline_alts ? PSEUDO_INLINE_ALTS_ON : PSEUDO_INLINE_ALTS_OFF); return reparse_or_reload(cmd); } static void handle_LYK_INSERT_FILE(BOOLEAN *refresh_screen, int *old_c, int real_c) { /* * See if the current link is in a form TEXTAREA. */ if (LinkIsTextarea(curdoc.link)) { /* * Reject attempts to use this for gaining access to local files when * such access is restricted: if no_file_url was set via the file_url * restriction, if no_goto_file was set for the anonymous account, or * if HTDirAccess was set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the * -nobrowse or -selective switches, it is assumed that inserting files * or checking for existence of files needs to be denied. - kw */ if (no_file_url || no_goto_file || HTDirAccess == HT_DIR_FORBID || HTDirAccess == HT_DIR_SELECTIVE) { if (*old_c != real_c) { *old_c = real_c; if (no_goto_file) HTUserMsg2(GOTO_XXXX_DISALLOWED, STR_FILE_URL); else HTUserMsg(NOAUTH_TO_ACCESS_FILES); HTInfoMsg(FILE_INSERT_CANCELLED); } return; } (void) HText_InsertFile(&links[curdoc.link]); /* * TODO: * Move cursor "n" lines from the current line to position it on the * 1st line following the text that was inserted. If the target * line/anchor requires us to scroll up/down, position the target in * the approximate center of the screen. * * [Current behavior leaves cursor on the same line relative to the * start of the TEXTAREA that it was on before the insertion. This is * the same behavior that occurs with (my) editor, so this TODO will * stay unimplemented.] */ *refresh_screen = TRUE; } else { HTInfoMsg(NOT_IN_TEXTAREA); } } #if defined(DIRED_SUPPORT) && defined(OK_INSTALL) static void handle_LYK_INSTALL(void) { if (lynx_edit_mode && nlinks > 0 && !no_dired_support) local_install(NULL, links[curdoc.link].lname, &newdoc.address); } #endif static const char *hexy = "0123456789ABCDEF"; #define HEX(n) hexy[(n) & 0xf] /* * URL-encode a parameter which can then be appended to a URI. * RFC-3986 lists reserved characters, which should be encoded. */ static char *urlencode(char *str) { char *result = NULL; char *ptr; int ch; if (str != NULL) { result = malloc(strlen(str) * 3 + 1); ptr = result; assert(result); while ((ch = UCH(*str++)) != 0) { if (ch == ' ') { *ptr = '+'; ptr++; } else if (ch > 127 || StrChr(":/?#[]@!$&'()*+,;=", ch) != 0) { *ptr++ = '%'; *ptr++ = HEX(ch >> 4); *ptr++ = HEX(ch); } else { *ptr++ = (char) ch; } } *ptr = '\0'; } return result; } /* * Fill in "%s" marker(s) in the url_template by prompting the user for the * values. */ static BOOLEAN check_JUMP_param(char **url_template) { int param = 1; char *subs; char *result = *url_template; char *encoded = NULL; int code = TRUE; bstring *input = NULL; CTRACE((tfp, "check_JUMP_param: %s\n", result)); while ((subs = strstr(result, "%s")) != 0) { char prompt[MAX_LINE]; RecallType recall = NORECALL; CTRACE((tfp, "Prompt for query param%d: %s\n", param, result)); sprintf(prompt, gettext("Query parameter %d: "), param++); statusline(prompt); BStrCopy0(input, ""); if (encoded) FREE(encoded); if (LYgetBString(&input, FALSE, 0, recall) < 0) { /* * cancelled via ^G */ HTInfoMsg(CANCELLED); code = FALSE; break; } else if ((encoded = urlencode(input->str)) != '\0') { int subs_at = (int) (subs - result); int fill_in = (int) strlen(encoded) - 2; size_t have = strlen(result); size_t want = strlen(encoded) + have - 1; int n; char *update = realloc(result, want + 1); if (update == 0) { HTInfoMsg(NOT_ENOUGH_MEMORY); code = FALSE; break; } CTRACE((tfp, " reply: %s\n", input->str)); CTRACE((tfp, " coded: %s\n", encoded)); result = update; result[want] = '\0'; for (n = (int) want; (n - fill_in) >= subs_at; --n) { result[n] = result[n - fill_in]; } for (n = subs_at; encoded[n - subs_at] != '\0'; ++n) { result[n] = encoded[n - subs_at]; } CTRACE((tfp, " subst: %s\n", result)); } else { HTInfoMsg(CANCELLED); code = FALSE; break; } } BStrFree(input); FREE(encoded); *url_template = result; return (BOOLEAN) code; } static void fill_JUMP_Params(char **addressp) { if (LYJumpFileURL) { check_JUMP_param(addressp); } } static BOOLEAN handle_LYK_JUMP(int c, bstring **user_input, char **old_user_input GCC_UNUSED, RecallType * recall GCC_UNUSED, BOOLEAN *FirstURLRecall GCC_UNUSED, int *URLNum GCC_UNUSED, int *URLTotal GCC_UNUSED, int *ch GCC_UNUSED, int *old_c, int real_c) { char *ret; if (no_jump || JThead == NULL) { if (*old_c != real_c) { *old_c = real_c; if (no_jump) HTUserMsg(JUMP_DISALLOWED); else HTUserMsg(NO_JUMPFILE); } } else { LYJumpFileURL = TRUE; if ((ret = LYJump(c)) != NULL) { #ifdef PERMIT_GOTO_FROM_JUMP if (!strncasecomp(ret, "Go ", 3)) { LYJumpFileURL = FALSE; StrAllocCopy(*old_user_input, (*user_input)->str); *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0); *recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL); *URLNum = *URLTotal; *FirstURLRecall = TRUE; if (!strcasecomp(ret, "Go :")) { if (recall) { *ch = UPARROW_KEY; return TRUE; } FREE(*old_user_input); HTUserMsg(NO_RANDOM_URLS_YET); return FALSE; } ret = HTParse((ret + 3), startfile, PARSE_ALL); BStrCopy0((*user_input), ret); FREE(ret); return TRUE; } #endif /* PERMIT_GOTO_FROM_JUMP */ ret = HTParse(ret, startfile, PARSE_ALL); if (!LYTrimStartfile(ret)) { LYRemoveBlanks((*user_input)->str); } if (check_JUMP_param(&ret)) { set_address(&newdoc, ret); StrAllocCopy(lynxjumpfile, ret); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; LYUserSpecifiedURL = TRUE; } FREE(ret); } else { LYJumpFileURL = FALSE; } } return FALSE; } static void handle_LYK_KEYMAP(BOOLEAN *vi_keys_flag, BOOLEAN *emacs_keys_flag, int *old_c, int real_c) { if (*old_c != real_c) { *old_c = real_c; set_address(&newdoc, STR_LYNXKEYMAP); StrAllocCopy(newdoc.title, CURRENT_KEYMAP_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; /* * If vi_keys changed, the keymap did too, so force no cache, and reset * the flag. - FM */ if (*vi_keys_flag != vi_keys || *emacs_keys_flag != emacs_keys) { LYforce_no_cache = TRUE; *vi_keys_flag = vi_keys; *emacs_keys_flag = emacs_keys; } #if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) /* * Remember whether we are in dired menu so we can display the right * keymap. */ if (!no_dired_support) { prev_lynx_edit_mode = lynx_edit_mode; } #endif /* DIRED_SUPPORT && OK_OVERRIDE */ LYforce_no_cache = TRUE; } } static void handle_LYK_LAST_LINK(void) { int i = curdoc.link; for (;;) { if (++i >= nlinks || links[i].ly != links[curdoc.link].ly) { set_curdoc_link(i - 1); break; } } } static void handle_LYK_LEFT_LINK(void) { if (curdoc.link > 0 && links[curdoc.link].ly == links[curdoc.link - 1].ly) { set_curdoc_link(curdoc.link - 1); } } static BOOLEAN handle_LYK_LIST(int *cmd) { /* * Don't do if already viewing list page. */ if (!strcmp(NonNull(curdoc.title), LIST_PAGE_TITLE) && LYIsUIPage(curdoc.address, UIP_LIST_PAGE)) { /* * Already viewing list page, so get out. */ *cmd = LYK_PREV_DOC; return TRUE; } /* * Print list page to file. */ if (showlist(&newdoc, TRUE) < 0) return FALSE; StrAllocCopy(newdoc.title, LIST_PAGE_TITLE); /* * showlist will set newdoc's other fields. It may leave post_data intact * so the list can be used to follow internal links in the current document * even if it is a POST response. - kw */ if (LYValidate || check_realm) { LYPermitURL = TRUE; StrAllocCopy(lynxlistfile, newdoc.address); } return FALSE; } static void handle_LYK_MAIN_MENU(int *old_c, int real_c) { /* * If its already the homepage then don't reload it. */ if (!STREQ(curdoc.address, homepage)) { if (HTConfirmDefault(CONFIRM_MAIN_SCREEN, NO) == YES) { set_address(&newdoc, homepage); StrAllocCopy(newdoc.title, gettext("Entry into main screen")); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; LYhighlight(FALSE, curdoc.link, prev_target->str); #ifdef DIRED_SUPPORT if (lynx_edit_mode) { DIRED_UNCACHE_2; } #endif /* DIRED_SUPPORT */ } } else { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(IN_MAIN_SCREEN); } } } static void handle_LYK_MINIMAL(void) { if (!historical_comments) { #ifdef USE_SOURCE_CACHE if (!HTcan_reparse_document()) { #endif /* * Check if this is a reply from a POST, and if so, seek * confirmation of reload if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) { HTInfoMsg(WILL_NOT_RELOAD_DOC); } else { HText_setNoCache(HTMainText); move_address(&newdoc, &curdoc); newdoc.line = curdoc.line; newdoc.link = curdoc.link; } #ifdef USE_SOURCE_CACHE } /* end if no bypass */ #endif } minimal_comments = (BOOLEAN) !minimal_comments; if (!historical_comments) { HTAlert(minimal_comments ? MINIMAL_ON_IN_EFFECT : MINIMAL_OFF_VALID_ON); } else { HTAlert(minimal_comments ? MINIMAL_ON_BUT_HISTORICAL : MINIMAL_OFF_HISTORICAL_ON); } #ifdef USE_SOURCE_CACHE (void) reparse_document(); #endif return; } #if defined(DIRED_SUPPORT) static void handle_LYK_MODIFY(BOOLEAN *refresh_screen) { if (lynx_edit_mode && nlinks > 0 && !no_dired_support) { int ret; ret = local_modify(&curdoc, &newdoc.address); if (ret == PERMIT_FORM_RESULT) { /* Permit form thrown up */ *refresh_screen = TRUE; } else if (ret) { DIRED_UNCACHE_1; move_address(&newdoc, &curdoc); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; newdoc.line = curdoc.line; newdoc.link = curdoc.link; LYclear(); } } } #endif /* DIRED_SUPPORT */ #ifdef EXP_NESTED_TABLES static BOOLEAN handle_LYK_NESTED_TABLES(int *cmd) { nested_tables = (BOOLEAN) !nested_tables; HTUserMsg(nested_tables ? NESTED_TABLES_ON : NESTED_TABLES_OFF); return reparse_or_reload(cmd); } #endif static BOOLEAN handle_LYK_OPTIONS(int *cmd, BOOLEAN *refresh_screen) { #ifndef NO_OPTION_MENU if (!LYUseFormsOptions) { BOOLEAN LYUseDefaultRawMode_flag = LYUseDefaultRawMode; BOOLEAN LYSelectPopups_flag = LYSelectPopups; BOOLEAN verbose_img_flag = verbose_img; BOOLEAN keypad_mode_flag = (BOOL) keypad_mode; BOOLEAN show_dotfiles_flag = show_dotfiles; BOOLEAN user_mode_flag = (BOOL) user_mode; int CurrentAssumeCharSet_flag = UCLYhndl_for_unspec; int CurrentCharSet_flag = current_char_set; int HTfileSortMethod_flag = HTfileSortMethod; char *CurrentUserAgent = NULL; char *CurrentNegoLanguage = NULL; char *CurrentNegoCharset = NULL; StrAllocCopy(CurrentUserAgent, NonNull(LYUserAgent)); StrAllocCopy(CurrentNegoLanguage, NonNull(language)); StrAllocCopy(CurrentNegoCharset, NonNull(pref_charset)); LYoptions(); /** do the old-style options stuff **/ if (keypad_mode_flag != keypad_mode || (user_mode_flag != user_mode && (user_mode_flag == NOVICE_MODE || user_mode == NOVICE_MODE)) || (((HTfileSortMethod_flag != HTfileSortMethod) || (show_dotfiles_flag != show_dotfiles)) && (isFILE_URL(curdoc.address) || isFTP_URL(curdoc.address))) || CurrentCharSet_flag != current_char_set || CurrentAssumeCharSet_flag != UCLYhndl_for_unspec || verbose_img_flag != verbose_img || LYUseDefaultRawMode_flag != LYUseDefaultRawMode || LYSelectPopups_flag != LYSelectPopups || ((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) || strcmp(CurrentNegoLanguage, NonNull(language)) || strcmp(CurrentNegoCharset, NonNull(pref_charset))) && (!StrNCmp(curdoc.address, "http", 4) || isLYNXCGI(curdoc.address)))) { BOOLEAN canreparse_post = FALSE; /* * Check if this is a reply from a POST, and if so, seek * confirmation of reload if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && #ifdef USE_SOURCE_CACHE (!(canreparse_post = HTcan_reparse_document())) && #endif confirm_post_resub(curdoc.address, curdoc.title, 2, 1) == FALSE) { HTInfoMsg(WILL_NOT_RELOAD_DOC); } else { copy_address(&newdoc, &curdoc); if (((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) || strcmp(CurrentNegoLanguage, NonNull(language)) || strcmp(CurrentNegoCharset, NonNull(pref_charset))) && (StrNCmp(curdoc.address, "http", 4) == 0 || !isLYNXCGI(curdoc.address) == 0))) { /* * An option has changed which may influence content * negotiation, and the resource is from a http or https or * lynxcgi URL (the only protocols which currently do * anything with this information). Set reloading = TRUE * so that proxy caches will be flushed, which is necessary * until the time when all proxies understand HTTP 1.1 * Vary: and all Servers properly use it... Treat like * case LYK_RELOAD (see comments there). - KW */ reloading = TRUE; } if (HTisDocumentSource()) { srcmode_for_next_retrieval(1); } #ifdef USE_SOURCE_CACHE if (reloading == FALSE) { /* one more attempt to be smart enough: */ if (reparse_document()) { FREE(CurrentUserAgent); FREE(CurrentNegoLanguage); FREE(CurrentNegoCharset); return FALSE; } } #endif if (canreparse_post && confirm_post_resub(curdoc.address, curdoc.title, 2, 1) == FALSE) { if (HTisDocumentSource()) { srcmode_for_next_retrieval(0); } FREE(CurrentUserAgent); FREE(CurrentNegoLanguage); FREE(CurrentNegoCharset); return FALSE; } HEAD_request = HTLoadedDocumentIsHEAD(); HText_setNoCache(HTMainText); newdoc.line = curdoc.line; newdoc.link = curdoc.link; LYforce_no_cache = TRUE; free_address(&curdoc); /* So it doesn't get pushed. */ } } FREE(CurrentUserAgent); FREE(CurrentNegoLanguage); FREE(CurrentNegoCharset); *refresh_screen = TRUE; /* to repaint screen */ return FALSE; } /* end if !LYUseFormsOptions */ #endif /* !NO_OPTION_MENU */ #ifndef NO_OPTION_FORMS /* * Generally stolen from LYK_COOKIE_JAR. Options menu handling is * done in postoptions(), called from getfile() currently. * * postoptions() is also responsible for reloading the document * before the 'options menu' but only when (a few) important * options were changed. * * It is critical that post_data is freed here since the * submission of changed options is done via the same protocol as * LYNXOPTIONS: */ /* * Don't do if already viewing options page. */ if (!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU)) { set_address(&newdoc, LYNXOPTIONS_PAGE("/")); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; LYforce_no_cache = TRUE; /* change to 'if (check_realm && !LYValidate)' and make change near top of getfile to forbid using forms options menu with -validate: - kw */ if (LYValidate || check_realm) { LYPermitURL = TRUE; } } else { /* * If already in the options menu, get out. */ *cmd = LYK_PREV_DOC; return TRUE; } #endif /* !NO_OPTION_FORMS */ return FALSE; } static void handle_NEXT_DOC(void) { if (LYhist_next(&curdoc, &newdoc)) { free_address(&curdoc); /* avoid push */ return; } HTUserMsg(gettext("No next document present")); } static void handle_LYK_NEXT_LINK(int c, int *old_c, int real_c) { if (curdoc.link < nlinks - 1) { /* next link */ LYhighlight(FALSE, curdoc.link, prev_target->str); #ifdef FASTTAB /* * Move to different textarea if TAB in textarea. */ if (LinkIsTextarea(curdoc.link) && c == '\t') { int thisgroup = links[curdoc.link].l_form->number; char *thisname = links[curdoc.link].l_form->name; do curdoc.link++; while ((curdoc.link < nlinks - 1) && LinkIsTextarea(curdoc.link) && links[curdoc.link].l_form->number == thisgroup && sametext(links[curdoc.link].l_form->name, thisname)); } else { curdoc.link++; } #else curdoc.link++; #endif /* FASTTAB */ /* * At the bottom of list and there is only one page. Move to the top * link on the page. */ } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) { set_curdoc_link(0); } else if (more_text) { /* next page */ LYChgNewline(display_lines); } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_END); } } static void handle_LYK_NEXT_PAGE(int *old_c, int real_c) { if (more_text) { LYChgNewline(display_lines); } else if (curdoc.link < nlinks - 1) { set_curdoc_link(nlinks - 1); } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_END); } } static BOOLEAN handle_LYK_NOCACHE(int *old_c, int real_c) { if (nlinks > 0) { if (links[curdoc.link].type == WWW_FORM_LINK_TYPE && links[curdoc.link].l_form->type != F_SUBMIT_TYPE && links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE && links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NOT_ON_SUBMIT_OR_LINK); } return FALSE; } else { LYforce_no_cache = TRUE; reloading = TRUE; } } return TRUE; } static void handle_LYK_PREV_LINK(int *arrowup, int *old_c, int real_c) { if (curdoc.link > 0) { /* previous link */ set_curdoc_link(curdoc.link - 1); } else if (!more_text && curdoc.link == 0 && LYGetNewline() == 1) { /* at the top of list */ /* * If there is only one page of data and the user goes off the top, * just move the cursor to last link on the page. */ set_curdoc_link(nlinks - 1); } else if (curdoc.line > 1) { /* previous page */ /* * Go back to the previous page. */ int scrollamount = (LYGetNewline() > display_lines ? display_lines : LYGetNewline() - 1); LYChgNewline(-scrollamount); if (scrollamount < display_lines && nlinks > 0 && curdoc.link == 0 && links[0].ly - 1 + scrollamount <= display_lines) { newdoc.link = HText_LinksInLines(HTMainText, 1, scrollamount) - 1; } else { *arrowup = TRUE; } } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_BEGIN); } } #define nhist_1 (nhist - 1) /* workaround for indent */ static int handle_PREV_DOC(int *cmd, int *old_c, int real_c) { if (nhist > 0) { /* if there is anything to go back to */ /* * Check if the previous document is a reply from a POST, and if so, * seek confirmation of resubmission if the safe element is not set and * the document is not still in the cache or LYresubmit_posts is set. * If not confirmed and it is not the startfile, pop it so we go to the * yet previous document, until we're OK or reach the startfile. If we * reach the startfile and its not OK or we don't get confirmation, * cancel. - FM */ DocAddress WWWDoc; HTParentAnchor *tmpanchor; BOOLEAN conf = FALSE, first = TRUE; HTLastConfirmCancelled(); /* reset flag */ while (nhist > 0) { conf = FALSE; if (HDOC(nhist_1).post_data == NULL) { break; } WWWDoc.address = HDOC(nhist_1).address; WWWDoc.post_data = HDOC(nhist_1).post_data; WWWDoc.post_content_type = HDOC(nhist_1).post_content_type; WWWDoc.bookmark = HDOC(nhist_1).bookmark; WWWDoc.isHEAD = HDOC(nhist_1).isHEAD; WWWDoc.safe = HDOC(nhist_1).safe; tmpanchor = HTAnchor_findAddress(&WWWDoc); if (HTAnchor_safe(tmpanchor)) { break; } if ((HTAnchor_document(tmpanchor) == NULL && (isLYNXIMGMAP(WWWDoc.address) || (conf = confirm_post_resub(WWWDoc.address, HDOC(nhist_1).title, 0, 0)) == FALSE)) || ((LYresubmit_posts && !conf && (NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)], &curdoc) || NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)], &newdoc))) && !confirm_post_resub(WWWDoc.address, HDOC(nhist_1).title, 2, 2))) { if (HTLastConfirmCancelled()) { if (!first && curdoc.internal_link) free_address(&curdoc); *cmd = LYK_DO_NOTHING; return 2; } if (nhist == 1) { HTInfoMsg(CANCELLED); *old_c = 0; *cmd = LYK_DO_NOTHING; return 2; } else { HTUserMsg2(WWW_SKIP_MESSAGE, WWWDoc.address); do { /* Should be LYhist_prev when _next supports */ LYpop(&curdoc); /* skipping of forms */ } while (nhist > 1 && !are_different((DocInfo *) &history[nhist_1], &curdoc)); first = FALSE; /* have popped at least one */ continue; } } else { /* * Break from loop; if user just confirmed to load again * because document wasn't in cache, set LYforce_no_cache to * avoid unnecessary repeat question down the road. - kw */ if (conf) LYforce_no_cache = TRUE; break; } } if (!first) curdoc.internal_link = FALSE; /* * Set newdoc.address to empty to pop a file. */ LYhist_prev_register(&curdoc); /* Why not call _prev instead of zeroing address? */ free_address(&newdoc); #ifdef DIRED_SUPPORT if (lynx_edit_mode) { DIRED_UNCACHE_2; } #endif /* DIRED_SUPPORT */ } else if (child_lynx == TRUE) { return (1); /* exit on left arrow in main screen */ } else if (*old_c != real_c) { *old_c = real_c; HTUserMsg(ALREADY_AT_FIRST); } return 0; } static void handle_LYK_PREV_PAGE(int *old_c, int real_c) { if (LYGetNewline() > 1) { LYChgNewline(-display_lines); } else if (curdoc.link > 0) { set_curdoc_link(0); } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_BEGIN); } } static void handle_LYK_PRINT(BOOLEAN *ForcePush, int *old_c, int real_c) { if (LYValidate) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(PRINT_DISABLED); } return; } /* * Don't do if already viewing print options page. */ if (!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS) && print_options(&newdoc.address, curdoc.address, HText_getNumOfLines()) >= 0) { LYRegisterUIPage(newdoc.address, UIP_PRINT_OPTIONS); StrAllocCopy(newdoc.title, PRINT_OPTIONS_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; *ForcePush = TRUE; /* see LYpush() and print_options() */ if (check_realm) LYPermitURL = TRUE; } } static BOOLEAN handle_LYK_QUIT(void) { int c; if (LYQuitDefaultYes == TRUE) { c = HTConfirmDefault(REALLY_QUIT, YES); } else { c = HTConfirmDefault(REALLY_QUIT, NO); } if (LYQuitDefaultYes == TRUE) { if (c != NO) { return (TRUE); } else { HTInfoMsg(NO_CANCEL); } } else if (c == YES) { return (TRUE); } else { HTInfoMsg(NO_CANCEL); } return FALSE; } static BOOLEAN handle_LYK_RAW_TOGGLE(int *cmd) { if (HTLoadedDocumentCharset()) { HTUserMsg(gettext("charset for this document specified explicitly, sorry...")); return FALSE; } else { LYUseDefaultRawMode = (BOOL) !LYUseDefaultRawMode; HTUserMsg(LYRawMode ? RAWMODE_OFF : RAWMODE_ON); HTMLSetCharacterHandling(current_char_set); return reparse_or_reload(cmd); } } static void handle_LYK_RELOAD(int real_cmd) { /* * Check if this is a reply from a POST, and if so, * seek confirmation if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) { HTInfoMsg(CANCELLED); return; } /* * Check to see if should reload source, or load html */ if (HTisDocumentSource()) { if ((forced_UCLYhdnl = HTMainText_Get_UCLYhndl()) >= 0) force_old_UCLYhndl_on_reload = TRUE; srcmode_for_next_retrieval(1); } HEAD_request = HTLoadedDocumentIsHEAD(); HText_setNoCache(HTMainText); /* * Do assume the reloaded document will be the same. - FM * * (I don't remember all the reasons why we couldn't assume this. As the * problems show up, we'll try to fix them, or add warnings. - FM) */ newdoc.line = curdoc.line; newdoc.link = curdoc.link; free_address(&curdoc); /* so it doesn't get pushed */ #ifdef VMS lynx_force_repaint(); #endif /* VMS */ /* * Reload should force a cache refresh on a proxy. -- Ari L. * * * -- but only if this was really a reload requested by the user, not if we * jumped here to handle reloading for INLINE_TOGGLE, IMAGE_TOGGLE, * RAW_TOGGLE, etc. - KW */ if (real_cmd == LYK_RELOAD) reloading = REAL_RELOAD; return; } #ifdef DIRED_SUPPORT static void handle_LYK_REMOVE(BOOLEAN *refresh_screen) { if (lynx_edit_mode && nlinks > 0 && !no_dired_support) { int linkno = curdoc.link; /* may be changed in local_remove - kw */ local_remove(&curdoc); if (LYAutoUncacheDirLists >= 1) do_cleanup_after_delete(); else if (curdoc.link != linkno) *refresh_screen = TRUE; } } #endif /* DIRED_SUPPORT */ static void handle_LYK_RIGHT_LINK(void) { if (curdoc.link < nlinks - 1 && links[curdoc.link].ly == links[curdoc.link + 1].ly) { set_curdoc_link(curdoc.link + 1); } } static void handle_LYK_SHELL(BOOLEAN *refresh_screen, int *old_c, int real_c) { if (!no_shell) { stop_curses(); printf("%s\r\n", SPAWNING_MSG); #if defined(__CYGWIN__) /* handling "exec $SHELL" does not work if $SHELL is null */ if (LYGetEnv("SHELL") == NULL) { Cygwin_Shell(); } else #endif { static char *shell = NULL; if (shell == 0) StrAllocCopy(shell, LYSysShell()); LYSystem(shell); } start_curses(); *refresh_screen = TRUE; /* for an HText_pageDisplay() */ } else { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(SPAWNING_DISABLED); } } } static void handle_LYK_SOFT_DQUOTES(void) { #ifdef USE_SOURCE_CACHE if (!HTcan_reparse_document()) { #endif /* * Check if this is a reply from a POST, and if so, seek confirmation * of reload if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) { HTInfoMsg(WILL_NOT_RELOAD_DOC); } else { HText_setNoCache(HTMainText); move_address(&newdoc, &curdoc); newdoc.line = curdoc.line; newdoc.link = curdoc.link; } #ifdef USE_SOURCE_CACHE } /* end if no bypass */ #endif soft_dquotes = (BOOLEAN) !soft_dquotes; HTUserMsg(soft_dquotes ? SOFT_DOUBLE_QUOTE_ON : SOFT_DOUBLE_QUOTE_OFF); #ifdef USE_SOURCE_CACHE (void) reparse_document(); #endif return; } #define GetAnchorNumber(link) \ ((nlinks > 0 && link >= 0) \ ? links[link].anchor_number \ : -1) #define GetAnchorLineNo(link) \ ((nlinks > 0 && link >= 0) \ ? links[link].anchor_line_num \ : -1) /* * Adjust the top-of-screen line number for the new document if the redisplayed * screen would not show the given link-number. */ #ifdef USE_SOURCE_CACHE static int wrap_reparse_document(void) { int result; int anchor_number = GetAnchorNumber(curdoc.link); int old_line_num = HText_getAbsLineNumber(HTMainText, anchor_number); int old_from_top = old_line_num - LYGetNewline() + 1; /* get the offset for the current anchor */ int old_offset = ((nlinks > 0 && curdoc.link >= 0) ? links[curdoc.link].sgml_offset : -1); CTRACE((tfp, "original anchor %d, topline %d, link %d, offset %d\n", anchor_number, old_line_num, curdoc.link, old_offset)); /* reparse the document (producing a new anchor list) */ result = reparse_document(); /* readjust top-line and link-number */ if (result && old_offset >= 0) { int new_anchor = HText_closestAnchor(HTMainText, old_offset); int new_lineno = HText_getAbsLineNumber(HTMainText, new_anchor); int top_lineno; CTRACE((tfp, "old anchor %d -> new anchor %d\n", anchor_number, new_anchor)); if (new_lineno - old_from_top < 0) old_from_top = new_lineno; /* Newline and newdoc.line are 1-based, * but 0-based lines are simpler to work with. */ top_lineno = HText_getPreferredTopLine(HTMainText, new_lineno - old_from_top) + 1; CTRACE((tfp, "preferred top %d\n", top_lineno)); if (top_lineno != LYGetNewline()) { LYSetNewline(top_lineno); newdoc.link = HText_anchorRelativeTo(HTMainText, top_lineno - 1, new_anchor); curdoc.link = newdoc.link; CTRACE((tfp, "adjusted anchor %d, topline %d, link %d, offset %d\n", new_anchor, top_lineno, curdoc.link, HText_locateAnchor(HTMainText, new_anchor))); } else { newdoc.link = curdoc.link; } } return result; } #endif /* USE_SOURCE_CACHE */ static void handle_LYK_SOURCE(char **ownerS_address_p) { #ifdef USE_SOURCE_CACHE BOOLEAN canreparse_post = FALSE; #endif /* * Check if this is a reply from a POST, and if so, * seek confirmation if the safe element is not set. - FM */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && #ifdef USE_SOURCE_CACHE (!(canreparse_post = HTcan_reparse_document())) && #endif (curdoc.isHEAD ? HTConfirm(CONFIRM_POST_RESUBMISSION) : confirm_post_resub(curdoc.address, curdoc.title, 1, 1)) == FALSE) { HTInfoMsg(CANCELLED); return; } if (HTisDocumentSource()) { srcmode_for_next_retrieval(-1); } else { if (HText_getOwner()) StrAllocCopy(*ownerS_address_p, HText_getOwner()); LYUCPushAssumed(HTMainAnchor); srcmode_for_next_retrieval(1); } #ifdef USE_SOURCE_CACHE if (wrap_reparse_document()) { /* * These normally get cleaned up after getfile() returns; * since we're not calling getfile(), we have to clean them * up ourselves. -dsb */ HTOutputFormat = WWW_PRESENT; #ifdef USE_PRETTYSRC if (psrc_view) HTMark_asSource(); psrc_view = FALSE; #endif FREE(*ownerS_address_p); /* not used with source_cache */ LYUCPopAssumed(); /* probably a right place here */ HTMLSetCharacterHandling(current_char_set); /* restore now */ return; } else if (canreparse_post) { srcmode_for_next_retrieval(0); LYUCPopAssumed(); /* probably a right place here */ return; } #endif if (curdoc.title) StrAllocCopy(newdoc.title, curdoc.title); free_address(&curdoc); /* so it doesn't get pushed */ LYforce_no_cache = TRUE; } static void handle_LYK_SWITCH_DTD(void) { #ifdef USE_SOURCE_CACHE BOOLEAN canreparse = FALSE; if (!(canreparse = HTcan_reparse_document())) { #endif /* * Check if this is a reply from a POST, and if so, * seek confirmation of reload if the safe element * is not set. - FM, kw */ if ((curdoc.post_data != NULL && curdoc.safe != TRUE) && confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) { HTInfoMsg(WILL_NOT_RELOAD_DOC); } else { /* * If currently viewing preparsed source, switching to the other * DTD parsing may show source differences, so stay in source view * - kw */ /* NOTE: this conditional can be considered incorrect - current behaviour - when viewing source and LYPreparsedSource==TRUE, pressing ^V will toggle parser mode AND switch back from the source view to presentation view.-HV */ if (HTisDocumentSource() && LYPreparsedSource) { srcmode_for_next_retrieval(1); } HText_setNoCache(HTMainText); move_address(&newdoc, &curdoc); newdoc.line = curdoc.line; newdoc.link = curdoc.link; } #ifdef USE_SOURCE_CACHE } /* end if no bypass */ #endif Old_DTD = !Old_DTD; HTSwitchDTD(!Old_DTD); HTUserMsg(Old_DTD ? USING_DTD_0 : USING_DTD_1); #ifdef USE_SOURCE_CACHE if (canreparse) { if (HTisDocumentSource() && LYPreparsedSource) { srcmode_for_next_retrieval(1); } if (!reparse_document()) { srcmode_for_next_retrieval(0); } } #endif return; } #ifdef DIRED_SUPPORT static void handle_LYK_TAG_LINK(void) { if (lynx_edit_mode && nlinks > 0 && !no_dired_support) { if (!strcmp(LYGetHiliteStr(curdoc.link, 0), "..")) return; /* Never tag the parent directory */ if (dir_list_style == MIXED_STYLE) { if (!strcmp(LYGetHiliteStr(curdoc.link, 0), "../")) return; } else if (!StrNCmp(LYGetHiliteStr(curdoc.link, 0), "Up to ", 6)) return; { /* * HTList-based management of tag list, see LYLocal.c - KW */ HTList *t1 = tagged; char *tagname = NULL; BOOLEAN found = FALSE; while ((tagname = (char *) HTList_nextObject(t1)) != NULL) { if (!strcmp(links[curdoc.link].lname, tagname)) { found = TRUE; HTList_removeObject(tagged, tagname); FREE(tagname); tagflag(FALSE, curdoc.link); break; } } if (!found) { if (tagged == NULL) tagged = HTList_new(); tagname = NULL; StrAllocCopy(tagname, links[curdoc.link].lname); HTList_addObject(tagged, tagname); tagflag(TRUE, curdoc.link); } } if (curdoc.link < nlinks - 1) { set_curdoc_link(curdoc.link + 1); } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) { set_curdoc_link(0); } else if (more_text) { /* next page */ LYChgNewline(display_lines); } } } #endif /* DIRED_SUPPORT */ static void handle_LYK_TOGGLE_HELP(void) { if (user_mode == NOVICE_MODE) { toggle_novice_line(); noviceline(more_text); } } static void handle_LYK_TOOLBAR(BOOLEAN *try_internal, BOOLEAN *force_load, int *old_c, int real_c) { char *cp; char *toolbar = NULL; if (!HText_hasToolbar(HTMainText)) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_TOOLBAR); } } else if (*old_c != real_c) { *old_c = real_c; cp = trimPoundSelector(curdoc.address); HTSprintf0(&toolbar, "%s#%s", curdoc.address, LYToolbarName); restorePoundSelector(cp); set_address(&newdoc, toolbar); FREE(toolbar); *try_internal = TRUE; *force_load = TRUE; /* force MainLoop to reload */ } } static void handle_LYK_TRACE_LOG(BOOLEAN *trace_flag_ptr) { #ifndef NO_LYNX_TRACE /* * Check whether we've started a TRACE log in this session. - FM */ if (LYTraceLogFP == NULL) { HTUserMsg(NO_TRACELOG_STARTED); return; } /* * Don't do if already viewing the TRACE log. - FM */ if (LYIsUIPage(curdoc.address, UIP_TRACELOG)) return; /* * If TRACE mode is on, turn it off during this fetch of the TRACE log, so * we don't enter stuff about this fetch, and set a flag for turning it * back on when we return to this loop. Note that we'll miss any messages * about memory exhaustion if it should occur. It seems unlikely that * anything else bad might happen, but if it does, we'll miss messages * about that too. We also fflush(), close, and open it again, to make * sure all stderr messages thus far will be in the log. - FM */ if (!LYReopenTracelog(trace_flag_ptr)) return; LYLocalFileToURL(&(newdoc.address), LYTraceLogPath); LYRegisterUIPage(newdoc.address, UIP_TRACELOG); StrAllocCopy(newdoc.title, LYNX_TRACELOG_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; if (LYValidate || check_realm) { LYPermitURL = TRUE; } LYforce_no_cache = TRUE; #else HTUserMsg(TRACE_DISABLED); #endif /* NO_LYNX_TRACE */ } #ifdef DIRED_SUPPORT static void handle_LYK_UPLOAD(void) { /* * Don't do if already viewing upload options page. */ if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) return; if (lynx_edit_mode && !no_dired_support) { LYUpload_options(&(newdoc.address), curdoc.address); StrAllocCopy(newdoc.title, UPLOAD_OPTIONS_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; /* * Uncache the current listing so that it will be updated to included * the uploaded file if placed in the current directory. - FM */ DIRED_UNCACHE_1; } } #endif /* DIRED_SUPPORT */ static void handle_LYK_UP_xxx(int *arrowup, int *old_c, int real_c, int scroll_by) { if (LYGetNewline() > 1) { if (LYGetNewline() - scroll_by < 1) scroll_by = LYGetNewline() - 1; LYChgNewline(-scroll_by); if (nlinks > 0 && curdoc.link > -1) { if (links[curdoc.link].ly + scroll_by <= display_lines) { newdoc.link = curdoc.link + HText_LinksInLines(HTMainText, LYGetNewline(), scroll_by); } else { *arrowup = TRUE; } } } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_BEGIN); } } static void handle_LYK_UP_HALF(int *arrowup, int *old_c, int real_c) { handle_LYK_UP_xxx(arrowup, old_c, real_c, display_lines / 2); } static void handle_LYK_UP_LINK(int *follow_col, int *arrowup, int *old_c, int real_c) { if (curdoc.link > 0 && (links[0].ly != links[curdoc.link].ly || !HText_LinksInLines(HTMainText, 1, LYGetNewline() - 1))) { /* more links before this on screen, and first of them on a different line or no previous links before this screen? */ int newlink; if (*follow_col == -1) { const char *text = LYGetHiliteStr(curdoc.link, 0); *follow_col = links[curdoc.link].lx; if (text != NULL) *follow_col += (int) strlen(text) / 2; } newlink = find_link_near_col(*follow_col, -1); if (newlink > -1) { set_curdoc_link(newlink); } else if (*old_c != real_c) { *old_c = real_c; HTUserMsg(NO_LINKS_ABOVE); } } else if (curdoc.line > 1 && LYGetNewline() > 1) { /* previous page */ int scrollamount = (LYGetNewline() > display_lines ? display_lines : LYGetNewline() - 1); LYChgNewline(-scrollamount); if (scrollamount < display_lines && nlinks > 0 && curdoc.link > -1 && links[0].ly - 1 + scrollamount <= display_lines) { newdoc.link = HText_LinksInLines(HTMainText, 1, scrollamount) - 1; } else { *arrowup = TRUE; } } else if (*old_c != real_c) { *old_c = real_c; HTInfoMsg(ALREADY_AT_BEGIN); } } static void handle_LYK_UP_TWO(int *arrowup, int *old_c, int real_c) { handle_LYK_UP_xxx(arrowup, old_c, real_c, 2); } static void handle_LYK_VIEW_BOOKMARK(BOOLEAN *refresh_screen, int *old_c, int real_c) { const char *cp; if (LYValidate) { if (*old_c != real_c) { *old_c = real_c; HTUserMsg(BOOKMARKS_DISABLED); } return; } /* * See if a bookmark exists. If it does replace newdoc.address with its * name. */ if ((cp = get_bookmark_filename(&newdoc.address)) != NULL) { if (*cp == '\0' || !strcmp(cp, " ") || !strcmp(curdoc.address, newdoc.address)) { if (LYMultiBookmarks != MBM_OFF) *refresh_screen = TRUE; return; } #ifdef KANJI_CODE_OVERRIDE if (HTCJK == JAPANESE) { last_kcode = NOKANJI; /* AUTO */ } #endif LYforce_no_cache = TRUE; /*force the document to be reloaded */ StrAllocCopy(newdoc.title, BOOKMARK_TITLE); StrAllocCopy(newdoc.bookmark, BookmarkPage); LYFreePostData(&newdoc); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; } else { if (*old_c != real_c) { *old_c = real_c; LYMBM_statusline(BOOKMARKS_NOT_OPEN); LYSleepAlert(); if (LYMultiBookmarks != MBM_OFF) { *refresh_screen = TRUE; } } } } static BOOLEAN handle_LYK_VLINKS(int *cmd, BOOLEAN *newdoc_link_is_absolute) { int c; if (LYIsUIPage(curdoc.address, UIP_VLINKS)) { /* * Already viewing visited links page, so get out. */ *cmd = LYK_PREV_DOC; return TRUE; } /* * Print visited links page to file. */ c = LYShowVisitedLinks(&newdoc.address); if (c < 0) { HTUserMsg(VISITED_LINKS_EMPTY); return FALSE; } StrAllocCopy(newdoc.title, VISITED_LINKS_TITLE); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; if (c > 0) { /* Select a correct link. */ *newdoc_link_is_absolute = TRUE; newdoc.link = c - 1; } if (LYValidate || check_realm) { LYPermitURL = TRUE; StrAllocCopy(lynxlinksfile, newdoc.address); } return FALSE; } void handle_LYK_WHEREIS(int cmd, BOOLEAN *refresh_screen) { BOOLEAN have_target_onscreen = (BOOLEAN) (!isBEmpty(prev_target) && HText_pageHasPrevTarget()); BOOL found; int oldcur = curdoc.link; /* temporarily remember */ char *remember_old_target = NULL; if (have_target_onscreen) StrAllocCopy(remember_old_target, prev_target->str); else StrAllocCopy(remember_old_target, ""); if (cmd == LYK_WHEREIS) { /* * Reset prev_target to force prompting for a new search string and to * turn off highlighting if no search string is entered by the user. */ BStrCopy0(prev_target, ""); } found = textsearch(&curdoc, &prev_target, (cmd == LYK_WHEREIS) ? 0 : ((cmd == LYK_NEXT) ? 1 : -1)); /* * Force a redraw to ensure highlighting of hits even when found on the * same page, or clearing of highlighting if the default search string was * erased without replacement. - FM */ /* * Well let's try to avoid it at least in a few cases * where it is not needed. - kw */ if (www_search_result >= 0 && www_search_result != curdoc.line) { *refresh_screen = TRUE; /* doesn't really matter */ } else if (!found) { *refresh_screen = have_target_onscreen; } else if (!have_target_onscreen && found) { *refresh_screen = TRUE; } else if (www_search_result == curdoc.line && curdoc.link == oldcur && curdoc.link >= 0 && nlinks > 0 && links[curdoc.link].ly >= (display_lines / 3)) { *refresh_screen = TRUE; } else if ((LYcase_sensitive && 0 != strcmp(prev_target->str, remember_old_target)) || (!LYcase_sensitive && 0 != strcasecomp8(prev_target->str, remember_old_target))) { *refresh_screen = TRUE; } FREE(remember_old_target); } /* * Get a number from the user and follow that link number. */ static void handle_LYK_digit(int c, BOOLEAN *force_load, int *old_c, int real_c, BOOLEAN *try_internal GCC_UNUSED) { int lindx = ((nlinks > 0) ? curdoc.link : 0); int number; char *temp = NULL; /* pass cur line num for use in follow_link_number() * Note: Current line may not equal links[cur].line */ number = curdoc.line; switch (follow_link_number(c, lindx, &newdoc, &number)) { case DO_LINK_STUFF: /* * Follow a normal link. */ set_address(&newdoc, links[lindx].lname); StrAllocCopy(newdoc.title, LYGetHiliteStr(lindx, 0)); /* * For internal links, retain POST content if present. If we are on * the List Page, prevent pushing it on the history stack. Otherwise * set try_internal to signal that the top of the loop should attempt * to reposition directly, without calling getfile. - kw */ if (track_internal_links) { if (links[lindx].type == WWW_INTERN_LINK_TYPE) { LYinternal_flag = TRUE; newdoc.internal_link = TRUE; if (LYIsListpageTitle(NonNull(curdoc.title)) && (LYIsUIPage(curdoc.address, UIP_LIST_PAGE) || LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) { if (check_history()) { LYinternal_flag = TRUE; } else { HTLastConfirmCancelled(); /* reset flag */ if (!confirm_post_resub(newdoc.address, newdoc.title, ((LYresubmit_posts && HText_POSTReplyLoaded(&newdoc)) ? 1 : 2), 2)) { if (HTLastConfirmCancelled() || (LYresubmit_posts && !HText_POSTReplyLoaded(&newdoc))) { /* cancel the whole thing */ LYforce_no_cache = FALSE; reloading = FALSE; copy_address(&newdoc, &curdoc); StrAllocCopy(newdoc.title, curdoc.title); newdoc.internal_link = curdoc.internal_link; HTInfoMsg(CANCELLED); if (nlinks > 0) HText_pageDisplay(curdoc.line, prev_target->str); break; } else if (LYresubmit_posts) { /* If LYresubmit_posts is set, and the answer was No, and we have a cached copy, then use it. - kw */ LYforce_no_cache = FALSE; } else { /* if No, but not ^C or ^G, drop * the post data. Maybe the link * wasn't meant to be internal after * all, here we can recover from that * assumption. - kw */ LYFreePostData(&newdoc); newdoc.internal_link = FALSE; HTAlert(DISCARDING_POST_DATA); } } } /* * Don't push the List Page if we follow an internal link given * by it. - kw */ free_address(&curdoc); } else *try_internal = TRUE; if (!(LYresubmit_posts && newdoc.post_data)) LYinternal_flag = TRUE; *force_load = TRUE; break; } else { /* * Free POST content if not an internal link. - kw */ LYFreePostData(&newdoc); } } /* * Might be an anchor in the same doc from a POST form. If so, don't * free the content. -- FM */ if (are_different(&curdoc, &newdoc)) { LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; if (isLYNXMESSAGES(newdoc.address)) LYforce_no_cache = TRUE; } newdoc.internal_link = FALSE; *force_load = TRUE; /* force MainLoop to reload */ break; case DO_GOTOLINK_STUFF: /* * Position on a normal link, don't follow it. - KW */ LYSetNewline(newdoc.line); newdoc.line = 1; if (LYGetNewline() == curdoc.line) { /* * It's a link in the current page. - FM */ if (nlinks > 0 && curdoc.link > -1) { if (curdoc.link == newdoc.link) { /* * It's the current link, and presumably reflects a typo in * the statusline entry, so issue a statusline message for * the typo-prone users (like me 8-). - FM */ HTSprintf0(&temp, LINK_ALREADY_CURRENT, number); HTUserMsg(temp); FREE(temp); } else { /* * It's a different link on this page, */ set_curdoc_link(newdoc.link); newdoc.link = 0; } } } break; /* nothing more to do */ case DO_GOTOPAGE_STUFF: /* * Position on a page in this document. - FM */ LYSetNewline(newdoc.line); newdoc.line = 1; if (LYGetNewline() == curdoc.line) { /* * It's the current page, so issue a statusline message for the * typo-prone users (like me 8-). - FM */ if (LYGetNewline() <= 1) { HTInfoMsg(ALREADY_AT_BEGIN); } else if (!more_text) { HTInfoMsg(ALREADY_AT_END); } else { HTSprintf0(&temp, ALREADY_AT_PAGE, number); HTUserMsg(temp); FREE(temp); } } break; case PRINT_ERROR: *old_c = real_c; HTUserMsg(BAD_LINK_NUM_ENTERED); break; } return; } #ifdef SUPPORT_CHDIR /* original implementation by VH */ void handle_LYK_CHDIR(void) { static bstring *buf = NULL; char *p = NULL; if (no_chdir) { HTUserMsg(CHDIR_DISABLED); return; } _statusline(gettext("cd to:")); if (LYgetBString(&buf, FALSE, 0, NORECALL) < 0 || isBEmpty(buf)) { HTInfoMsg(CANCELLED); return; } if (LYIsTilde(buf->str[0]) && (LYIsPathSep(buf->str[1]) || buf->str[1] == '\0')) { HTSprintf0(&p, "%s%s", Home_Dir(), buf->str + 1); } else { StrAllocCopy(p, buf->str); } CTRACE((tfp, "changing directory to '%s'\n", p)); if (chdir(p)) { switch (errno) { case EACCES: HTInfoMsg(COULD_NOT_ACCESS_DIR); break; case ENOENT: HTInfoMsg(gettext("No such directory")); break; case ENOTDIR: HTInfoMsg(gettext("A component of path is not a directory")); break; default: HTInfoMsg(gettext("failed to change directory")); break; } } else { #ifdef DIRED_SUPPORT /*if in dired, load content of other directory */ if (!no_dired_support && (lynx_edit_mode || (LYIsUIPage(curdoc.address, UIP_DIRED_MENU)))) { char buf2[LY_MAXPATH]; char *addr = NULL; Current_Dir(buf2); LYLocalFileToURL(&addr, buf2); newdoc.address = addr; newdoc.isHEAD = FALSE; StrAllocCopy(newdoc.title, gettext("A URL specified by the user")); LYFreePostData(&newdoc); FREE(newdoc.bookmark); newdoc.safe = FALSE; newdoc.internal_link = FALSE; /**force_load = TRUE;*/ if (lynx_edit_mode) { DIRED_UNCACHE_2; } } else #endif HTInfoMsg(OPERATION_DONE); } FREE(p); } static void handle_LYK_PWD(void) { char buffer[LY_MAXPATH]; int save_secs = InfoSecs; BOOLEAN save_wait = no_pause; if (Secs2SECS(save_secs) < 1) InfoSecs = SECS2Secs(1); no_pause = FALSE; HTInfoMsg(Current_Dir(buffer)); InfoSecs = save_secs; no_pause = save_wait; } #endif #ifdef USE_CURSES_PADS /* * Having jumps larger than this is counter-productive. Indeed, it is natural * to expect that when the relevant text appears, one would "overshoot" and * would scroll 3-4 extra full screens. When going back, the "accumulation" * logic would again start moving in full screens, so one would overshoot * again, etc. * * Going back, one can fix it in 28 keypresses. The relevant text will appear * on the screen soon enough for the key-repeat to become not that important, * and we are still moving in smaller steps than when we overshot. Since key * repeat is not important, even if we overshoot again, it is going to be by 30 * steps, which is easy to fix by reversing the direction again. */ static int repeat_to_delta(int n) { int threshold = LYcols / 3; while (threshold > 0) { if (n >= threshold) { n = threshold; break; } threshold = (threshold * 2) / 3; } return n; } static void handle_LYK_SHIFT_LEFT(BOOLEAN *flag, int count) { if (!LYwideLines) { HTAlert(SHIFT_VS_LINEWRAP); return; } if (LYshiftWin > 0) { LYshiftWin -= repeat_to_delta(count); *flag = TRUE; } if (LYshiftWin < 0) LYshiftWin = 0; } static void handle_LYK_SHIFT_RIGHT(BOOLEAN *flag, int count) { if (!LYwideLines) { HTAlert(SHIFT_VS_LINEWRAP); return; } LYshiftWin += repeat_to_delta(count); *flag = TRUE; } static BOOLEAN handle_LYK_LINEWRAP_TOGGLE(int *cmd, BOOLEAN *flag) { static const char *choices[] = { "Try to fit screen width", "No line wrap in columns", "Wrap columns at screen width", "Wrap columns at 3/4 screen width", "Wrap columns at 2/3 screen width", "Wrap columns at 1/2 screen width", "Wrap columns at 1/3 screen width", "Wrap columns at 1/4 screen width", NULL }; static int wrap[] = { 0, 0, 12, /* In units of 1/12 */ 9, 8, 6, 4, 3 }; int c; int code = FALSE; CTRACE((tfp, "Entering handle_LYK_LINEWRAP_TOGGLE\n")); if (LYwin != stdscr) { /* Somehow the mouse is over the number instead of being over the name, so we decrease x. */ c = LYChoosePopup(!LYwideLines, LYlines / 2 - 2, LYcolLimit / 2 - 6, choices, (int) TABLESIZE(choices) - 1, FALSE, TRUE); /* * LYhandlePopupList() wasn't really meant to be used outside of * old-style Options menu processing. One result of mis-using it here * is that we have to deal with side-effects regarding SIGINT signal * handler and the term_options global variable. - kw */ if (!term_options) { CTRACE((tfp, "...setting LYwideLines %d, LYtableCols %d (have %d and %d)\n", c, wrap[c], LYwideLines, LYtableCols)); LYwideLines = c; LYtableCols = wrap[c]; if (LYwideLines == 0) LYshiftWin = 0; *flag = TRUE; HTUserMsg(LYwideLines ? LINEWRAP_OFF : LINEWRAP_ON); code = reparse_or_reload(cmd); } } return (BOOLEAN) code; } #endif #ifdef USE_MAXSCREEN_TOGGLE static BOOLEAN handle_LYK_MAXSCREEN_TOGGLE(int *cmd) { static int flag = 0; CTRACE((tfp, "Entering handle_LYK_MAXSCREEN_TOGGLE\n")); if (flag) { CTRACE((tfp, "Calling recoverWindowSize()\n")); recoverWindowSize(); flag = 0; } else { CTRACE((tfp, "Calling maxmizeWindowSize()\n")); maxmizeWindowSize(); flag = 1; } return reparse_or_reload(cmd); } #endif #ifdef LY_FIND_LEAKS #define CleanupMainLoop() \ BStrFree(prev_target); \ BStrFree(user_input_buffer) #else #define CleanupMainLoop() /* nothing */ #endif /* * Here's where we do all the work. * mainloop is basically just a big switch dependent on the users input. I * have tried to offload most of the work done here to procedures to make it * more modular, but this procedure still does a lot of variable manipulation. * This needs some work to make it neater. - Lou Moutilli * (memoir from the original Lynx - FM) */ int mainloop(void) { #if defined(WIN_EX) /* 1997/10/08 (Wed) 14:52:06 */ char sjis_buff[MAX_LINE]; char temp_buff[sizeof(sjis_buff) * 4]; #endif int c = 0; int real_c = 0; int old_c = 0; int pending_form_c = -1; int cmd = LYK_DO_NOTHING, real_cmd = LYK_DO_NOTHING; int getresult; int arrowup = FALSE, show_help = FALSE; bstring *user_input_buffer = NULL; const char *cshelpfile = NULL; BOOLEAN first_file = TRUE; BOOLEAN popped_doc = FALSE; BOOLEAN refresh_screen = FALSE; BOOLEAN force_load = FALSE; BOOLEAN try_internal = FALSE; BOOLEAN crawl_ok = FALSE; BOOLEAN vi_keys_flag = vi_keys; BOOLEAN emacs_keys_flag = emacs_keys; BOOLEAN trace_mode_flag = FALSE; BOOLEAN forced_HTML_mode = LYforce_HTML_mode; char cfile[128]; FILE *cfp; char *cp; int ch = 0; RecallType recall = NORECALL; int URLTotal = 0; int URLNum; BOOLEAN FirstURLRecall = TRUE; char *temp = NULL; BOOLEAN ForcePush = FALSE; BOOLEAN override_LYresubmit_posts = FALSE; BOOLEAN newdoc_link_is_absolute = FALSE; BOOLEAN curlink_is_editable; BOOLEAN use_last_tfpos; unsigned int len; int i; int follow_col = -1, key_count = 0, last_key = 0; int tmpNewline; DocInfo tmpDocInfo; /* "internal" means "within the same document, with certainty". It includes a * space so it cannot conflict with any (valid) "TYPE" attributes on A * elements. [According to which DTD, anyway??] - kw */ HTInternalLink = HTAtom_for("internal link"); /* init, used as const */ #ifndef WWW_SOURCE WWW_SOURCE = HTAtom_for("www/source"); /* init, used as const */ #endif /* * curdoc.address contains the name of the file that is currently open. * newdoc.address contains the name of the file that will soon be * opened if it exits. * prev_target contains the last search string the user searched for. * newdoc.title contains the link name that the user last chose to get * into the current link (file). */ /* initialize some variables */ newdoc.address = NULL; newdoc.title = NULL; newdoc.post_data = NULL; newdoc.post_content_type = NULL; newdoc.bookmark = NULL; newdoc.internal_link = FALSE; curdoc.address = NULL; curdoc.title = NULL; curdoc.post_data = NULL; curdoc.post_content_type = NULL; curdoc.bookmark = NULL; curdoc.internal_link = FALSE; #ifdef USE_COLOR_STYLE curdoc.style = NULL; newdoc.style = NULL; #endif #ifndef USE_SESSIONS nhist = 0; #endif BStrCopy0(user_input_buffer, ""); BStrCopy0(prev_target, ""); #ifdef LY_FIND_LEAKS atexit(free_mainloop_variables); #endif initialize: set_address(&newdoc, startfile); StrAllocCopy(startrealm, startfile); StrAllocCopy(newdoc.title, gettext("Entry into main screen")); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.line = 1; newdoc.link = 0; #ifdef USE_SLANG if (TRACE && LYCursesON) { LYaddstr("\n"); LYrefresh(); } #endif /* USE_SLANG */ CTRACE((tfp, "Entering mainloop, startfile=%s\n", startfile)); if (form_post_data) { BStrCopy0(newdoc.post_data, form_post_data); StrAllocCopy(newdoc.post_content_type, "application/x-www-form-urlencoded"); } else if (form_get_data) { StrAllocCat(newdoc.address, form_get_data); } if (bookmark_start) { if (LYValidate) { HTAlert(BOOKMARKS_DISABLED); bookmark_start = FALSE; goto initialize; } else if (traversal) { HTAlert(BOOKMARKS_NOT_TRAVERSED); traversal = FALSE; crawl = FALSE; bookmark_start = FALSE; goto initialize; } else { const char *cp1; /* * See if a bookmark page exists. If it does, replace * newdoc.address with its name */ if ((cp1 = get_bookmark_filename(&newdoc.address)) != NULL && *cp1 != '\0' && strcmp(cp1, " ")) { StrAllocCopy(newdoc.title, BOOKMARK_TITLE); StrAllocCopy(newdoc.bookmark, BookmarkPage); StrAllocCopy(startrealm, newdoc.address); LYFreePostData(&newdoc); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; CTRACE((tfp, "Using bookmarks=%s\n", newdoc.address)); } else { HTUserMsg(BOOKMARKS_NOT_OPEN); bookmark_start = FALSE; goto initialize; } } } FREE(form_post_data); FREE(form_get_data); LYSetDisplayLines(); while (TRUE) { #ifdef USE_COLOR_STYLE if (curdoc.style != NULL) force_load = TRUE; #endif /* * If newdoc.address is different from curdoc.address then we need to * go out and find and load newdoc.address. */ if (LYforce_no_cache || force_load || are_different(&curdoc, &newdoc)) { force_load = FALSE; /* done */ if (TRACE && LYCursesON) { LYHideCursor(); /* make sure cursor is down */ #ifdef USE_SLANG LYaddstr("\n"); #endif /* USE_SLANG */ LYrefresh(); } try_again: /* * Push the old file onto the history stack if we have a current * doc and a new address. - FM */ if (curdoc.address && newdoc.address) { /* * Don't actually push if this is a LYNXDOWNLOAD URL, because * that returns NORMAL even if it fails due to a spoof attempt * or file access problem, and we set the newdoc structure * elements to the curdoc structure elements under case NORMAL. * - FM */ if (!isLYNXDOWNLOAD(newdoc.address)) { LYpush(&curdoc, ForcePush); } } else if (!newdoc.address) { /* * If newdoc.address is empty then pop a file and load it. - * FM */ LYhist_prev(&newdoc); popped_doc = TRUE; /* * If curdoc had been reached via an internal * (fragment) link from what we now have just * popped into newdoc, then override non-caching in * all cases. - kw */ if (track_internal_links && curdoc.internal_link && !are_phys_different(&curdoc, &newdoc)) { LYinternal_flag = TRUE; LYoverride_no_cache = TRUE; LYforce_no_cache = FALSE; try_internal = TRUE; } else { /* * Force a no_cache override unless it's a bookmark file, * or it has POST content and LYresubmit_posts is set * without safe also set, and we are not going to another * position in the current document or restoring the * previous document due to a NOT_FOUND or NULLFILE return * value from getfile(). - FM */ if ((newdoc.bookmark != NULL) || (newdoc.post_data != NULL && !newdoc.safe && LYresubmit_posts && !override_LYresubmit_posts && NO_INTERNAL_OR_DIFFERENT(&curdoc, &newdoc))) { LYoverride_no_cache = FALSE; } else { LYoverride_no_cache = TRUE; } } } override_LYresubmit_posts = FALSE; if (HEAD_request) { /* * Make SURE this is an appropriate request. - FM */ if (newdoc.address) { if (LYCanDoHEAD(newdoc.address) == TRUE) { newdoc.isHEAD = TRUE; } else if (isLYNXIMGMAP(newdoc.address)) { if (LYCanDoHEAD(newdoc.address + LEN_LYNXIMGMAP) == TRUE) { StrAllocCopy(temp, newdoc.address + LEN_LYNXIMGMAP); free_address(&newdoc); newdoc.address = temp; newdoc.isHEAD = TRUE; temp = NULL; } } } try_internal = FALSE; HEAD_request = FALSE; } /* * If we're getting the TRACE log and it's not new, check whether * its HText structure has been dumped, and if so, fflush() and * fclose() it to ensure it's fully updated, and then fopen() it * again. - FM */ if (LYUseTraceLog == TRUE && trace_mode_flag == FALSE && LYTraceLogFP != NULL && LYIsUIPage(newdoc.address, UIP_TRACELOG)) { DocAddress WWWDoc; HTParentAnchor *tmpanchor; WWWDoc.address = newdoc.address; WWWDoc.post_data = newdoc.post_data; WWWDoc.post_content_type = newdoc.post_content_type; WWWDoc.bookmark = newdoc.bookmark; WWWDoc.isHEAD = newdoc.isHEAD; WWWDoc.safe = newdoc.safe; tmpanchor = HTAnchor_findAddress(&WWWDoc); if ((HText *) HTAnchor_document(tmpanchor) == NULL) { if (!LYReopenTracelog(&trace_mode_flag)) { old_c = 0; cmd = LYK_PREV_DOC; goto new_cmd; } } } LYRequestTitle = newdoc.title; if (newdoc.bookmark) LYforce_HTML_mode = TRUE; if (LYValidate && startfile_ok && newdoc.address && startfile && homepage && (!strcmp(newdoc.address, startfile) || !strcmp(newdoc.address, homepage))) { LYPermitURL = TRUE; } /* reset these two variables here before getfile() * so they will be available in partial mode * (was previously implemented in case NORMAL). */ BStrCopy0(prev_target, ""); /* Reset for new coming document */ LYSetNewline(newdoc.line); /* set for LYGetNewline() */ #ifdef USE_PRETTYSRC psrc_first_tag = TRUE; #endif #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION textfields_need_activation = textfields_activation_option; #endif FREE(LYRequestReferer); /* * Don't send Referer if we have to load a document again that we * got from the history stack. We don't know any more how we * originally got to that page. Using a Referer based on the * current HTMainText could only be right by coincidence. - kw * 1999-11-01 */ if (popped_doc) LYNoRefererForThis = TRUE; if (track_internal_links) { if (try_internal) { if (newdoc.address && isLYNXIMGMAP(newdoc.address)) { try_internal = FALSE; } else if (curdoc.address && isLYNXIMGMAP(curdoc.address)) { try_internal = FALSE; } } if (try_internal) { char *hashp = findPoundSelector(newdoc.address); if (hashp) { HTFindPoundSelector(hashp + 1); } getresult = (HTMainText != NULL) ? NORMAL : NOT_FOUND; try_internal = FALSE; /* done */ /* fix up newdoc.address which may have been fragment-only */ if (getresult == NORMAL && (!hashp || hashp == newdoc.address)) { if (!hashp) { set_address(&newdoc, HTLoadedDocumentURL()); } else { StrAllocCopy(temp, HTLoadedDocumentURL()); StrAllocCat(temp, hashp); /* append fragment */ set_address(&newdoc, temp); FREE(temp); } } } else { if (newdoc.internal_link && newdoc.address && *newdoc.address == '#' && nhist > 0) { char *cp0; if (isLYNXIMGMAP(HDOC(nhist_1).address)) cp0 = HDOC(nhist_1).address + LEN_LYNXIMGMAP; else cp0 = HDOC(nhist_1).address; StrAllocCopy(temp, cp0); (void) trimPoundSelector(temp); StrAllocCat(temp, newdoc.address); free_address(&newdoc); newdoc.address = temp; temp = NULL; } tmpDocInfo = newdoc; tmpNewline = -1; fill_JUMP_Params(&newdoc.address); getresult = getfile(&newdoc, &tmpNewline); if (!reloading && !popped_doc && (tmpNewline >= 0)) { LYSetNewline(tmpNewline); } else { newdoc.link = tmpDocInfo.link; } } } else { tmpDocInfo = newdoc; tmpNewline = -1; fill_JUMP_Params(&newdoc.address); getresult = getfile(&newdoc, &tmpNewline); if (!reloading && !popped_doc && (tmpNewline >= 0)) { LYSetNewline(tmpNewline); } else { newdoc.link = tmpDocInfo.link; } } #ifdef INACTIVE_INPUT_STYLE_VH textinput_redrawn = FALSE; /* for sure */ #endif switch (getresult) { case NOT_FOUND: /* * OK! can't find the file, so it must not be around now. Do * any error logging, if appropriate. */ LYoverride_no_cache = FALSE; /* Was TRUE if popped. - FM */ LYinternal_flag = FALSE; /* Reset to default. - kw */ turn_trace_back_on(&trace_mode_flag); if (!first_file && !LYCancelledFetch) { /* * Do error mail sending and/or traversal stuff. Note that * the links[] elements may not be valid at this point, if * we did call HTuncache_current_document! This should not * have happened for traversal, but for sending error mail * check that HTMainText exists for this reason. - kw */ if (error_logging && nhist > 0 && !popped_doc && !LYUserSpecifiedURL && HTMainText && nlinks > 0 && curdoc.link < nlinks && !isLYNXHIST(NonNull(newdoc.address)) && !isLYNXCACHE(NonNull(newdoc.address)) && !isLYNXCOOKIE(NonNull(newdoc.address))) { char *mail_owner = NULL; if (owner_address && isMAILTO_URL(owner_address)) { mail_owner = owner_address + LEN_MAILTO_URL; } /* * Email a bad link message to the owner of the * document, or to ALERTMAIL if defined, but NOT to * lynx-dev (it is rejected in mailmsg). - FM, kw */ #ifndef ALERTMAIL if (mail_owner) #endif mailmsg(curdoc.link, mail_owner, HDOC(nhist_1).address, HDOC(nhist_1).title); } if (traversal) { FILE *ofp; if ((ofp = LYAppendToTxtFile(TRAVERSE_ERRORS)) == NULL) { if ((ofp = LYNewTxtFile(TRAVERSE_ERRORS)) == NULL) { perror(NOOPEN_TRAV_ERR_FILE); exit_immediately(EXIT_FAILURE); } } if (nhist > 0) { fprintf(ofp, "%s %s\tin %s\n", popped_doc ? newdoc.address : links[curdoc.link].lname, links[curdoc.link].target, HDOC(nhist_1).address); } else { fprintf(ofp, "%s %s\t\n", popped_doc ? newdoc.address : links[curdoc.link].lname, links[curdoc.link].target); } LYCloseOutput(ofp); } } /* * Fall through to do the NULL stuff and reload the old file, * unless the first file wasn't found or has gone missing. */ if (!nhist) { /* * If nhist = 0 then it must be the first file. */ CleanupMainLoop(); exit_immediately_with_error_message(NOT_FOUND, first_file); return (EXIT_FAILURE); } /* FALLTHRU */ case NULLFILE: /* * Not supposed to return any file. */ LYoverride_no_cache = FALSE; /* Was TRUE if popped. - FM */ popped_doc = FALSE; /* Was TRUE if popped. - FM */ LYinternal_flag = FALSE; /* Reset to default. - kw */ turn_trace_back_on(&trace_mode_flag); free_address(&newdoc); /* to pop last doc */ FREE(newdoc.bookmark); LYJumpFileURL = FALSE; reloading = FALSE; LYPermitURL = FALSE; LYCancelledFetch = FALSE; ForcePush = FALSE; LYforce_HTML_mode = FALSE; force_old_UCLYhndl_on_reload = FALSE; if (traversal) { crawl_ok = FALSE; if (traversal_link_to_add) { /* * It's a binary file, or the fetch attempt failed. * Add it to TRAVERSE_REJECT_FILE so we don't try again * in this run. */ if (!lookup_reject(traversal_link_to_add)) { add_to_reject_list(traversal_link_to_add); } FREE(traversal_link_to_add); } } /* * Make sure the first file was found and has not gone missing. */ if (!nhist) { /* * If nhist = 0 then it must be the first file. */ if (first_file && homepage && !LYSameFilename(homepage, startfile)) { /* * Couldn't return to the first file but there is a * homepage we can use instead. Useful for when the * first URL causes a program to be invoked. - GL * * But first make sure homepage is different from * startfile (above), then make it the same (below) so * we don't enter an infinite getfile() loop on on * failures to find the files. - FM */ set_address(&newdoc, homepage); LYFreePostData(&newdoc); FREE(newdoc.bookmark); StrAllocCopy(startfile, homepage); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; newdoc.internal_link = FALSE; goto try_again; } else { CleanupMainLoop(); exit_immediately_with_error_message(NULLFILE, first_file); return (EXIT_FAILURE); } } /* * If we're going to pop from history because getfile didn't * succeed, reset LYforce_no_cache first. This would have been * done in HTAccess.c if the request got that far, but the URL * may have been handled or rejected in getfile without taking * care of that. - kw */ LYforce_no_cache = FALSE; /* * Retrieval of a newdoc just failed, and just going to * try_again would pop the next doc from history and try to get * it without further questions. This may not be the right * thing to do if we have POST data, so fake a PREV_DOC key if * it seems that some prompting should be done. This doesn't * affect the traversal logic, since with traversal POST data * can never occur. - kw */ if (HDOC(nhist - 1).post_data && !HDOC(nhist - 1).safe) { if (HText_POSTReplyLoaded((DocInfo *) &history[(nhist_1)])) { override_LYresubmit_posts = TRUE; goto try_again; } /* Set newdoc fields, just in case the PREV_DOC gets * cancelled. - kw */ if (!curdoc.address) { set_address(&newdoc, HTLoadedDocumentURL()); StrAllocCopy(newdoc.title, HTLoadedDocumentTitle()); if (HTMainAnchor && HTMainAnchor->post_data) { BStrCopy(newdoc.post_data, HTMainAnchor->post_data); StrAllocCopy(newdoc.post_content_type, HTMainAnchor->post_content_type); } else { BStrFree(newdoc.post_data); } newdoc.isHEAD = HTLoadedDocumentIsHEAD(); newdoc.safe = HTLoadedDocumentIsSafe(); newdoc.internal_link = FALSE; } else { copy_address(&newdoc, &curdoc); StrAllocCopy(newdoc.title, curdoc.title); BStrCopy(newdoc.post_data, curdoc.post_data); StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type); newdoc.isHEAD = curdoc.isHEAD; newdoc.safe = curdoc.safe; newdoc.internal_link = curdoc.internal_link; newdoc.line = curdoc.line; newdoc.link = curdoc.link; } cmd = LYK_PREV_DOC; goto new_cmd; } override_LYresubmit_posts = TRUE; goto try_again; case NORMAL: /* * Marvelously, we got the document! */ LYoverride_no_cache = FALSE; /* Was TRUE if popped. - FM */ LYinternal_flag = FALSE; /* Reset to default. - kw */ turn_trace_back_on(&trace_mode_flag); /* * If it's the first file and we're interactive, check whether * it's a bookmark file which was not accessed via the -book * switch. - FM */ if (((first_file == TRUE) && (dump_output_immediately == FALSE) && isEmpty(newdoc.bookmark)) && ((LYisLocalFile(newdoc.address) == TRUE) && !(strcmp(NonNull(HText_getTitle()), BOOKMARK_TITLE))) && (temp = HTParse(newdoc.address, "", PARSE_PATH + PARSE_PUNCTUATION)) != NULL) { const char *name = wwwName(Home_Dir()); len = (unsigned) strlen(name); #ifdef VMS if (!strncasecomp(temp, name, len) && strlen(temp) > len) #else if (!StrNCmp(temp, name, len) && strlen(temp) > len) #endif /* VMS */ { /* * We're interactive and this might be a bookmark file * entered as a startfile rather than invoked via * -book. Check if it's in our bookmark file list, and * if so, reload if with the relevant bookmark elements * set. - FM */ cp = NULL; if (temp[len] == '/') { if (StrChr(&temp[(len + 1)], '/')) { HTSprintf0(&cp, ".%s", &temp[len]); } else { StrAllocCopy(cp, &temp[(len + 1)]); } } else { StrAllocCopy(cp, &temp[len]); } for (i = 0; i <= MBM_V_MAXFILES; i++) { if (MBM_A_subbookmark[i] && LYSameFilename(cp, MBM_A_subbookmark[i])) { StrAllocCopy(BookmarkPage, MBM_A_subbookmark[i]); break; } } FREE(cp); if (i <= MBM_V_MAXFILES) { FREE(temp); if (LYValidate) { HTAlert(BOOKMARKS_DISABLED); CleanupMainLoop(); return (EXIT_FAILURE); } if ((temp = HTParse(newdoc.address, "", PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION))) { set_address(&newdoc, temp); HTuncache_current_document(); free_address(&curdoc); StrAllocCat(newdoc.address, wwwName(Home_Dir())); StrAllocCat(newdoc.address, "/"); StrAllocCat(newdoc.address, (StrNCmp(BookmarkPage, "./", 2) ? BookmarkPage : (BookmarkPage + 2))); StrAllocCopy(newdoc.title, BOOKMARK_TITLE); StrAllocCopy(newdoc.bookmark, BookmarkPage); #ifdef USE_COLOR_STYLE if (curdoc.style) StrAllocCopy(newdoc.style, curdoc.style); #endif StrAllocCopy(startrealm, newdoc.address); LYFreePostData(&newdoc); newdoc.isHEAD = FALSE; newdoc.safe = FALSE; FREE(temp); if (!strcmp(homepage, startfile)) StrAllocCopy(homepage, newdoc.address); StrAllocCopy(startfile, newdoc.address); CTRACE((tfp, "Reloading as bookmarks=%s\n", newdoc.address)); goto try_again; } } } cp = NULL; } FREE(temp); if (traversal) { /* * During traversal build up lists of all links traversed. * Traversal mode is a special feature for traversing http * links in the web. */ if (traversal_link_to_add) { /* * Add the address we sought to TRAVERSE_FILE. */ if (!lookup_link(traversal_link_to_add)) add_to_table(traversal_link_to_add); FREE(traversal_link_to_add); } if (curdoc.address && curdoc.title && !isLYNXIMGMAP(curdoc.address)) /* * Add the address we got to TRAVERSE_FOUND_FILE. */ add_to_traverse_list(curdoc.address, curdoc.title); } /* * If this was a LYNXDOWNLOAD, we still have curdoc, not a * newdoc, so reset the address, title and positioning * elements. - FM */ if (newdoc.address && curdoc.address && isLYNXDOWNLOAD(newdoc.address)) { copy_address(&newdoc, &curdoc); StrAllocCopy(newdoc.title, NonNull(curdoc.title)); StrAllocCopy(newdoc.bookmark, curdoc.bookmark); newdoc.line = curdoc.line; newdoc.link = curdoc.link; newdoc.internal_link = FALSE; /* can't be true. - kw */ } /* * Set Newline to the saved line. It contains the line the * user was on if s/he has been in the file before, or it is 1 * if this is a new file. * * We already set Newline before getfile() and probably update * it explicitly if popping from the history stack via LYpop() * or LYpop_num() within getfile() cycle. * * In partial mode, Newline was probably updated in * LYMainLoop_pageDisplay() if user scrolled the document while * loading. Incremental loading stage already closed in * HT*Copy(). */ #ifdef DISP_PARTIAL /* Newline = newdoc.line; */ display_partial = FALSE; /* for sure, LYNXfoo:/ may be a problem */ #else /* Should not be needed either if we remove "DISP_PARTIAL" from * LYHistory.c, but lets leave it as an important comment for * now. */ /* Newline = newdoc.line; */ #endif /* * If we are going to a target line or the first page of a * popped document, override any www_search line result. */ if (LYGetNewline() > 1 || popped_doc == TRUE) www_search_result = -1; /* * Make sure curdoc.line will not be equal to Newline, so we * get a redraw. */ curdoc.line = -1; break; } /* end switch */ if (TRACE) { if (!LYTraceLogFP || trace_mode_flag) { LYSleepAlert(); /* allow me to look at the results */ } } /* * Set the files the same. */ copy_address(&curdoc, &newdoc); BStrCopy(curdoc.post_data, newdoc.post_data); StrAllocCopy(curdoc.post_content_type, newdoc.post_content_type); StrAllocCopy(curdoc.bookmark, newdoc.bookmark); #ifdef USE_COLOR_STYLE StrAllocCopy(curdoc.style, HText_getStyle()); if (curdoc.style != NULL) style_readFromFile(curdoc.style); #endif curdoc.isHEAD = newdoc.isHEAD; curdoc.internal_link = newdoc.internal_link; /* * Set the remaining document elements and add to the visited links * list. - FM */ if (ownerS_address != NULL) { #ifndef USE_PRETTYSRC if (HTOutputFormat == WWW_SOURCE && !HText_getOwner()) #else if ((LYpsrc ? psrc_view : HTOutputFormat == WWW_SOURCE) && !HText_getOwner()) #endif HText_setMainTextOwner(ownerS_address); FREE(ownerS_address); } if (HText_getTitle()) { StrAllocCopy(curdoc.title, HText_getTitle()); } else if (!dump_output_immediately) { StrAllocCopy(curdoc.title, newdoc.title); } StrAllocCopy(owner_address, HText_getOwner()); curdoc.safe = HTLoadedDocumentIsSafe(); if (!dump_output_immediately) { LYAddVisitedLink(&curdoc); } /* * Reset WWW present mode so that if we were getting the source, we * get rendered HTML from now on. */ HTOutputFormat = WWW_PRESENT; #ifdef USE_PRETTYSRC psrc_view = FALSE; #endif HTMLSetCharacterHandling(current_char_set); /* restore, for sure? */ /* * Reset all of the other relevant flags. - FM */ LYUserSpecifiedURL = FALSE; /* only set for goto's and jumps's */ LYJumpFileURL = FALSE; /* only set for jump's */ LYNoRefererForThis = FALSE; /* always reset on return here */ reloading = FALSE; /* set for RELOAD and NOCACHE keys */ HEAD_request = FALSE; /* only set for HEAD requests */ LYPermitURL = FALSE; /* only for LYValidate or check_realm */ ForcePush = FALSE; /* only set for some PRINT requests. */ LYforce_HTML_mode = FALSE; force_old_UCLYhndl_on_reload = FALSE; popped_doc = FALSE; pending_form_c = -1; } /* end if (LYforce_no_cache || force_load || are_different(...)) */ if (dump_output_immediately) { if (crawl) { print_crawl_to_fd(stdout, curdoc.address, curdoc.title); } else if (!dump_links_only) { print_wwwfile_to_fd(stdout, FALSE, FALSE); } CleanupMainLoop(); return ((dump_server_status >= 400) ? EXIT_FAILURE : EXIT_SUCCESS); } /* * If the recent_sizechange variable is set to TRUE then the window * size changed recently. */ if (recent_sizechange) { /* * First we need to make sure the display library - curses, slang, * whatever - gets notified about the change, and gets a chance to * update external structures appropriately. Hopefully the * stop_curses()/start_curses() sequence achieves this, at least if * the display library has a way to get the new screen size from * the OS. * * However, at least for ncurses, the update of the internal * structures will come still too late - the changed screen size is * detected in doupdate(), which would only be called (indirectly * through the HText_pageDisplay below) after the WINDOW structures * are already filled based on the old size. So we notify the * ncurses library directly here. - kw */ #if defined(NCURSES) && defined(HAVE_RESIZETERM) && defined(HAVE_WRESIZE) resizeterm(LYlines, LYcols); wresize(LYwin, LYlines, LYcols); #else #if 0 /* defined(PDCURSES) && defined(HAVE_XCURSES) */ resize_term(LYlines, LYcols); if (LYwin != 0) LYwin = resize_window(LYwin, LYlines, LYcols); refresh(); #else stop_curses(); start_curses(); LYclear(); #endif #endif refresh_screen = TRUE; /* to force a redraw */ if (HTMainText) /* to REALLY force it... - kw */ HText_setStale(HTMainText); recent_sizechange = FALSE; LYSetDisplayLines(); } if (www_search_result != -1) { /* * This was a WWW search, set the line to the result of the search. */ LYSetNewline(www_search_result); www_search_result = -1; /* reset */ } if (first_file == TRUE) { /* * We can never again have the first file. */ first_file = FALSE; /* * Set the startrealm, and deal as best we can with preserving * forced HTML mode for a local startfile. - FM */ temp = HTParse(curdoc.address, "", PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION); if (isEmpty(temp)) { StrAllocCopy(startrealm, NO_NOTHING); } else { StrAllocCopy(startrealm, temp); FREE(temp); if (!(temp = HTParse(curdoc.address, "", PARSE_PATH + PARSE_PUNCTUATION))) { LYAddHtmlSep(&startrealm); } else { if (forced_HTML_mode && !dump_output_immediately && !curdoc.bookmark && isFILE_URL(curdoc.address) && strlen(temp) > 1) { /* * We forced HTML for a local startfile which is not a * bookmark file and has a path of at least two * letters. If it doesn't have a suffix mapped to * text/html, we'll set the entire path (including the * lead slash) as a "suffix" mapped to text/html to * ensure it is always treated as an HTML source file. * We are counting on a tail match to this full path * for some other URL fetched during the session having * too low a probability to worry about, but it could * happen. - FM */ HTAtom *encoding; if (HTFileFormat(temp, &encoding, NULL) != WWW_HTML) { HTSetSuffix(temp, "text/html", "8bit", 1.0); } } if ((cp = strrchr(temp, '/')) != NULL) { *(cp + 1) = '\0'; StrAllocCat(startrealm, temp); } } } FREE(temp); CTRACE((tfp, "Starting realm is '%s'\n\n", startrealm)); if (traversal) { /* * Set up the crawl output stuff. */ if (curdoc.address && !lookup_link(curdoc.address)) { if (!isLYNXIMGMAP(curdoc.address)) crawl_ok = TRUE; add_to_table(curdoc.address); } /* * Set up the traversal_host comparison string. */ if (StrNCmp((curdoc.address ? curdoc.address : "NULL"), "http", 4)) { StrAllocCopy(traversal_host, NO_NOTHING); } else if (check_realm) { StrAllocCopy(traversal_host, startrealm); } else { temp = HTParse(curdoc.address, "", PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION); if (isEmpty(temp)) { StrAllocCopy(traversal_host, NO_NOTHING); } else { StrAllocCopy(traversal_host, temp); LYAddHtmlSep(&traversal_host); } FREE(temp); } CTRACE((tfp, "Traversal host is '%s'\n\n", traversal_host)); } if (startfile) { /* * If homepage was not equated to startfile, make the homepage * URL the first goto entry. - FM */ if (homepage && strcmp(startfile, homepage)) HTAddGotoURL(homepage); /* * If we are not starting up with startfile (e.g., had -book), * or if we are using the startfile and it has no POST content, * make the startfile URL a goto entry. - FM */ if (strcmp(startfile, newdoc.address) || newdoc.post_data == NULL) HTAddGotoURL(startfile); } if (TRACE) { refresh_screen = TRUE; if (!LYTraceLogFP || trace_mode_flag) { LYSleepAlert(); } } } #ifdef USE_SOURCE_CACHE /* * If the parse settings have changed since this HText was * generated, we need to reparse and redraw it. -dsb * * Should be configured to avoid shock for experienced lynx users. * Currently enabled for cached sources only. */ if (HTdocument_settings_changed()) { if (HTcan_reparse_document()) { HTInfoMsg(gettext("Reparsing document under current settings...")); reparse_document(); } else { /* * Urk. I have no idea how to recover from a failure here. * At a guess, I'll try reloading. -dsb */ /* currently disabled *** HTUserMsg(gettext("Reparsing document under current settings...")); cmd = LYK_RELOAD; goto new_cmd; */ } } if (from_source_cache) { from_source_cache = FALSE; /* reset */ curdoc.line = -1; /* so curdoc.line != Newline, see below */ } #endif /* * If the curdoc.line is different than Newline then there must have * been a change since last update. Run HText_pageDisplay() to create * a fresh screen of text output. * * If we got new HTMainText go this way. All display_partial calls * ends here for final redraw. */ if (curdoc.line != LYGetNewline()) { #ifdef INACTIVE_INPUT_STYLE_VH textinput_redrawn = FALSE; #endif refresh_screen = FALSE; HText_pageDisplay(LYGetNewline(), prev_target->str); #ifdef DIRED_SUPPORT if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged)) showtags(tagged); #endif /* DIRED_SUPPORT */ /* * Check if there is more info below this page. */ more_text = HText_canScrollDown(); if (newdoc.link < 0) goto_line(LYGetNewline()); LYSetNewline(HText_getTopOfScreen() + 1); curdoc.line = LYGetNewline(); if (curdoc.title == NULL) { /* * If we don't yet have a title, try to get it, or set to that * for newdoc.title. - FM */ if (HText_getTitle()) { StrAllocCopy(curdoc.title, HText_getTitle()); } else { StrAllocCopy(curdoc.title, newdoc.title); } } /* * If the request is to highlight a link which is counted from the * start of document, correct the link number: */ if (newdoc_link_is_absolute) { newdoc_link_is_absolute = FALSE; if (curdoc.line > 1) newdoc.link -= HText_LinksInLines(HTMainText, 1, curdoc.line - 1); } if (arrowup) { /* * arrowup is set if we just came up from a page below. */ curdoc.link = nlinks - 1; arrowup = FALSE; } else { curdoc.link = newdoc.link; if (curdoc.link >= nlinks) { curdoc.link = nlinks - 1; } else if (curdoc.link < 0 && nlinks > 0) { /* * We may have popped a doc (possibly in local_dired) which * didn't have any links when it was pushed, but does have * links now (e.g., a file was created). Code below * assumes that curdoc.link is valid and that * (curdoc.link==-1) only occurs if (nlinks==0) is true. - * KW */ curdoc.link = 0; } } show_help = FALSE; /* reset */ newdoc.line = 1; newdoc.link = 0; curdoc.line = LYGetNewline(); /* set */ } else if (newdoc.link < 0) { newdoc.link = 0; /* ...just in case getfile set this */ } /* * Refresh the screen if necessary. */ if (refresh_screen) { #if defined(FANCY_CURSES) || defined (USE_SLANG) if (enable_scrollback) { LYclear(); } else { LYerase(); } #else LYclear(); #endif /* FANCY_CURSES || USE_SLANG */ HText_pageDisplay(LYGetNewline(), prev_target->str); #ifdef DIRED_SUPPORT if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged)) showtags(tagged); #endif /* DIRED_SUPPORT */ /* * Check if there is more info below this page. */ more_text = HText_canScrollDown(); /* * Adjust curdoc.link as above; nlinks may have changed, if the * refresh_screen flag was set as a result of a size change. Code * below assumes that curdoc.link is valid and that * (curdoc.link==-1) only occurs if (nlinks==0) is true. - kw */ if (curdoc.link >= nlinks) { curdoc.link = nlinks - 1; } else if (curdoc.link < 0 && nlinks > 0) { curdoc.link = 0; } if (user_mode == NOVICE_MODE) noviceline(more_text); /* print help message */ refresh_screen = FALSE; } curlink_is_editable = (BOOLEAN) (nlinks > 0 && LinkIsTextLike(curdoc.link)); use_last_tfpos = (BOOLEAN) (curlink_is_editable && (real_cmd == LYK_LPOS_PREV_LINK || real_cmd == LYK_LPOS_NEXT_LINK)); #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION if (!textfields_need_activation) textinput_activated = TRUE; #endif #if defined(WIN_EX) /* 1997/10/08 (Wed) 14:52:06 */ if (nlinks > 0) { char *p = "LYNX (unknown link type)"; /* Show the URL & kanji code . */ if (strlen(links[curdoc.link].lname) == 0) { if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) { switch (links[curdoc.link].l_form->type) { case F_TEXT_SUBMIT_TYPE: case F_SUBMIT_TYPE: case F_IMAGE_SUBMIT_TYPE: p = "[SUBMIT]"; break; case F_PASSWORD_TYPE: p = "Password"; break; case F_OPTION_LIST_TYPE: p = "Option list"; break; case F_CHECKBOX_TYPE: p = "Check box"; break; case F_RADIO_TYPE: p = "[Radio]"; break; case F_RESET_TYPE: p = "[Reset]"; break; case F_TEXT_TYPE: p = "Text input"; break; case F_TEXTAREA_TYPE: p = "Text input lines"; break; default: break; } set_ws_title(p); } } else { if (user_mode == ADVANCED_MODE) { p = curdoc.title; } else { p = links[curdoc.link].lname; } if (strlen(p) < (sizeof(sjis_buff) / 10)) { strcpy(temp_buff, p); if (StrChr(temp_buff, '%')) { HTUnEscape(temp_buff); } str_sjis(sjis_buff, temp_buff); set_ws_title(LYElideString(sjis_buff, 10)); } } } else { if (strlen(curdoc.address) < sizeof(temp_buff) - 1) { if (user_mode == ADVANCED_MODE) { str_sjis(temp_buff, curdoc.title); } else { strcpy(temp_buff, curdoc.address); } set_ws_title(HTUnEscape(temp_buff)); } } #endif /* WIN_EX */ /* * Report unread or new mail, if appropriate. */ if (check_mail && !no_mail) LYCheckMail(); /* * If help is not on the screen, then put a message on the screen to * tell the user other misc info. */ if (!show_help) { show_main_statusline(links[curdoc.link], ((curlink_is_editable && textinput_activated) ? FOR_INPUT : FOR_PANEL)); } else { show_help = FALSE; } if (nlinks > 0) { /* * Highlight current link, unless it is an active text input field. */ if (!curlink_is_editable) { LYhighlight(TRUE, curdoc.link, prev_target->str); #ifndef INACTIVE_INPUT_STYLE_VH } else if (!textinput_activated) { LYhighlight(TRUE, curdoc.link, prev_target->str); #endif } } if (traversal) { /* * Don't go interactively into forms, or accept keystrokes from the * user */ if (crawl && crawl_ok) { crawl_ok = FALSE; #ifdef FNAMES_8_3 sprintf(cfile, "lnk%05d.dat", crawl_count); #else sprintf(cfile, "lnk%08d.dat", crawl_count); #endif /* FNAMES_8_3 */ crawl_count = crawl_count + 1; if ((cfp = LYNewTxtFile(cfile)) != NULL) { print_crawl_to_fd(cfp, curdoc.address, curdoc.title); LYCloseOutput(cfp); } else { #ifdef UNIX FILE *fp = (dump_output_immediately ? stderr : stdout); #else FILE *fp = stdout; #endif if (!dump_output_immediately) cleanup(); fprintf(fp, gettext("Fatal error - could not open output file %s\n"), cfile); CleanupMainLoop(); if (!dump_output_immediately) { exit_immediately(EXIT_FAILURE); } return (EXIT_FAILURE); } } } else { /* * Normal, non-traversal handling. */ if (curlink_is_editable && (textinput_activated || pending_form_c != -1)) { if (pending_form_c != -1) { real_c = pending_form_c; pending_form_c = -1; } else { /* * Replace novice lines if in NOVICE_MODE. */ if (user_mode == NOVICE_MODE) { form_noviceline(FormIsReadonly(links[curdoc.link].l_form)); } real_c = change_form_link(curdoc.link, &newdoc, &refresh_screen, use_last_tfpos, FALSE); } #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION if (textfields_need_activation) textinput_activated = FALSE; #ifdef INACTIVE_INPUT_STYLE_VH textinput_redrawn = FALSE; #endif #endif c = (real_c == LKC_DONE) ? DO_NOTHING : LKC_TO_C(real_c); if (c != DO_NOTHING && peek_mouse_link() != -1 && peek_mouse_link() != -2) old_c = 0; if (peek_mouse_link() >= 0 && LKC_TO_LAC(keymap, real_c) != LYK_CHANGE_LINK) { do_change_link(); if ((c == '\n' || c == '\r') && LinkIsTextLike(curdoc.link) && !textfields_need_activation) { c = DO_NOTHING; } #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION } else if (LinkIsTextarea(curdoc.link) && textfields_need_activation && !FormIsReadonly(links[curdoc.link].l_form) && peek_mouse_link() < 0 && (((LKC_TO_LAC(keymap, real_c) == LYK_NEXT_LINK || #ifdef TEXTAREA_AUTOGROW LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE || #endif LKC_TO_LAC(keymap, real_c) == LYK_LPOS_NEXT_LINK || LKC_TO_LAC(keymap, real_c) == LYK_DOWN_LINK) && ((curdoc.link < nlinks - 1 && LinkIsTextarea(curdoc.link + 1) && (links[curdoc.link].l_form->number == links[curdoc.link + 1].l_form->number) && strcmp(links[curdoc.link].l_form->name, links[curdoc.link + 1].l_form->name) == 0) || (curdoc.link == nlinks - 1 && more_text && HText_TAHasMoreLines(curdoc.link, 1)))) || ((LKC_TO_LAC(keymap, real_c) == LYK_PREV_LINK || LKC_TO_LAC(keymap, real_c) == LYK_LPOS_PREV_LINK || LKC_TO_LAC(keymap, real_c) == LYK_UP_LINK) && ((curdoc.link > 0 && LinkIsTextarea(curdoc.link - 1) && (links[curdoc.link].l_form->number == links[curdoc.link - 1].l_form->number) && strcmp(links[curdoc.link].l_form->name, links[curdoc.link - 1].l_form->name) == 0) || (curdoc.link == 0 && curdoc.line > 1 && HText_TAHasMoreLines(curdoc.link, -1)))))) { textinput_activated = TRUE; #ifdef TEXTAREA_AUTOGROW if ((c == '\n' || c == '\r') && LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE) c = LAC_TO_LKC0(LYK_NEXT_LINK); #endif /* TEXTAREA_AUTOGROW */ #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION */ } else switch (c) { case '\n': case '\r': #ifdef TEXTAREA_AUTOGROW /* * If on the bottom line of a TEXTAREA, and the user * hit the ENTER key, we add a new line/anchor * automatically, positioning the cursor on it. * * If at the bottom of the screen, we effectively * perform an LYK_DOWN_HALF-like operation, then move * down to the new line we just added. --KED 02/14/99 * * [There is some redundancy and non-standard * indentation in the monster-if() below. This is * intentional ... to try and improve the * "readability" (such as it is). Caveat emptor to * anyone trying to change it.] */ if (LinkIsTextarea(curdoc.link) && ((curdoc.link == nlinks - 1 && !(more_text && HText_TAHasMoreLines(curdoc.link, 1))) || ((curdoc.link < nlinks - 1) && !LinkIsTextarea(curdoc.link + 1)) || ((curdoc.link < nlinks - 1) && (LinkIsTextarea(curdoc.link + 1) && ((links[curdoc.link].l_form->number != links[curdoc.link + 1].l_form->number) || (strcmp(links[curdoc.link].l_form->name, links[curdoc.link + 1].l_form->name) != 0)))))) { HText_ExpandTextarea(&links[curdoc.link], 1); if (links[curdoc.link].ly < display_lines) { refresh_screen = TRUE; } else { LYChgNewline(display_lines / 2); if (nlinks > 0 && curdoc.link > -1 && links[curdoc.link].ly > display_lines / 2) { newdoc.link = curdoc.link; for (i = 0; links[i].ly <= (display_lines / 2); i++) --newdoc.link; newdoc.link++; } } #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION if (textfields_need_activation) { textinput_activated = TRUE; textfields_need_activation = textfields_activation_option; #ifdef INACTIVE_INPUT_STYLE_VH textinput_redrawn = TRUE; #endif }; #endif } #endif /* TEXTAREA_AUTOGROW */ /* * Make return in input field (if it was returned by * change_form_link) act as LYK_NEXT_LINK, independent * of what key (if any) is mapped to LYK_NEXT_LINK. - * kw */ c = LAC_TO_LKC0(LYK_NEXT_LINK); break; default: if (old_c != c && old_c != real_c && c != real_c) real_c = c; } } else { #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH) if (curlink_is_editable && !textinput_redrawn) { /*draw the text entry, but don't activate it */ textinput_redrawn = TRUE; change_form_link_ex(curdoc.link, &newdoc, &refresh_screen, use_last_tfpos, FALSE, TRUE); if (LYShowCursor) { LYmove(links[curdoc.link].ly, ((links[curdoc.link].lx > 0) ? (links[curdoc.link].lx - 1) : 0)); } else { LYHideCursor(); } } #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */ /* * Get a keystroke from the user. Save the last keystroke to * avoid redundant error reporting. */ real_c = c = LYgetch(); /* get user input */ if (c != last_key) key_count = 0; key_count++; last_key = c; #ifndef VMS if (c == 3) { /* ^C */ /* * This shouldn't happen. We'll try to deal with whatever * bug caused it. - FM */ signal(SIGINT, cleanup_sig); old_c = 0; cmd = LYK_QUIT; goto new_cmd; } #endif /* !VMS */ if (LKC_HAS_ESC_MOD(c) && EditBinding(c) != LYE_FORM_PASS) { /* * If ESC + was read (and not recognized as a * terminal escape sequence for another key), ignore the * ESC modifier and act on only if the line editor * binding would have passed the same ESC-modified * lynxkeycode back to us if it had been pressed in a text * input field. Otherwise set interesting part so that it * will map to 0, to prevent that ESC + acts like * , which might be unexpected. - kw */ c = (c & ~LKC_MASK) | LAC_TO_LKC(0); } if (old_c != real_c) { old_c = 0; } } } #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; c = DO_NOTHING; } #else if (recent_sizechange) { if (c <= 0) c = DO_NOTHING; } #endif /* VMS */ new_keyboard_input: /* * A goto point for new input without going back through the getch() * loop. */ if (traversal) { if ((c = DoTraversal(c, &crawl_ok)) < 0) { CleanupMainLoop(); return (EXIT_FAILURE); } } /* traversal */ #ifdef WIN_EX if (c == DO_NOTHING) cmd = LYK_DO_NOTHING; else #endif cmd = LKC_TO_LAC(keymap, c); /* adds 1 to map EOF to 0 */ #if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) if (lynx_edit_mode && !no_dired_support && LKC_TO_LAC(key_override, c)) cmd = LKC_TO_LAC(key_override, c); #endif /* DIRED_SUPPORT && OK_OVERRIDE */ real_cmd = cmd; /* * A goto point for new input without going back through the getch() * loop. */ new_cmd: force_old_UCLYhndl_on_reload = FALSE; CTRACE_FLUSH(tfp); if (cmd != LYK_UP_LINK && cmd != LYK_DOWN_LINK) follow_col = -1; CTRACE((tfp, "Handling key as %s\n", ((LYKeycodeToKcmd((LYKeymapCode) cmd) != 0) ? LYKeycodeToKcmd((LYKeymapCode) cmd)->name : "unknown"))); switch (cmd) { case -1: HTUserMsg(COMMAND_UNKNOWN); break; case 0: /* unmapped character */ default: if (curdoc.link >= 0 && curdoc.link < nlinks && LinkIsTextLike(curdoc.link)) { #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION if (textfields_need_activation) { show_main_statusline(links[curdoc.link], FOR_PANEL); #ifdef INACTIVE_INPUT_STYLE_VH textinput_redrawn = FALSE; #endif } else #endif show_main_statusline(links[curdoc.link], FOR_INPUT); } else if (more_text) { HTInfoMsg(MOREHELP); } else { HTInfoMsg(HELP); } show_help = TRUE; if (TRACE) { sprintf(cfile, "%d", c); LYaddstr(cfile); /* show the user input */ cfile[0] = '\0'; } break; case LYK_COMMAND: cmd = handle_LYK_COMMAND(&user_input_buffer); goto new_cmd; case LYK_INTERRUPT: /* * No network transmission to interrupt - 'til we multithread. */ break; case LYK_F_LINK_NUM: c = '\0'; /* FALLTHRU */ case LYK_1: /* FALLTHRU */ case LYK_2: /* FALLTHRU */ case LYK_3: /* FALLTHRU */ case LYK_4: /* FALLTHRU */ case LYK_5: /* FALLTHRU */ case LYK_6: /* FALLTHRU */ case LYK_7: /* FALLTHRU */ case LYK_8: /* FALLTHRU */ case LYK_9: handle_LYK_digit(c, &force_load, &old_c, real_c, &try_internal); break; case LYK_SOURCE: /* toggle view source mode */ handle_LYK_SOURCE(&ownerS_address); break; case LYK_CHANGE_CENTER: /* ^Q */ if (no_table_center) { no_table_center = FALSE; HTInfoMsg(gettext("TABLE center enable.")); } else { no_table_center = TRUE; HTInfoMsg(gettext("TABLE center disable.")); } /* FALLTHRU */ case LYK_RELOAD: /* control-R to reload and refresh */ handle_LYK_RELOAD(real_cmd); break; case LYK_HISTORICAL: /* toggle 'historical' comments parsing */ handle_LYK_HISTORICAL(); break; case LYK_MINIMAL: /* toggle 'minimal' comments parsing */ handle_LYK_MINIMAL(); break; case LYK_SOFT_DQUOTES: handle_LYK_SOFT_DQUOTES(); break; case LYK_SWITCH_DTD: handle_LYK_SWITCH_DTD(); break; case LYK_QUIT: /* quit */ if (handle_LYK_QUIT()) { CleanupMainLoop(); return (EXIT_SUCCESS); } break; case LYK_ABORT: /* don't ask the user about quitting */ CleanupMainLoop(); return (EXIT_SUCCESS); case LYK_NEXT_PAGE: /* next page */ handle_LYK_NEXT_PAGE(&old_c, real_c); break; case LYK_PREV_PAGE: /* page up */ handle_LYK_PREV_PAGE(&old_c, real_c); break; case LYK_UP_TWO: handle_LYK_UP_TWO(&arrowup, &old_c, real_c); break; case LYK_DOWN_TWO: handle_LYK_DOWN_TWO(&old_c, real_c); break; case LYK_UP_HALF: handle_LYK_UP_HALF(&arrowup, &old_c, real_c); break; case LYK_DOWN_HALF: handle_LYK_DOWN_HALF(&old_c, real_c); break; #ifdef CAN_CUT_AND_PASTE case LYK_TO_CLIPBOARD: /* ^S */ { char *s; int ch2; /* The logic resembles one of ADD_BOOKMARK */ if (nlinks > 0 && links[curdoc.link].lname && links[curdoc.link].type != WWW_FORM_LINK_TYPE) { /* Makes sense to copy a link */ _statusline("Copy D)ocument's or L)ink's URL to clipboard or C)ancel?"); ch2 = LYgetch_single(); if (ch2 == 'D') s = curdoc.address; else if (ch2 == 'C') break; else s = links[curdoc.link].lname; } else s = curdoc.address; if (isEmpty(s)) HTInfoMsg(gettext("Current URL is empty.")); if (put_clip(s)) HTInfoMsg(gettext("Copy to clipboard failed.")); else if (s == curdoc.address) HTInfoMsg(gettext("Document URL put to clipboard.")); else HTInfoMsg(gettext("Link URL put to clipboard.")); } break; case LYK_PASTE_URL: if (no_goto && !LYValidate) { /* Go to not allowed. - FM */ HTUserMsg(GOTO_DISALLOWED); } else { unsigned char *s = (unsigned char *) get_clip_grab(), *e, *t; char *buf; int len2; if (!s) break; len2 = (int) strlen((const char *) s); e = s + len2; while (s < e && StrChr(" \t\n\r", *s)) s++; while (s < e && StrChr(" \t\n\r", e[-1])) e--; if (s[0] == '<' && e > s && e[-1] == '>') { s++; e--; if (!strncasecomp((const char *) s, "URL:", 4)) s += 4; } if (s >= e) { HTInfoMsg(gettext("No URL in the clipboard.")); break; } len = (unsigned) (e - s + 1); if (len < MAX_LINE) len = MAX_LINE; /* Required for do_check_goto_URL() */ buf = typeMallocn(char, len); LYStrNCpy(buf, (const char *) s, (e - s)); t = (unsigned char *) buf; while (s < e) { if (StrChr(" \t\n\r", *s)) { int nl2 = 0; /* Keep whitespace without NL - file names! */ unsigned char *s1 = s; while (StrChr(" \t\n\r", *s)) { if (!nl2 && *s == '\n') nl2 = 1; s++; } if (!nl2) { while (s1 < s) { if (*s1 != '\r' && *s1 != '\n') *t = *s1; t++, s1++; } } } else *t++ = *s++; } *t = '\0'; get_clip_release(); BStrCopy0(user_input_buffer, buf); do_check_goto_URL(&user_input_buffer, &temp, &force_load); free(buf); } break; #endif #ifdef KANJI_CODE_OVERRIDE case LYK_CHG_KCODE: if (LYRawMode && (HTCJK == JAPANESE)) { switch (last_kcode) { case NOKANJI: last_kcode = SJIS; break; case SJIS: last_kcode = EUC; break; case EUC: last_kcode = NOKANJI; break; default: break; } } LYmove(0, 0); lynx_start_title_color(); LYaddstr(str_kcode(last_kcode)); lynx_stop_title_color(); break; #endif case LYK_REFRESH: refresh_screen = TRUE; lynx_force_repaint(); break; case LYK_HOME: if (curdoc.line > 1) { LYSetNewline(1); } else { cmd = LYK_PREV_PAGE; goto new_cmd; } break; case LYK_END: i = HText_getNumOfLines() - display_lines + 2; if (i >= 1 && LYGetNewline() != i) { LYSetNewline(i); /* go to end of file */ arrowup = TRUE; /* position on last link */ } else { cmd = LYK_NEXT_PAGE; goto new_cmd; } break; case LYK_FIRST_LINK: handle_LYK_FIRST_LINK(); break; case LYK_LAST_LINK: handle_LYK_LAST_LINK(); break; case LYK_PREV_LINK: case LYK_LPOS_PREV_LINK: handle_LYK_PREV_LINK(&arrowup, &old_c, real_c); break; case LYK_NEXT_LINK: case LYK_LPOS_NEXT_LINK: handle_LYK_NEXT_LINK(c, &old_c, real_c); break; case LYK_FASTFORW_LINK: handle_LYK_FASTFORW_LINK(&old_c, real_c); break; case LYK_FASTBACKW_LINK: if (handle_LYK_FASTBACKW_LINK(&cmd, &old_c, real_c)) goto new_cmd; break; case LYK_UP_LINK: handle_LYK_UP_LINK(&follow_col, &arrowup, &old_c, real_c); break; case LYK_DOWN_LINK: handle_LYK_DOWN_LINK(&follow_col, &old_c, real_c); break; case LYK_CHANGE_LINK: do_change_link(); #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH) if (textfields_need_activation) textinput_redrawn = FALSE; #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */ break; case LYK_RIGHT_LINK: handle_LYK_RIGHT_LINK(); break; case LYK_LEFT_LINK: handle_LYK_LEFT_LINK(); break; case LYK_COOKIE_JAR: /* show the cookie jar */ if (handle_LYK_COOKIE_JAR(&cmd)) goto new_cmd; break; #ifdef USE_CACHEJAR case LYK_CACHE_JAR: /* show the cache jar */ if (handle_LYK_CACHE_JAR(&cmd)) goto new_cmd; break; #endif case LYK_HISTORY: /* show the history page */ if (handle_LYK_HISTORY(ForcePush)) break; /* FALLTHRU */ case LYK_PREV_DOC: /* back up a level */ switch (handle_PREV_DOC(&cmd, &old_c, real_c)) { case 1: CleanupMainLoop(); return (EXIT_SUCCESS); case 2: goto new_cmd; } break; case LYK_NEXT_DOC: /* undo back up a level */ handle_NEXT_DOC(); break; case LYK_NOCACHE: /* Force submission of form or link with no-cache */ if (!handle_LYK_NOCACHE(&old_c, real_c)) break; /* FALLTHRU */ case LYK_ACTIVATE: /* follow a link */ case LYK_MOUSE_SUBMIT: /* follow a link, submit TEXT_SUBMIT input */ switch (handle_LYK_ACTIVATE(&c, cmd, &try_internal, &refresh_screen, &force_load, real_cmd)) { case 1: continue; case 2: goto new_keyboard_input; case 3: pending_form_c = c; break; } break; case LYK_SUBMIT: handle_LYK_SUBMIT(curdoc.link, &newdoc, &refresh_screen); break; case LYK_RESET: handle_LYK_RESET(curdoc.link, &refresh_screen); break; case LYK_ELGOTO: /* edit URL of current link and go to it */ if (handle_LYK_ELGOTO(&ch, &user_input_buffer, &temp, &old_c, real_c)) do_check_goto_URL(&user_input_buffer, &temp, &force_load); break; case LYK_ECGOTO: /* edit current URL and go to to it */ if (handle_LYK_ECGOTO(&ch, &user_input_buffer, &temp, &old_c, real_c)) do_check_goto_URL(&user_input_buffer, &temp, &force_load); break; case LYK_GOTO: /* 'g' to goto a random URL */ if (handle_LYK_GOTO(&ch, &user_input_buffer, &temp, &recall, &URLTotal, &URLNum, &FirstURLRecall, &old_c, real_c)) { if (do_check_recall(ch, &user_input_buffer, &temp, URLTotal, &URLNum, recall, &FirstURLRecall)) do_check_goto_URL(&user_input_buffer, &temp, &force_load); } break; case LYK_DWIMHELP: /* show context-dependent help file */ handle_LYK_DWIMHELP(&cshelpfile); /* FALLTHRU */ case LYK_HELP: /* show help file */ handle_LYK_HELP(&cshelpfile); break; case LYK_INDEX: /* index file */ handle_LYK_INDEX(&old_c, real_c); break; case LYK_MAIN_MENU: /* return to main screen */ handle_LYK_MAIN_MENU(&old_c, real_c); break; #ifdef EXP_NESTED_TABLES case LYK_NESTED_TABLES: if (handle_LYK_NESTED_TABLES(&cmd)) goto new_cmd; break; #endif case LYK_OPTIONS: /* options screen */ if (handle_LYK_OPTIONS(&cmd, &refresh_screen)) goto new_cmd; break; case LYK_INDEX_SEARCH: /* search for a user string */ handle_LYK_INDEX_SEARCH(&force_load, ForcePush, &old_c, real_c); break; case LYK_WHEREIS: /* search within the document */ case LYK_NEXT: /* find the next occurrence in the document */ case LYK_PREV: /* find the previous occurrence in the document */ handle_LYK_WHEREIS(cmd, &refresh_screen); break; case LYK_COMMENT: /* reply by mail */ handle_LYK_COMMENT(&refresh_screen, &owner_address, &old_c, real_c); break; #ifdef DIRED_SUPPORT case LYK_TAG_LINK: /* tag or untag the current link */ handle_LYK_TAG_LINK(); break; case LYK_MODIFY: /* rename a file or directory */ handle_LYK_MODIFY(&refresh_screen); break; case LYK_CREATE: /* create a new file or directory */ handle_LYK_CREATE(); break; #endif /* DIRED_SUPPORT */ case LYK_DWIMEDIT: /* context-dependent edit */ switch (handle_LYK_DWIMEDIT(&cmd, &old_c, real_c)) { case 1: continue; case 2: goto new_cmd; } /* FALLTHRU */ case LYK_EDIT: /* edit */ handle_LYK_EDIT(&old_c, real_c); break; case LYK_DEL_BOOKMARK: /* remove a bookmark file link */ handle_LYK_DEL_BOOKMARK(&refresh_screen, &old_c, real_c); break; #ifdef DIRED_SUPPORT case LYK_REMOVE: /* remove files and directories */ handle_LYK_REMOVE(&refresh_screen); break; #endif /* DIRED_SUPPORT */ #if defined(DIRED_SUPPORT) && defined(OK_INSTALL) case LYK_INSTALL: /* install a file into system area */ handle_LYK_INSTALL(); break; #endif /* DIRED_SUPPORT && OK_INSTALL */ case LYK_INFO: /* show document info */ if (handle_LYK_INFO(&cmd)) goto new_cmd; break; case LYK_EDITTEXTAREA: /* use external editor on a TEXTAREA - KED */ handle_LYK_EDIT_TEXTAREA(&refresh_screen, &old_c, real_c); break; case LYK_GROWTEXTAREA: /* add new lines to bottom of TEXTAREA - KED */ handle_LYK_GROW_TEXTAREA(&refresh_screen); break; case LYK_INSERTFILE: /* insert file in TEXTAREA, above cursor - KED */ handle_LYK_INSERT_FILE(&refresh_screen, &old_c, real_c); break; case LYK_PRINT: /* print the file */ handle_LYK_PRINT(&ForcePush, &old_c, real_c); break; case LYK_LIST: /* list links in the current document */ if (handle_LYK_LIST(&cmd)) goto new_cmd; break; #ifdef USE_ADDRLIST_PAGE case LYK_ADDRLIST: /* always list URL's (only) */ if (handle_LYK_ADDRLIST(&cmd)) goto new_cmd; break; #endif /* USE_ADDRLIST_PAGE */ case LYK_VLINKS: /* list links visited during the current session */ if (handle_LYK_VLINKS(&cmd, &newdoc_link_is_absolute)) goto new_cmd; break; case LYK_TOOLBAR: /* go to Toolbar or Banner in current document */ handle_LYK_TOOLBAR(&try_internal, &force_load, &old_c, real_c); break; #if defined(DIRED_SUPPORT) || defined(VMS) case LYK_DIRED_MENU: /* provide full file management menu */ handle_LYK_DIRED_MENU(&refresh_screen, &old_c, real_c); break; #endif /* DIRED_SUPPORT || VMS */ #ifdef USE_EXTERNALS case LYK_EXTERN_LINK: /* use external program on url */ handle_LYK_EXTERN_LINK(&refresh_screen); break; case LYK_EXTERN_PAGE: /* use external program on current page */ handle_LYK_EXTERN_PAGE(&refresh_screen); break; #endif /* USE_EXTERNALS */ case LYK_ADD_BOOKMARK: /* add link to bookmark file */ handle_LYK_ADD_BOOKMARK(&refresh_screen, &old_c, real_c); break; case LYK_VIEW_BOOKMARK: /* v to view home page */ handle_LYK_VIEW_BOOKMARK(&refresh_screen, &old_c, real_c); break; case LYK_SHELL: /* (!) shell escape */ handle_LYK_SHELL(&refresh_screen, &old_c, real_c); break; case LYK_DOWNLOAD: switch (handle_LYK_DOWNLOAD(&cmd, &old_c, real_c)) { case 1: continue; case 2: goto new_cmd; } break; #ifdef DIRED_SUPPORT case LYK_UPLOAD: handle_LYK_UPLOAD(); break; #endif /* DIRED_SUPPORT */ case LYK_TRACE_TOGGLE: /* Toggle TRACE mode. */ handle_LYK_TRACE_TOGGLE(); break; case LYK_TRACE_LOG: /* View TRACE log. */ handle_LYK_TRACE_LOG(&trace_mode_flag); break; case LYK_IMAGE_TOGGLE: if (handle_LYK_IMAGE_TOGGLE(&cmd)) goto new_cmd; break; case LYK_INLINE_TOGGLE: if (handle_LYK_INLINE_TOGGLE(&cmd)) goto new_cmd; break; case LYK_RAW_TOGGLE: if (handle_LYK_RAW_TOGGLE(&cmd)) goto new_cmd; break; case LYK_HEAD: if (handle_LYK_HEAD(&cmd)) goto new_cmd; break; case LYK_TOGGLE_HELP: handle_LYK_TOGGLE_HELP(); break; case LYK_EDITMAP: handle_LYK_EDITMAP(&old_c, real_c); break; case LYK_KEYMAP: handle_LYK_KEYMAP(&vi_keys_flag, &emacs_keys_flag, &old_c, real_c); break; case LYK_JUMP: if (handle_LYK_JUMP(c, &user_input_buffer, &temp, &recall, &FirstURLRecall, &URLNum, &URLTotal, &ch, &old_c, real_c)) { if (do_check_recall(ch, &user_input_buffer, &temp, URLTotal, &URLNum, recall, &FirstURLRecall)) do_check_goto_URL(&user_input_buffer, &temp, &force_load); } break; case LYK_CLEAR_AUTH: handle_LYK_CLEAR_AUTH(&old_c, real_c); break; case LYK_DO_NOTHING: /* pretty self explanatory */ break; #ifdef SUPPORT_CHDIR case LYK_CHDIR: handle_LYK_CHDIR(); break; case LYK_PWD: handle_LYK_PWD(); break; #endif #ifdef USE_CURSES_PADS case LYK_SHIFT_LEFT: handle_LYK_SHIFT_LEFT(&refresh_screen, key_count); break; case LYK_SHIFT_RIGHT: handle_LYK_SHIFT_RIGHT(&refresh_screen, key_count); break; case LYK_LINEWRAP_TOGGLE: if (handle_LYK_LINEWRAP_TOGGLE(&cmd, &refresh_screen)) goto new_cmd; break; #endif #ifdef USE_MAXSCREEN_TOGGLE case LYK_MAXSCREEN_TOGGLE: if (handle_LYK_MAXSCREEN_TOGGLE(&cmd)) goto new_cmd; break; #endif } /* end of BIG switch */ } } static int are_different(DocInfo *doc1, DocInfo *doc2) { char *cp1, *cp2; /* * Do we have two addresses? */ if (!doc1->address || !doc2->address) return (TRUE); /* * Do they differ in the type of request? */ if (doc1->isHEAD != doc2->isHEAD) return (TRUE); /* * See if the addresses are different, making sure we're not tripped up by * multiple anchors in the the same document from a POST form. -- FM */ cp1 = trimPoundSelector(doc1->address); cp2 = trimPoundSelector(doc2->address); /* * Are the base addresses different? */ if (strcmp(doc1->address, doc2->address)) { restorePoundSelector(cp1); restorePoundSelector(cp2); return (TRUE); } restorePoundSelector(cp1); restorePoundSelector(cp2); /* * Do the docs have different contents? */ if (doc1->post_data) { if (doc2->post_data) { if (!BINEQ(doc1->post_data, doc2->post_data)) return (TRUE); } else return (TRUE); } else if (doc2->post_data) return (TRUE); /* * We'll assume the two documents in fact are the same. */ return (FALSE); } /* This determines whether two docs are _physically_ different, * meaning they are "from different files". - kw */ static int are_phys_different(DocInfo *doc1, DocInfo *doc2) { char *cp1, *cp2, *ap1 = doc1->address, *ap2 = doc2->address; /* * Do we have two addresses? */ if (!doc1->address || !doc2->address) return (TRUE); /* * Do they differ in the type of request? */ if (doc1->isHEAD != doc2->isHEAD) return (TRUE); /* * Skip over possible LYNXIMGMAP parts. - kw */ if (isLYNXIMGMAP(doc1->address)) ap1 += LEN_LYNXIMGMAP; if (isLYNXIMGMAP(doc2->address)) ap2 += LEN_LYNXIMGMAP; /* * If there isn't any real URL in doc2->address, but maybe just * a fragment, doc2 is assumed to be an internal reference in * the same physical document, so return FALSE. - kw */ if (*ap2 == '\0' || *ap2 == '#') return (FALSE); /* * See if the addresses are different, making sure we're not tripped up by * multiple anchors in the the same document from a POST form. -- FM */ cp1 = trimPoundSelector(doc1->address); cp2 = trimPoundSelector(doc2->address); /* * Are the base addresses different? */ if (strcmp(ap1, ap2)) { restorePoundSelector(cp1); restorePoundSelector(cp2); return (TRUE); } restorePoundSelector(cp1); restorePoundSelector(cp2); /* * Do the docs have different contents? */ if (doc1->post_data) { if (doc2->post_data) { if (!BINEQ(doc1->post_data, doc2->post_data)) return (TRUE); } else return (TRUE); } else if (doc2->post_data) return (TRUE); /* * We'll assume the two documents in fact are the same. */ return (FALSE); } /* * Utility for freeing the list of goto URLs. - FM */ #ifdef LY_FIND_LEAKS static void HTGotoURLs_free(void) { LYFreeStringList(Goto_URLs); Goto_URLs = NULL; } #endif /* * Utility for listing Goto URLs, making any repeated URLs the most current in * the list. - FM */ void HTAddGotoURL(char *url) { char *copy = NULL; char *old; HTList *cur; if (isEmpty(url)) return; CTRACE((tfp, "HTAddGotoURL %s\n", url)); StrAllocCopy(copy, url); if (!Goto_URLs) { Goto_URLs = HTList_new(); #ifdef LY_FIND_LEAKS atexit(HTGotoURLs_free); #endif HTList_addObject(Goto_URLs, copy); return; } cur = Goto_URLs; while (NULL != (old = (char *) HTList_nextObject(cur))) { if (!strcmp(old, copy)) { HTList_removeObject(Goto_URLs, old); FREE(old); break; } } HTList_addObject(Goto_URLs, copy); return; } /* * When help is not on the screen, put a message on the screen to tell the user * other misc info. */ static void show_main_statusline(const LinkInfo curlink, int for_what) { /* * Make sure form novice lines are replaced. */ if (user_mode == NOVICE_MODE && for_what != FOR_INPUT) { noviceline(more_text); } if (HTisDocumentSource()) { /* * Currently displaying HTML source. */ _statusline(SOURCE_HELP); /* * If we are in forms mode then explicitly tell the user what each kind * of link is. */ #ifdef INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0) { #else #ifdef NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 && !(curlink.type & WWW_LINK_TYPE)) { #else } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 && !(user_mode == ADVANCED_MODE && (curlink.type & WWW_LINK_TYPE))) { #endif /* NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES */ #endif /* INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE */ if (curlink.type == WWW_FORM_LINK_TYPE) { show_formlink_statusline(curlink.l_form, for_what); } else { statusline(NORMAL_LINK_MESSAGE); } /* * Let them know if it's an index -- very rare. */ if (is_www_index) { const char *indx = gettext("-index-"); LYmove(LYlines - 1, LYcolLimit - (int) strlen(indx)); lynx_start_reverse(); LYaddstr(indx); lynx_stop_reverse(); } } else if (user_mode == ADVANCED_MODE && nlinks > 0) { /* * Show the URL or, for some internal links, the fragment */ char *cp = NULL; if (curlink.type == WWW_INTERN_LINK_TYPE && !isLYNXIMGMAP(curlink.lname)) { cp = findPoundSelector(curlink.lname); } if (!cp) cp = curlink.lname; status_link(cp, more_text, is_www_index); } else if (is_www_index && more_text) { char buf[128]; sprintf(buf, WWW_INDEX_MORE_MESSAGE, key_for_func(LYK_INDEX_SEARCH)); _statusline(buf); } else if (is_www_index) { char buf[128]; sprintf(buf, WWW_INDEX_MESSAGE, key_for_func(LYK_INDEX_SEARCH)); _statusline(buf); } else if (more_text) { if (user_mode == NOVICE_MODE) _statusline(MORE); else _statusline(MOREHELP); } else { _statusline(HELP); } /* turn off cursor since now it's probably on statusline -HV */ /* But not if LYShowCursor is on. -show_cursor may be used as a * workaround to avoid putting the cursor in the last position, for * curses implementations or terminals that cannot deal with that * correctly. - kw */ if (!LYShowCursor) { LYHideCursor(); } } /* * Public function for redrawing the statusline appropriate for the selected * link. It should only be called at times when curdoc.link, nlinks, and the * links[] array are valid. - kw */ void repaint_main_statusline(int for_what) { if (curdoc.link >= 0 && curdoc.link < nlinks) show_main_statusline(links[curdoc.link], for_what); } static void form_noviceline(int disabled) { LYmove(LYlines - 2, 0); LYclrtoeol(); if (!disabled) { LYaddstr(FORM_NOVICELINE_ONE); } LYParkCursor(); if (disabled) return; if (EditBinding(FROMASCII('\025')) == LYE_ERASE) { LYaddstr(FORM_NOVICELINE_TWO); } else if (EditBinding(FROMASCII('\025')) == LYE_DELBL) { LYaddstr(FORM_NOVICELINE_TWO_DELBL); } else { char *temp = NULL; char *erasekey = fmt_keys(LYKeyForEditAction(LYE_ERASE), -1); if (erasekey) { HTSprintf0(&temp, FORM_NOVICELINE_TWO_VAR, erasekey); } else { erasekey = fmt_keys(LYKeyForEditAction(LYE_DELBL), -1); if (erasekey) HTSprintf0(&temp, FORM_NOVICELINE_TWO_DELBL_VAR, erasekey); } if (temp) { LYaddstr(temp); FREE(temp); } FREE(erasekey); } } static void exit_immediately_with_error_message(int state, int first_file) { char *buf = 0; char *buf2 = 0; if (first_file) { /* print statusline messages as a hint, if any */ LYstatusline_messages_on_exit(&buf2); } if (state == NOT_FOUND) { HTSprintf0(&buf, "%s\n%s %s\n", NonNull(buf2), gettext("lynx: Can't access startfile"), /* * hack: if we fail in HTAccess.c * avoid duplicating URL, oh. */ (buf2 && strstr(buf2, gettext("Can't Access"))) ? "" : startfile); } if (state == NULLFILE) { HTSprintf0(&buf, "%s\n%s\n%s\n", NonNull(buf2), gettext("lynx: Start file could not be found or is not text/html or text/plain"), gettext(" Exiting...")); } FREE(buf2); if (!dump_output_immediately) cleanup(); if (buf != 0) { #ifdef UNIX if (dump_output_immediately) { fputs(buf, stderr); } else #endif /* UNIX */ { SetOutputMode(O_TEXT); fputs(buf, stdout); SetOutputMode(O_BINARY); } FREE(buf); } if (!dump_output_immediately) { exit_immediately(EXIT_FAILURE); } /* else: return(EXIT_FAILURE) in mainloop */ } static void status_link(char *curlink_name, int show_more, int show_indx) { #define MAX_STATUS (LYcolLimit - 1) #define MIN_STATUS 0 char format[MAX_LINE]; int prefix = 0; int length; *format = 0; if (show_more && !nomore) { sprintf(format, "%.*s ", (int) (sizeof(format) - 2), gettext("-more-")); prefix = (int) strlen(format); } if (show_indx) { sprintf(format + prefix, "%.*s ", ((int) sizeof(format) - prefix - 2), gettext("-index-")); } prefix = (int) strlen(format); length = (int) strlen(curlink_name); if (prefix > MAX_STATUS || prefix >= MAX_LINE - 1) { _user_message("%s", format); /* no room for url */ } else { sprintf(format + prefix, "%%.%ds", MAX_STATUS - prefix); if ((length + prefix > MAX_STATUS) && long_url_ok) { char *buf = NULL; int cut_from_pos; int cut_to_pos; int n; StrAllocCopy(buf, curlink_name); /* * Scan to find the final leaf of the URL. Ignore trailing '/'. */ for (cut_to_pos = length - 2; (cut_to_pos > 0) && (buf[cut_to_pos] != '/'); cut_to_pos--) ; /* * Jump back to the next leaf to remove. */ for (cut_from_pos = cut_to_pos - 4; (cut_from_pos > 0) && ((buf[cut_from_pos] != '/') || ((prefix + cut_from_pos + 4 + (length - cut_to_pos)) >= MAX_STATUS)); cut_from_pos--) ; /* * Replace some leaves to '...', if possible, and put the final * leaf at the end. We assume that one can recognize the link from * at least MIN_STATUS characters. */ if (cut_from_pos > MIN_STATUS) { for (n = 1; n <= 3; n++) buf[cut_from_pos + n] = '.'; for (n = 0; cut_to_pos + n <= length; n++) buf[cut_from_pos + 4 + n] = buf[cut_to_pos + n]; } _user_message(format, buf); CTRACE((tfp, "lastline = %s\n", buf)); /* don't forget to erase me */ FREE(buf); } else { /* show (possibly truncated) url */ _user_message(format, curlink_name); } } } const char *LYDownLoadAddress(void) { return NonNull(newdoc.address); } lynx2-8-8/src/LYMainLoop.h000644 023711 023712 00000001474 10475671321 015712 0ustar00dickeylynx000000 000000 #ifndef LYMAINLOOP_H #define LYMAINLOOP_H #ifndef HTUTILS_H #include #endif #ifdef __cplusplus extern "C" { #endif #ifdef DISP_PARTIAL extern BOOL LYMainLoop_pageDisplay(int line_num); #endif extern BOOLEAN LYOpenTraceLog(void); extern const char *LYDownLoadAddress(void); extern int LYGetNewline(void); extern int mainloop(void); extern void HTAddGotoURL(char *url); extern void LYChgNewline(int adjust); extern void LYCloseTracelog(void); extern void LYSetNewline(int value); extern void handle_LYK_TRACE_TOGGLE(void); extern void handle_LYK_WHEREIS(int cmd, BOOLEAN *refresh_screen); extern void repaint_main_statusline(int for_what); #ifdef SUPPORT_CHDIR extern void handle_LYK_CHDIR(void); #endif #ifdef __cplusplus } #endif #endif /* LYMAINLOOP_H */ lynx2-8-8/src/LYMap.c000644 023711 023712 00000042632 12245762550 014707 0ustar00dickeylynx000000 000000 /* * $LynxId: LYMap.c,v 1.48 2013/11/28 11:21:09 tom Exp $ * Lynx Client-side Image MAP Support LYMap.c * ================================== * * Author: FM Foteos Macrides (macrides@sci.wfbr.edu) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DIRED_SUPPORT #include #include #endif #include #include #define NO_MAP_TITLE "[USEMAP]" typedef struct _LYMapElement { char *address; char *title; BOOLEAN intern_flag; } LYMapElement; typedef struct _LYImageMap { char *address; char *title; HTList *elements; } LYImageMap; static HTList *LynxMaps = NULL; BOOL LYMapsOnly = FALSE; /* * Utility for freeing a list of MAPs. */ void ImageMapList_free(HTList *theList) { LYImageMap *map; LYMapElement *element; HTList *cur = theList; HTList *current; if (!cur) return; while (NULL != (map = (LYImageMap *) HTList_nextObject(cur))) { FREE(map->address); FREE(map->title); if (map->elements) { current = map->elements; while (NULL != (element = (LYMapElement *) HTList_nextObject(current))) { FREE(element->address); FREE(element->title); FREE(element); } HTList_delete(map->elements); map->elements = NULL; } FREE(map); } HTList_delete(theList); return; } #ifdef LY_FIND_LEAKS /* * Utility for freeing the global list of MAPs. - kw */ static void LYLynxMaps_free(void) { ImageMapList_free(LynxMaps); LynxMaps = NULL; return; } #endif /* LY_FIND_LEAKS */ /* * We keep two kinds of lists: * - A global list (LynxMaps) shared by MAPs from all documents that * do not have POST data. * - For each response to a POST which contains MAPs, a list specific * to this combination of URL and post_data. It is kept in the * HTParentAnchor structure and is freed when the document is removed * from memory, in the course of normal removal of anchors. * MAPs from POST responses can only be accessed via internal links, * i.e., from within the same document (with the same post_data). * The notion of "same document" is extended, so that LYNXIMGMAP: * and List Page screens are logically part of the document on which * they are based. - kw * * If track_internal_links is false, only the global list will be used * for all MAPs. * */ /* * Utility for creating an LYImageMap list, if it doesn't exist already, adding * LYImageMap entry structures if needed, and removing any LYMapElements in a * pre-existing LYImageMap entry so that it will have only those from AREA tags * for the current analysis of MAP element content. - FM */ BOOL LYAddImageMap(char *address, char *title, HTParentAnchor *node_anchor) { LYImageMap *tmp = NULL; LYImageMap *old = NULL; HTList *cur = NULL; HTList *theList = NULL; HTList *curele = NULL; LYMapElement *ele = NULL; if (isEmpty(address)) return FALSE; if (!(node_anchor && node_anchor->address)) return FALSE; /* * Set theList to either the global LynxMaps list or, if we are associated * with post data, the specific list. The list is created if it doesn't * already exist. - kw */ if (track_internal_links && node_anchor->post_data) { /* * We are handling a MAP element found while parsing node_anchor's * stream of data, and node_anchor has post_data associated and should * therefore represent a POST response, so use the specific list. - kw */ theList = node_anchor->imaps; if (!theList) { theList = node_anchor->imaps = HTList_new(); } } else { if (!LynxMaps) { LynxMaps = HTList_new(); #ifdef LY_FIND_LEAKS atexit(LYLynxMaps_free); #endif } theList = LynxMaps; } if (theList) { cur = theList; while (NULL != (old = (LYImageMap *) HTList_nextObject(cur))) { if (old->address == 0) /* shouldn't happen */ continue; if (!strcmp(old->address, address)) { FREE(old->address); FREE(old->title); if (old->elements) { curele = old->elements; while (NULL != (ele = (LYMapElement *) HTList_nextObject(curele))) { FREE(ele->address); FREE(ele->title); FREE(ele); } HTList_delete(old->elements); old->elements = NULL; } break; } } } tmp = (old != NULL) ? old : typecalloc(LYImageMap); if (tmp == NULL) { outofmem(__FILE__, "LYAddImageMap"); return FALSE; } StrAllocCopy(tmp->address, address); if (non_empty(title)) StrAllocCopy(tmp->title, title); if (tmp != old) HTList_addObject(theList, tmp); return TRUE; } /* * Utility for adding LYMapElement's to LYImageMap's * in the appropriate list. - FM */ BOOL LYAddMapElement(char *map, char *address, char *title, HTParentAnchor *node_anchor, int intern_flag GCC_UNUSED) { LYMapElement *tmp = NULL; LYImageMap *theMap = NULL; HTList *theList = NULL; HTList *cur = NULL; if (isEmpty(map) || isEmpty(address)) return FALSE; if (!(node_anchor && node_anchor->address)) return FALSE; /* * Set theList to either the global LynxMaps list or, if we are associated * with post data, the specific list. The list should already exist, since * this function is only called if the AREA tag we are handling was within * a MAP element in node_anchor's stream of data, so that LYAddImageMap has * been called. - kw */ if (track_internal_links && node_anchor->post_data) { /* * We are handling an AREA tag found while parsing node_anchor's stream * of data, and node_anchor has post_data associated and should * therefore represent a POST response, so use the specific list. - kw */ theList = node_anchor->imaps; if (!theList) { return FALSE; } } else { if (!LynxMaps) LYAddImageMap(map, NULL, node_anchor); theList = LynxMaps; } cur = theList; while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) { if (!strcmp(theMap->address, map)) { break; } } if (!theMap) return FALSE; if (!theMap->elements) theMap->elements = HTList_new(); cur = theMap->elements; while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) { if (!strcmp(tmp->address, address)) { FREE(tmp->address); FREE(tmp->title); HTList_removeObject(theMap->elements, tmp); FREE(tmp); break; } } tmp = typecalloc(LYMapElement); if (tmp == NULL) { perror("Out of memory in LYAddMapElement"); return FALSE; } StrAllocCopy(tmp->address, address); if (non_empty(title)) StrAllocCopy(tmp->title, title); else StrAllocCopy(tmp->title, address); if (track_internal_links) tmp->intern_flag = (BOOLEAN) intern_flag; HTList_appendObject(theMap->elements, tmp); CTRACE((tfp, "LYAddMapElement\n\tmap %s\n\taddress %s\n\ttitle %s)\n", NonNull(map), NonNull(address), NonNull(title))); return TRUE; } /* * Utility for checking whether an LYImageMap entry with a given address * already exists in the LynxMaps structure. - FM */ BOOL LYHaveImageMap(char *address) { LYImageMap *Map; HTList *cur = LynxMaps; if (!(cur && non_empty(address))) return FALSE; while (NULL != (Map = (LYImageMap *) HTList_nextObject(cur))) { if (!strcmp(Map->address, address)) { return TRUE; } } return FALSE; } /* * Fills in a DocAddress structure for getting the HTParentAnchor of the * underlying resource. ALso returns a pointer to that anchor in * *punderlying if we are dealing with POST data. - kw * * address is the address of the underlying resource, i.e., the one * containing the MAP element, the MAP's name appended as * fragment is ignored. * anAnchor is the LYNXIMGMAP: anchor; if it is associated with POST * data, we want the specific list, otherwise the global list. */ static void fill_DocAddress(DocAddress *wwwdoc, const char *address, HTParentAnchor *anAnchor, HTParentAnchor **punderlying) { char *doc_address = NULL; HTParentAnchor *underlying; StrAllocCopy(doc_address, address); if (anAnchor && anAnchor->post_data) { wwwdoc->address = doc_address; wwwdoc->post_data = anAnchor->post_data; wwwdoc->post_content_type = anAnchor->post_content_type; wwwdoc->bookmark = NULL; wwwdoc->isHEAD = FALSE; wwwdoc->safe = FALSE; underlying = HTAnchor_findAddress(wwwdoc); if (underlying->safe) wwwdoc->safe = TRUE; if (punderlying) *punderlying = underlying; } else { wwwdoc->address = doc_address; wwwdoc->post_data = NULL; wwwdoc->post_content_type = NULL; wwwdoc->bookmark = NULL; wwwdoc->isHEAD = FALSE; wwwdoc->safe = FALSE; if (punderlying) *punderlying = NULL; } } /* * Get the appropriate list for creating a LYNXIMGMAP: pseudo- document: * either the global list (LynxMaps), or the specific list if a List Page for a * POST response is requested. Also fill in the DocAddress structure etc. by * calling fill_DocAddress(). * * address is the address of the underlying resource, i.e., the one * containing the MAP element, the MAP's name appended as * fragment is ignored. * anchor is the LYNXIMGMAP: anchor for which LYLoadIMGmap() is * requested; if it is associated with POST data, we want the * specific list for this combination of address+post_data. * * if track_internal_links is false, the Anchor passed to * LYLoadIMGmap() will never have post_data, so that the global list * will be used. - kw */ static HTList *get_the_list(DocAddress *wwwdoc, const char *address, HTParentAnchor *anchor, HTParentAnchor **punderlying) { HTList *result; if (anchor->post_data) { fill_DocAddress(wwwdoc, address, anchor, punderlying); if (non_empty(punderlying)) { result = (*punderlying)->imaps; } else { result = anchor->imaps; } } else { fill_DocAddress(wwwdoc, address, NULL, punderlying); result = LynxMaps; } return result; } /* LYLoadIMGmap - F.Macrides (macrides@sci.wfeb.edu) * ------------ * Create a text/html stream with a list of links * for HyperText References in AREAs of a MAP. */ static int LYLoadIMGmap(const char *arg, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *sink) { HTFormat format_in = WWW_HTML; HTStream *target = NULL; char *buf = NULL; LYMapElement *tmp = NULL; LYImageMap *theMap = NULL; char *MapTitle = NULL; char *MapAddress = NULL; HTList *theList; HTList *cur = NULL; const char *address = NULL; char *cp = NULL; DocAddress WWWDoc; HTParentAnchor *underlying; BOOL old_cache_setting = LYforce_no_cache; BOOL old_reloading = reloading; HTFormat old_format_out = HTOutputFormat; if (isLYNXIMGMAP(arg)) { address = (arg + LEN_LYNXIMGMAP); } if (!(address && StrChr(address, ':'))) { HTAlert(MISDIRECTED_MAP_REQUEST); return (HT_NOT_LOADED); } theList = get_the_list(&WWWDoc, address, anAnchor, &underlying); if (WWWDoc.safe) anAnchor->safe = TRUE; if (!theList) { if (anAnchor->post_data && !WWWDoc.safe && ((underlying && underlying->document && !LYforce_no_cache) || HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) { HTAlert(FAILED_MAP_POST_REQUEST); return (HT_NOT_LOADED); } LYforce_no_cache = TRUE; reloading = TRUE; HTOutputFormat = WWW_PRESENT; LYMapsOnly = TRUE; if (!HTLoadAbsolute(&WWWDoc)) { LYforce_no_cache = old_cache_setting; reloading = old_reloading; HTOutputFormat = old_format_out; LYMapsOnly = FALSE; HTAlert(MAP_NOT_ACCESSIBLE); return (HT_NOT_LOADED); } LYforce_no_cache = old_cache_setting; reloading = old_reloading; HTOutputFormat = old_format_out; LYMapsOnly = FALSE; theList = get_the_list(&WWWDoc, address, anAnchor, &underlying); } if (!theList) { HTAlert(MAPS_NOT_AVAILABLE); return (HT_NOT_LOADED); } cur = theList; while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) { if (!strcmp(theMap->address, address)) { break; } } if (theMap && HTList_count(theMap->elements) == 0) { /* * We found a MAP without any usable AREA. Fake a redirection to the * address with fragment. We do this even for post data (internal link * within a document with post data) if it will not result in an * unwanted network request. - kw */ if (!anAnchor->post_data) { StrAllocCopy(redirecting_url, address); return (HT_REDIRECTING); } else if (WWWDoc.safe || (underlying->document && !anAnchor->document && (LYinternal_flag || LYoverride_no_cache))) { StrAllocCopy(redirecting_url, address); redirect_post_content = TRUE; return (HT_REDIRECTING); } } if (!(theMap && theMap->elements)) { if (anAnchor->post_data && !WWWDoc.safe && ((underlying && underlying->document && !LYforce_no_cache) || HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) { HTAlert(FAILED_MAP_POST_REQUEST); return (HT_NOT_LOADED); } LYforce_no_cache = TRUE; reloading = TRUE; HTOutputFormat = WWW_PRESENT; LYMapsOnly = TRUE; if (!HTLoadAbsolute(&WWWDoc)) { LYforce_no_cache = old_cache_setting; reloading = old_reloading; HTOutputFormat = old_format_out; LYMapsOnly = FALSE; HTAlert(MAP_NOT_ACCESSIBLE); return (HT_NOT_LOADED); } LYforce_no_cache = old_cache_setting; reloading = old_reloading; HTOutputFormat = old_format_out; LYMapsOnly = FALSE; cur = get_the_list(&WWWDoc, address, anAnchor, &underlying); while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) { if (!strcmp(theMap->address, address)) { break; } } if (!(theMap && theMap->elements)) { HTAlert(MAP_NOT_AVAILABLE); return (HT_NOT_LOADED); } } if (track_internal_links) anAnchor->no_cache = TRUE; target = HTStreamStack(format_in, format_out, sink, anAnchor); if (!target || target == NULL) { HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O, HTAtom_name(format_in), HTAtom_name(format_out)); HTAlert(buf); FREE(buf); return (HT_NOT_LOADED); } if (non_empty(theMap->title)) { StrAllocCopy(MapTitle, theMap->title); } else if (non_empty(anAnchor->title)) { StrAllocCopy(MapTitle, anAnchor->title); } else if (non_empty(LYRequestTitle) && strcasecomp(LYRequestTitle, NO_MAP_TITLE)) { StrAllocCopy(MapTitle, LYRequestTitle); } else if ((cp = StrChr(address, '#')) != NULL) { StrAllocCopy(MapTitle, (cp + 1)); } if (isEmpty(MapTitle)) { StrAllocCopy(MapTitle, NO_MAP_TITLE); } else { LYEntify(&MapTitle, TRUE); } #define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf)) HTSprintf0(&buf, "\n\n"); PUTS(buf); HTSprintf0(&buf, "\n", "http-equiv=\"content-type\"", LYCharSet_UC[current_char_set].MIMEname); PUTS(buf); /* * This page is a list of titles and anchors for them. Since titles * already passed SGML/HTML stage they are converted to current_char_set. * That is why we insist on META charset for this page. */ HTSprintf0(&buf, "%s\n", MapTitle); PUTS(buf); HTSprintf0(&buf, "\n\n"); PUTS(buf); HTSprintf0(&buf, "

        %s

        \n", MapTitle); PUTS(buf); StrAllocCopy(MapAddress, address); LYEntify(&MapAddress, FALSE); HTSprintf0(&buf, "

        MAP: %s

        \n", MapAddress); PUTS(buf); HTSprintf0(&buf, "<%s compact>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ? "ol" : "ul")); PUTS(buf); cur = theMap->elements; while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) { StrAllocCopy(MapAddress, tmp->address); LYEntify(&MapAddress, FALSE); PUTS("
      2. intern_flag) { PUTS(" TYPE=\"internal link\""); } PUTS("\n>"); LYformTitle(&MapTitle, tmp->title); LYEntify(&MapTitle, TRUE); PUTS(MapTitle); PUTS("\n"); } HTSprintf0(&buf, "\n\n\n", ((keypad_mode == NUMBERS_AS_ARROWS) ? "ol" : "ul")); PUTS(buf); (*target->isa->_free) (target); FREE(MapAddress); FREE(MapTitle); FREE(buf); return (HT_LOADED); } void LYPrintImgMaps(FILE *fp) { const char *only = HTLoadedDocumentURL(); size_t only_len = strlen(only); HTList *outer = LynxMaps; HTList *inner; LYImageMap *map; LYMapElement *elt; int count; if (HTList_count(outer) > 0) { while (NULL != (map = (LYImageMap *) HTList_nextObject(outer))) { if (only_len != 0) { if (StrNCmp(only, map->address, only_len) || (map->address[only_len] != '\0' && map->address[only_len] != '#')) { continue; } } fprintf(fp, "\n%s\n", isEmpty(map->title) ? NO_MAP_TITLE : map->title); fprintf(fp, "%s\n", map->address); inner = map->elements; count = 0; while (NULL != (elt = (LYMapElement *) HTList_nextObject(inner))) { fprintf(fp, "%4d. %s", ++count, elt->address); if (track_internal_links && elt->intern_flag) fprintf(fp, " TYPE=\"internal link\""); fprintf(fp, "\n"); } } } } #ifdef GLOBALDEF_IS_MACRO #define _LYIMGMAP_C_GLOBALDEF_1_INIT { "LYNXIMGMAP", LYLoadIMGmap, 0} GLOBALDEF(HTProtocol, LYLynxIMGmap, _LYIMGMAP_C_GLOBALDEF_1_INIT); #else GLOBALDEF HTProtocol LYLynxIMGmap = {"LYNXIMGMAP", LYLoadIMGmap, 0}; #endif /* GLOBALDEF_IS_MACRO */ lynx2-8-8/src/LYMap.h000644 023711 023712 00000001231 11452321030 014661 0ustar00dickeylynx000000 000000 /* $LynxId: LYMap.h,v 1.12 2010/09/25 11:35:42 tom Exp $ */ #ifndef LYMAP_H #define LYMAP_H #ifndef HTUTILS_H #include #endif #include #include #ifdef __cplusplus extern "C" { #endif extern BOOL LYMapsOnly; extern void ImageMapList_free(HTList *list); extern void LYPrintImgMaps(FILE *fp); extern BOOL LYAddImageMap(char *address, char *title, HTParentAnchor *node_anchor); extern BOOL LYAddMapElement(char *map, char *address, char *title, HTParentAnchor *node_anchor, int intern_flag); extern BOOL LYHaveImageMap(char *address); #ifdef __cplusplus } #endif #endif /* LYMAP_H */ lynx2-8-8/src/LYNews.c000644 023711 023712 00000032240 12245762550 015100 0ustar00dickeylynx000000 000000 /* * $LynxId: LYNews.c,v 1.61 2013/11/28 11:21:09 tom Exp $ */ #include #ifndef DISABLE_NEWS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Global variable for async i/o. */ BOOLEAN term_message = FALSE; static void terminate_message(int sig); static BOOLEAN message_has_content(const char *filename, BOOLEAN *nonspaces) { FILE *fp; char *buffer = NULL; BOOLEAN in_headers = TRUE; *nonspaces = FALSE; if (!filename || (fp = fopen(filename, "r")) == NULL) { CTRACE((tfp, "Failed to open file %s for reading!\n", NONNULL(filename))); return FALSE; } while (LYSafeGets(&buffer, fp) != NULL) { char *cp = buffer; char firstnonblank = '\0'; LYTrimNewline(cp); for (; *cp; cp++) { if (!firstnonblank && isgraph(UCH(*cp))) { firstnonblank = *cp; } else if (!isspace(UCH(*cp))) { *nonspaces = TRUE; } } if (firstnonblank && firstnonblank != '>') { if (!in_headers) { LYCloseInput(fp); FREE(buffer); return TRUE; } } if (!firstnonblank) { in_headers = FALSE; } } FREE(buffer); LYCloseInput(fp); return FALSE; } /* * This function is called from HTLoadNews() to have the user * create a file with news headers and a body for posting of * a new message (based on a newspost://nntp_host/newsgroups * or snewspost://secure_nntp_host/newsgroups URL), or to post * a followup (based on a newsreply://nntp_host/newsgroups or * snewsreply://secure_nntp_host/newsgroups URL). The group * or comma-separated list of newsgroups is passed without * a lead slash, and followup is TRUE for newsreply or * snewsreply URLs. - FM */ char *LYNewsPost(char *newsgroups, int followup) { char user_input[MAX_LINE]; char CJKinput[MAX_LINE]; char *cp = NULL; const char *kp = NULL; int c = 0; /* user input */ int len; FILE *fd = NULL; char my_tempfile[LY_MAXPATH]; FILE *fc = NULL; char CJKfile[LY_MAXPATH]; char *postfile = NULL; char *NewsGroups = NULL; char *References = NULL; char *org = NULL; FILE *fp = NULL; BOOLEAN nonempty = FALSE; BOOLEAN nonspaces = FALSE; /* * Make sure a non-zero length newspost, newsreply, snewspost or snewsreply * path was sent to us. - FM */ if (isEmpty(newsgroups)) return (postfile); /* * Return immediately if we do get called, maybe by some quirk of HTNews.c, * when we shouldn't. - kw */ if (no_newspost) return (postfile); /* * Open a temporary file for the headers and message body. - FM */ #ifdef __DJGPP__ if ((fd = LYOpenTemp(my_tempfile, HTML_SUFFIX, BIN_W)) == NULL) #else if ((fd = LYOpenTemp(my_tempfile, HTML_SUFFIX, "w")) == NULL) #endif /* __DJGPP__ */ { HTAlert(CANNOT_OPEN_TEMP); return (postfile); } /* * If we're using a Japanese display character set, open a temporary file * for a conversion to JIS. - FM */ CJKfile[0] = '\0'; if (current_char_set == UCGetLYhndl_byMIME("euc-jp") || current_char_set == UCGetLYhndl_byMIME("shift_jis")) { if ((fc = LYOpenTemp(CJKfile, HTML_SUFFIX, "w")) == NULL) { HTAlert(CANNOT_OPEN_TEMP); (void) LYRemoveTemp(my_tempfile); return (postfile); } } /* * The newsgroups could be a comma-seperated list. It need not have * spaces, but deal with any that may also have been hex escaped. - FM */ StrAllocCopy(NewsGroups, newsgroups); if ((cp = strstr(NewsGroups, ";ref="))) { *cp = '\0'; cp += 5; if (*cp == '<') { StrAllocCopy(References, cp); } else { StrAllocCopy(References, "<"); StrAllocCat(References, cp); StrAllocCat(References, ">"); } HTUnEscape(References); if (!((cp = StrChr(References, '@')) && cp > References + 1 && isalnum(UCH(cp[1])))) { FREE(References); } } HTUnEscape(NewsGroups); if (!*NewsGroups) { LYCloseTempFP(fd); /* Close the temp file. */ goto cleanup; } /* * Allow ^C to cancel the posting, i.e., don't let SIGINTs exit Lynx. */ signal(SIGINT, terminate_message); term_message = FALSE; /* * Show the list of newsgroups. - FM */ LYclear(); LYmove(2, 0); scrollok(LYwin, TRUE); /* Enable scrolling. */ LYaddstr(gettext("You will be posting to:")); LYaddstr("\n\t"); LYaddstr(NewsGroups); LYaddch('\n'); /* * Get the mail address for the From header, offering personal_mail_address * as default. */ LYaddstr(gettext("\n\n Please provide your mail address for the From: header\n")); sprintf(user_input, "From: %.*s", (int) sizeof(user_input) - 8, NonNull(personal_mail_address)); if (LYGetStr(user_input, FALSE, sizeof(user_input), NORECALL) < 0 || term_message) { HTInfoMsg(NEWS_POST_CANCELLED); LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ goto cleanup; } fprintf(fd, "%s\n", user_input); /* * Get the Subject header, offering the current document's title as the * default if this is a followup rather than a new post. - FM */ LYaddstr(gettext("\n\n Please provide or edit the Subject: header\n")); strcpy(user_input, "Subject: "); if ((followup == TRUE && nhist > 0) && (kp = HText_getTitle()) != NULL) { /* * Add the default subject. */ kp = LYSkipCBlanks(kp); #ifdef CJK_EX /* 1998/05/15 (Fri) 09:10:38 */ if (HTCJK == JAPANESE) { CJKinput[0] = '\0'; switch (kanji_code) { case EUC: TO_EUC((const unsigned char *) kp, (unsigned char *) CJKinput); kp = CJKinput; break; case SJIS: TO_SJIS((const unsigned char *) kp, (unsigned char *) CJKinput); kp = CJKinput; break; default: break; } } #endif if (strncasecomp(kp, "Re:", 3)) { strcat(user_input, "Re: "); } len = (int) strlen(user_input); LYStrNCpy(user_input + len, kp, (int) sizeof(user_input) - len - 1); } cp = NULL; if (LYGetStr(user_input, FALSE, sizeof(user_input), NORECALL) < 0 || term_message) { HTInfoMsg(NEWS_POST_CANCELLED); LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ goto cleanup; } fprintf(fd, "%s\n", user_input); /* * Add Organization: header. */ StrAllocCopy(cp, "Organization: "); if ((org = LYGetEnv("ORGANIZATION")) != NULL) { StrAllocCat(cp, org); } else if ((org = LYGetEnv("NEWS_ORGANIZATION")) != NULL) { StrAllocCat(cp, org); } #ifdef UNIX else if ((fp = fopen("/etc/organization", TXT_R)) != NULL) { char *buffer = 0; if (LYSafeGets(&buffer, fp) != NULL) { if (user_input[0] != '\0') { LYTrimNewline(buffer); StrAllocCat(cp, buffer); } } FREE(buffer); LYCloseInput(fp); } #else #ifdef _WINDOWS /* 1998/05/14 (Thu) 17:47:01 */ else { char *p, fname[LY_MAXPATH]; strcpy(fname, LynxSigFile); p = strrchr(fname, '/'); if (p != 0 && (p - fname) < sizeof(fname) - 15) { strcpy(p + 1, "LYNX_ETC.TXT"); if ((fp = fopen(fname, TXT_R)) != NULL) { if (fgets(user_input, (int) sizeof(user_input), fp) != NULL) { if ((org = StrChr(user_input, '\n')) != NULL) { *org = '\0'; } if (user_input[0] != '\0') { StrAllocCat(cp, user_input); } } LYCloseInput(fp); } } } #endif /* _WINDOWS */ #endif /* !UNIX */ LYStrNCpy(user_input, cp, (sizeof(user_input) - 16)); FREE(cp); LYaddstr(gettext("\n\n Please provide or edit the Organization: header\n")); if (LYGetStr(user_input, FALSE, sizeof(user_input), NORECALL) < 0 || term_message) { HTInfoMsg(NEWS_POST_CANCELLED); LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ goto cleanup; } fprintf(fd, "%s\n", user_input); if (References) { fprintf(fd, "References: %s\n", References); } /* * Add Newsgroups Summary and Keywords headers. */ fprintf(fd, "Newsgroups: %s\nSummary: \nKeywords: \n\n", NewsGroups); /* * Have the user create the message body. */ if (!no_editor && non_empty(editor)) { if (followup && nhist > 0) { /* * Ask if the user wants to include the original message. */ if (term_message) { _statusline(INC_ORIG_MSG_PROMPT); } else if (HTConfirm(INC_ORIG_MSG_PROMPT) == YES) { /* * The 'TRUE' will add the reply ">" in front of every line. * We're assuming that if the display character set is Japanese * and the document did not have a CJK charset, any non-EUC or * non-SJIS 8-bit characters in it where converted to 7-bit * equivalents. - FM */ print_wwwfile_to_fd(fd, FALSE, TRUE); } } LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ if (term_message || LYCharIsINTERRUPT(c)) goto cleanup; /* * Spawn the user's editor on the news file. */ edit_temporary_file(my_tempfile, "", SPAWNING_EDITOR_FOR_NEWS); nonempty = message_has_content(my_tempfile, &nonspaces); } else { /* * Use the built in line editior. */ LYaddstr(gettext("\n\n Please enter your message below.")); LYaddstr(gettext("\n When you are done, press enter and put a single period (.)")); LYaddstr(gettext("\n on a line and press enter again.")); LYaddstr("\n\n"); LYrefresh(); *user_input = '\0'; if (LYGetStr(user_input, FALSE, sizeof(user_input), NORECALL) < 0 || term_message) { HTInfoMsg(NEWS_POST_CANCELLED); LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ goto cleanup; } while (!STREQ(user_input, ".") && !term_message) { LYaddch('\n'); fprintf(fd, "%s\n", user_input); if (!nonempty && strlen(user_input)) nonempty = TRUE; *user_input = '\0'; if (LYGetStr(user_input, FALSE, sizeof(user_input), NORECALL) < 0) { HTInfoMsg(NEWS_POST_CANCELLED); LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ goto cleanup; } } fprintf(fd, "\n"); LYCloseTempFP(fd); /* Close the temp file. */ scrollok(LYwin, FALSE); /* Stop scrolling. */ } if (nonempty) { /* * Confirm whether to post, and if so, whether to append the sig file. * - FM */ LYStatusLine = (LYlines - 1); c = HTConfirm(POST_MSG_PROMPT); LYStatusLine = -1; if (c != YES) { LYclear(); /* clear the screen */ goto cleanup; } } else { HTAlert(gettext("Message has no original text!")); if (!nonspaces || HTConfirmDefault(POST_MSG_PROMPT, NO) != YES) goto cleanup; } if ((LynxSigFile != NULL) && (fp = fopen(LynxSigFile, TXT_R)) != NULL) { char *msg = NULL; HTSprintf0(&msg, APPEND_SIG_FILE, LynxSigFile); LYStatusLine = (LYlines - 1); if (term_message) { _user_message(APPEND_SIG_FILE, LynxSigFile); } else if (HTConfirm(msg) == YES) { if ((fd = LYAppendToTxtFile(my_tempfile)) != NULL) { char *buffer = NULL; fputs("-- \n", fd); while (LYSafeGets(&buffer, fp) != NULL) { fputs(buffer, fd); } LYCloseOutput(fd); } } LYCloseInput(fp); FREE(msg); LYStatusLine = -1; } LYclear(); /* clear the screen */ /* * If we are using a Japanese display character set, convert the contents * of the temp file to JIS (nothing should change if it does not, in fact, * contain EUC or SJIS di-bytes). Otherwise, use the temp file as is. - * FM */ if (CJKfile[0] != '\0') { if ((fd = fopen(my_tempfile, TXT_R)) != NULL) { char *buffer = NULL; while (LYSafeGets(&buffer, fd) != NULL) { TO_JIS((unsigned char *) buffer, (unsigned char *) CJKinput); fputs(CJKinput, fc); } LYCloseTempFP(fc); StrAllocCopy(postfile, CJKfile); LYCloseInput(fd); (void) LYRemoveTemp(my_tempfile); strcpy(my_tempfile, CJKfile); CJKfile[0] = '\0'; } else { StrAllocCopy(postfile, my_tempfile); } } else { StrAllocCopy(postfile, my_tempfile); } if (!followup) { /* * If it's not a followup, the current document most likely is the * group listing, so force a to have the article show up in the list * after the posting. Note, that if it's a followup via a link in a * news article, the user must do a reload manually on returning to the * group listing. - FM */ LYforce_no_cache = TRUE; } LYStatusLine = (LYlines - 1); HTUserMsg(POSTING_TO_NEWS); LYStatusLine = -1; /* * Come here to cleanup and exit. */ cleanup: #ifndef VMS signal(SIGINT, cleanup_sig); #endif /* !VMS */ term_message = FALSE; if (!postfile) (void) LYRemoveTemp(my_tempfile); (void) LYRemoveTemp(CJKfile); FREE(NewsGroups); FREE(References); return (postfile); } static void terminate_message(int sig GCC_UNUSED) { term_message = TRUE; /* * Reassert the AST. */ signal(SIGINT, terminate_message); #ifdef VMS /* * Refresh the screen to get rid of the "interrupt" message. */ lynx_force_repaint(); LYrefresh(); #endif /* VMS */ } #endif /* not DISABLE_NEWS */ lynx2-8-8/src/LYNews.h000644 023711 023712 00000000555 11452321030 015070 0ustar00dickeylynx000000 000000 /* $LynxId: LYNews.h,v 1.10 2010/09/25 11:35:12 tom Exp $ */ #ifndef LYNEWSPOST_H #define LYNEWSPOST_H #ifndef LYSTRUCTS_H #include #endif /* LYSTRUCTS_H */ #ifdef __cplusplus extern "C" { #endif extern BOOLEAN term_message; extern char *LYNewsPost(char *newsgroups, int followup); #ifdef __cplusplus } #endif #endif /* LYNEWSPOST_H */ lynx2-8-8/src/LYOptions.c000644 023711 023712 00000341755 12245762550 015635 0ustar00dickeylynx000000 000000 /* $LynxId: LYOptions.c,v 1.164 2013/10/25 01:10:17 tom Exp $ */ #include #include #include /* 'reloading' flag */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_COLOR_STYLE #include #endif #include BOOLEAN term_options; #define TOP_LINK "/" #define MBM_LINK "//MBM_MENU" #define MARGIN_STR (no_margins ? "" : "  ") #define MARGIN_LEN (no_margins ? 0 : 2) static void terminate_options(int sig); #define COL_OPTION_VALUES 36 /* display column where option values start */ #if defined(USE_SLANG) || defined(COLOR_CURSES) static BOOLEAN can_do_colors = FALSE; #endif static int LYChosenShowColor = SHOW_COLOR_UNKNOWN; /* whether to show and save */ BOOLEAN LYCheckUserAgent(void) { if (non_empty(LYUserAgent)) { if (strstr(LYUserAgent, "Lynx") == 0 && strstr(LYUserAgent, "lynx") == 0 && strstr(LYUserAgent, "L_y_n_x") == 0 && strstr(LYUserAgent, "l_y_n_x") == 0) { return FALSE; } } return TRUE; } static void validate_x_display(void) { char *cp; if ((cp = LYgetXDisplay()) != NULL) { StrAllocCopy(x_display, cp); } else { FREE(x_display); } } static void summarize_x_display(char *display_option) { if ((x_display == NULL && *display_option == '\0') || (x_display != NULL && !strcmp(x_display, display_option))) { if (x_display == NULL && LYisConfiguredForX == TRUE) { _statusline(VALUE_ACCEPTED_WARNING_X); } else if (x_display != NULL && LYisConfiguredForX == FALSE) { _statusline(VALUE_ACCEPTED_WARNING_NONX); } else { _statusline(VALUE_ACCEPTED); } } else { if (*display_option) { _statusline(FAILED_TO_SET_DISPLAY); } else { _statusline(FAILED_CLEAR_SET_DISPLAY); } } } static void SetupChosenShowColor(void) { #if defined(USE_SLANG) || defined(COLOR_CURSES) can_do_colors = TRUE; #if defined(COLOR_CURSES) if (LYCursesON) /* could crash if called before initialization */ can_do_colors = (has_colors() ? TRUE : FALSE); #endif if (!no_option_save) { if (LYChosenShowColor == SHOW_COLOR_UNKNOWN) { switch (LYrcShowColor) { case SHOW_COLOR_NEVER: LYChosenShowColor = (LYShowColor >= SHOW_COLOR_ON) ? SHOW_COLOR_ON : SHOW_COLOR_NEVER; break; case SHOW_COLOR_ALWAYS: if (!can_do_colors) LYChosenShowColor = SHOW_COLOR_ALWAYS; else LYChosenShowColor = (LYShowColor >= SHOW_COLOR_ON) ? SHOW_COLOR_ALWAYS : SHOW_COLOR_OFF; break; default: LYChosenShowColor = (LYShowColor >= SHOW_COLOR_ON) ? SHOW_COLOR_ON : SHOW_COLOR_OFF; } } } #endif /* USE_SLANG || COLOR_CURSES */ } #ifndef NO_OPTION_MENU static int boolean_choice(int status, int line, int column, STRING2PTR choices); #define LYChooseBoolean(status, line, column, choices) \ (BOOLEAN) boolean_choice(status, line, column, (const char *const*)choices) #define LYChooseEnum(status, line, column, choices) \ boolean_choice(status, line, column, (const char *const*)choices) #define MAXCHOICES 10 /* * Values for the options menu. - FM * * L_foo values are the Y coordinates for the menu item. * B_foo values are the X coordinates for the item's prompt string. * C_foo values are the X coordinates for the item's value string. */ #define L_EDITOR 2 #define L_DISPLAY 3 #define L_HOME 4 #define C_MULTI 24 #define B_BOOK 34 #define C_DEFAULT 50 #define L_FTPSTYPE 5 #define L_MAIL_ADDRESS 6 #define L_SSEARCH 7 #define L_LANGUAGE 8 #define L_PREF_CHARSET 9 #define L_ASSUME_CHARSET (L_PREF_CHARSET + 1) #define L_CHARSET 10 #define L_RAWMODE 11 #define L_COLOR L_RAWMODE #define B_COLOR 44 #define C_COLOR 62 #define L_BOOL_A 12 #define B_VIKEYS 5 #define C_VIKEYS 15 #define B_EMACSKEYS 22 #define C_EMACSKEYS 36 #define B_SHOW_DOTFILES 44 #define C_SHOW_DOTFILES 62 #define L_BOOL_B 13 #define B_SELECT_POPUPS 5 #define C_SELECT_POPUPS 36 #define B_SHOW_CURSOR 44 #define C_SHOW_CURSOR 62 #define L_KEYPAD 14 #define L_LINEED 15 #define L_LAYOUT 16 #ifdef DIRED_SUPPORT #define L_DIRED 17 #define L_USER_MODE 18 #define L_USER_AGENT 19 #define L_EXEC 20 #else #define L_USER_MODE 17 #define L_USER_AGENT 18 #define L_EXEC 19 #endif /* DIRED_SUPPORT */ #define L_VERBOSE_IMAGES L_USER_MODE #define B_VERBOSE_IMAGES 50 #define C_VERBOSE_IMAGES (B_VERBOSE_IMAGES + 21) /* a kludge to add assume_charset only in ADVANCED mode... */ #define L_Bool_A (use_assume_charset ? L_BOOL_A + 1 : L_BOOL_A) #define L_Bool_B (use_assume_charset ? L_BOOL_B + 1 : L_BOOL_B) #define L_Exec (use_assume_charset ? L_EXEC + 1 : L_EXEC) #define L_Rawmode (use_assume_charset ? L_RAWMODE + 1 : L_RAWMODE) #define L_Charset (use_assume_charset ? L_CHARSET + 1 : L_CHARSET) #define L_Color (use_assume_charset ? L_COLOR + 1 : L_COLOR) #define L_Keypad (use_assume_charset ? L_KEYPAD + 1 : L_KEYPAD) #define L_Lineed (use_assume_charset ? L_LINEED + 1 : L_LINEED) #define L_Layout (use_assume_charset ? L_LAYOUT + 1 : L_LAYOUT) #define L_Dired (use_assume_charset ? L_DIRED + 1 : L_DIRED) #define L_User_Mode (use_assume_charset ? L_USER_MODE + 1 : L_USER_MODE) #define L_User_Agent (use_assume_charset ? L_USER_AGENT + 1 : L_USER_AGENT) #define LPAREN '(' #define RPAREN ')' static int add_it(char *text, int len) { if (len) { text[len] = '\0'; LYaddstr(text); } return 0; } /* * addlbl() is used instead of plain LYaddstr() in old-style options menu * to show hot keys in bold. */ static void addlbl(const char *text) { char actual[80]; int s, d; BOOL b = FALSE; for (s = d = 0; text[s]; s++) { actual[d++] = text[s]; if (text[s] == LPAREN) { d = add_it(actual, d - 1); lynx_start_bold(); b = TRUE; actual[d++] = text[s]; } else if (text[s] == RPAREN) { d = add_it(actual, d); lynx_stop_bold(); b = FALSE; } } add_it(actual, d); if (b) lynx_stop_bold(); } #if !defined(VMS) || defined(USE_SLANG) #define HANDLE_LYOPTIONS \ if (term_options) { \ term_options = FALSE; \ } else { \ AddValueAccepted = TRUE; \ } \ goto draw_options #else #define HANDLE_LYOPTIONS \ term_options = FALSE; \ if (use_assume_charset != old_use_assume_charset) \ goto draw_options #endif /* !VMS || USE_SLANG */ void LYoptions(void) { #define ShowBool(value) LYaddstr((value) ? "ON " : "OFF") static const char *bool_choices[] = { "OFF", "ON", NULL }; static const char *const caseless_choices[] = { "CASE INSENSITIVE", "CASE SENSITIVE", NULL }; #ifdef DIRED_SUPPORT static const char *dirList_choices[] = { "Directories first", "Files first", "Mixed style", NULL }; #endif #if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)) static const char *exec_choices[] = { "ALWAYS OFF", "FOR LOCAL FILES ONLY", #ifndef NEVER_ALLOW_REMOTE_EXEC "ALWAYS ON", #endif /* !NEVER_ALLOW_REMOTE_EXEC */ NULL }; #endif static const char *fileSort_choices[] = { "By Filename", "By Type", "By Size", "By Date", NULL }; static const char *keypad_choices[] = { "Numbers act as arrows", "Links are numbered", "Links and form fields are numbered", NULL }; static const char *mbm_choices[] = { "OFF ", "STANDARD", "ADVANCED", NULL }; static const char *userMode_choices[] = { "Novice", "Intermediate", "Advanced", NULL }; #if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)) int itmp; #endif /* ENABLE_OPTS_CHANGE_EXEC */ int response, ch; /* * If the user changes the display we need memory to put it in. */ bstring *my_data = NULL; char *choices[MAXCHOICES]; int CurrentCharSet = current_char_set; int CurrentAssumeCharSet = UCLYhndl_for_unspec; int CurrentShowColor = LYShowColor; BOOLEAN CurrentRawMode = LYRawMode; BOOLEAN AddValueAccepted = FALSE; BOOL use_assume_charset; #if defined(VMS) || defined(USE_SLANG) BOOL old_use_assume_charset; #endif #ifdef DIRED_SUPPORT #ifdef ENABLE_OPTS_CHANGE_EXEC if (LYlines < 24) { HTAlert(OPTION_SCREEN_NEEDS_24); return; } #else if (LYlines < 23) { HTAlert(OPTION_SCREEN_NEEDS_23); return; } #endif /* ENABLE_OPTS_CHANGE_EXEC */ #else #ifdef ENABLE_OPTS_CHANGE_EXEC if (LYlines < 23) { HTAlert(OPTION_SCREEN_NEEDS_23); return; } #else if (LYlines < 22) { HTAlert(OPTION_SCREEN_NEEDS_22); return; } #endif /* ENABLE_OPTS_CHANGE_EXEC */ #endif /* DIRED_SUPPORT */ term_options = FALSE; LYStatusLine = (LYlines - 1); /* screen is otherwise too crowded */ signal(SIGINT, terminate_options); if (no_option_save) { if (LYShowColor == SHOW_COLOR_NEVER) { LYShowColor = SHOW_COLOR_OFF; } else if (LYShowColor == SHOW_COLOR_ALWAYS) { LYShowColor = SHOW_COLOR_ON; } #if defined(USE_SLANG) || defined(COLOR_CURSES) } else { SetupChosenShowColor(); #endif /* USE_SLANG || COLOR_CURSES */ } use_assume_charset = (BOOLEAN) (user_mode == ADVANCED_MODE); draw_options: #if defined(VMS) || defined(USE_SLANG) old_use_assume_charset = use_assume_charset; #endif /* * NOTE that printw() should be avoided for strings that might have * non-ASCII or multibyte/CJK characters. - FM */ #if defined(FANCY_CURSES) || defined (USE_SLANG) if (enable_scrollback) { LYclear(); } else { LYerase(); } #else LYclear(); #endif /* FANCY_CURSES || USE_SLANG */ LYmove(0, 5); lynx_start_h1_color(); LYaddstr(" Options Menu ("); LYaddstr(LYNX_NAME); LYaddstr(" Version "); LYaddstr(LYNX_VERSION); LYaddch(')'); lynx_stop_h1_color(); LYmove(L_EDITOR, 5); addlbl("(E)ditor : "); LYaddstr(non_empty(editor) ? editor : "NONE"); LYmove(L_DISPLAY, 5); addlbl("(D)ISPLAY variable : "); LYaddstr(non_empty(x_display) ? x_display : "NONE"); LYmove(L_HOME, 5); addlbl("mu(L)ti-bookmarks: "); LYaddstr(mbm_choices[LYMultiBookmarks]); LYmove(L_HOME, B_BOOK); if (LYMultiBookmarks != MBM_OFF) { addlbl("review/edit (B)ookmarks files"); } else { addlbl("(B)ookmark file: "); LYaddstr(non_empty(bookmark_page) ? bookmark_page : "NONE"); } LYmove(L_FTPSTYPE, 5); addlbl("(F)TP sort criteria : "); LYaddstr((HTfileSortMethod == FILE_BY_NAME ? "By Filename" : (HTfileSortMethod == FILE_BY_SIZE ? "By Size " : (HTfileSortMethod == FILE_BY_TYPE ? "By Type " : "By Date ")))); LYmove(L_MAIL_ADDRESS, 5); addlbl("(P)ersonal mail address : "); LYaddstr(non_empty(personal_mail_address) ? personal_mail_address : "NONE"); LYmove(L_SSEARCH, 5); addlbl("(S)earching type : "); LYaddstr(LYcase_sensitive ? "CASE SENSITIVE " : "CASE INSENSITIVE"); LYmove(L_Charset, 5); addlbl("display (C)haracter set : "); LYaddstr(LYchar_set_names[current_char_set]); LYmove(L_LANGUAGE, 5); addlbl("preferred document lan(G)uage: "); LYaddstr(non_empty(language) ? language : "NONE"); LYmove(L_PREF_CHARSET, 5); addlbl("preferred document c(H)arset : "); LYaddstr(non_empty(pref_charset) ? pref_charset : "NONE"); if (use_assume_charset) { LYmove(L_ASSUME_CHARSET, 5); addlbl("(^A)ssume charset if unknown : "); if (UCAssume_MIMEcharset) LYaddstr(UCAssume_MIMEcharset); else LYaddstr((UCLYhndl_for_unspec >= 0) ? LYCharSet_UC[UCLYhndl_for_unspec].MIMEname : "NONE"); } LYmove(L_Rawmode, 5); addlbl("Raw 8-bit or CJK m(O)de : "); ShowBool(LYRawMode); #if defined(USE_SLANG) || defined(COLOR_CURSES) LYmove(L_Color, B_COLOR); addlbl("show color (&) : "); if (no_option_save) { ShowBool(LYShowColor == SHOW_COLOR_OFF); } else { switch (LYChosenShowColor) { case SHOW_COLOR_NEVER: LYaddstr("NEVER "); break; case SHOW_COLOR_OFF: LYaddstr("OFF"); break; case SHOW_COLOR_ON: LYaddstr("ON "); break; case SHOW_COLOR_ALWAYS: #if defined(COLOR_CURSES) if (!has_colors()) LYaddstr("Always try"); else #endif LYaddstr("ALWAYS "); } } #endif /* USE_SLANG || COLOR_CURSES */ LYmove(L_Bool_A, B_VIKEYS); addlbl("(V)I keys: "); ShowBool(vi_keys); LYmove(L_Bool_A, B_EMACSKEYS); addlbl("e(M)acs keys: "); ShowBool(emacs_keys); LYmove(L_Bool_A, B_SHOW_DOTFILES); addlbl("sho(W) dot files: "); ShowBool(!no_dotfiles && show_dotfiles); LYmove(L_Bool_B, B_SELECT_POPUPS); addlbl("popups for selec(T) fields : "); ShowBool(LYSelectPopups); LYmove(L_Bool_B, B_SHOW_CURSOR); addlbl("show cursor (@) : "); ShowBool(LYShowCursor); LYmove(L_Keypad, 5); addlbl("(K)eypad mode : "); LYaddstr((fields_are_numbered() && links_are_numbered()) ? "Links and form fields are numbered" : (links_are_numbered() ? "Links are numbered " : (fields_are_numbered() ? "Form fields are numbered " : "Numbers act as arrows "))); LYmove(L_Lineed, 5); addlbl("li(N)e edit style : "); LYaddstr(LYEditorNames[current_lineedit]); #ifdef EXP_KEYBOARD_LAYOUT LYmove(L_Layout, 5); addlbl("Ke(Y)board layout : "); LYaddstr(LYKbLayoutNames[current_layout]); #endif #ifdef DIRED_SUPPORT LYmove(L_Dired, 5); addlbl("l(I)st directory style : "); LYaddstr((dir_list_style == FILES_FIRST) ? "Files first " : ((dir_list_style == MIXED_STYLE) ? "Mixed style " : "Directories first")); #endif /* DIRED_SUPPORT */ LYmove(L_User_Mode, 5); addlbl("(U)ser mode : "); LYaddstr((user_mode == NOVICE_MODE) ? "Novice " : ((user_mode == INTERMEDIATE_MODE) ? "Intermediate" : "Advanced ")); addlbl(" verbose images (!) : "); ShowBool(verbose_img); LYmove(L_User_Agent, 5); addlbl("user (A)gent : "); LYaddstr(non_empty(LYUserAgent) ? LYUserAgent : "NONE"); #if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)) LYmove(L_Exec, 5); addlbl("local e(X)ecution links : "); #ifndef NEVER_ALLOW_REMOTE_EXEC LYaddstr(local_exec ? "ALWAYS ON " : (local_exec_on_local_files ? "FOR LOCAL FILES ONLY" : "ALWAYS OFF ")); #else LYaddstr(local_exec_on_local_files ? "FOR LOCAL FILES ONLY" : "ALWAYS OFF "); #endif /* !NEVER_ALLOW_REMOTE_EXEC */ #endif /* ENABLE_OPTS_CHANGE_EXEC */ LYmove(LYlines - 3, 2); LYaddstr(SELECT_SEGMENT); lynx_start_bold(); LYaddstr(CAP_LETT_SEGMENT); lynx_stop_bold(); LYaddstr(OF_OPT_LINE_SEGMENT); if (!no_option_save) { LYaddstr(" '"); lynx_start_bold(); LYaddstr(">"); lynx_stop_bold(); LYaddstr("'"); LYaddstr(TO_SAVE_SEGMENT); } LYaddstr(OR_SEGMENT); LYaddstr("'"); lynx_start_bold(); LYaddstr("r"); lynx_stop_bold(); LYaddstr("'"); LYaddstr(TO_RETURN_SEGMENT); response = 0; while (response != 'R' && !LYisNonAlnumKeyname(response, LYK_PREV_DOC) && response != '>' && !term_options && !LYCharIsINTERRUPT_NO_letter(response)) { if (AddValueAccepted == TRUE) { _statusline(VALUE_ACCEPTED); AddValueAccepted = FALSE; } LYmove((LYlines - 2), 0); lynx_start_prompt_color(); LYaddstr(COMMAND_PROMPT); lynx_stop_prompt_color(); LYrefresh(); response = LYgetch_single(); if (term_options || LYCharIsINTERRUPT_NO_letter(response)) response = 'R'; if (LYisNonAlnumKeyname(response, LYK_REFRESH)) { lynx_force_repaint(); goto draw_options; } switch (response) { case 'E': /* Change the editor. */ if (no_editor) { _statusline(EDIT_DISABLED); } else if (system_editor) { _statusline(EDITOR_LOCKED); } else { if (non_empty(editor)) { BStrCopy0(my_data, editor); } else { /* clear the NONE */ LYmove(L_EDITOR, COL_OPTION_VALUES); LYaddstr(" "); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA); LYmove(L_EDITOR, COL_OPTION_VALUES); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_EDITOR, COL_OPTION_VALUES); if (term_options || ch == -1) { LYaddstr(non_empty(editor) ? editor : "NONE"); } else if (isBEmpty(my_data)) { FREE(editor); LYaddstr("NONE"); } else { StrAllocCopy(editor, my_data->str); LYaddstr(editor); } LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } } response = ' '; break; case 'D': /* Change the display. */ if (non_empty(x_display)) { BStrCopy0(my_data, x_display); } else { /* clear the NONE */ LYmove(L_DISPLAY, COL_OPTION_VALUES); LYaddstr(" "); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA); LYmove(L_DISPLAY, COL_OPTION_VALUES); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_DISPLAY, COL_OPTION_VALUES); #ifdef VMS #define CompareEnvVars(a,b) strcasecomp(a, b) #else #define CompareEnvVars(a,b) strcmp(a, b) #endif /* VMS */ if ((term_options || ch == -1) || (x_display != NULL && !CompareEnvVars(x_display, my_data->str))) { /* * Cancelled, or a non-NULL display string wasn't changed. - * FM */ LYaddstr(non_empty(x_display) ? x_display : "NONE"); LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } response = ' '; break; } else if (isBEmpty(my_data)) { if ((x_display == NULL) || (x_display != NULL && *x_display == '\0')) { /* * NULL or zero-length display string wasn't changed. - FM */ LYaddstr("NONE"); LYclrtoeol(); _statusline(VALUE_ACCEPTED); response = ' '; break; } } /* * Set the new DISPLAY variable. - FM */ LYsetXDisplay(my_data->str); validate_x_display(); LYaddstr(x_display ? x_display : "NONE"); LYclrtoeol(); summarize_x_display(my_data->str); response = ' '; break; case 'L': /* Change multibookmarks option. */ if (LYMBMBlocked) { _statusline(MULTIBOOKMARKS_DISALLOWED); response = ' '; break; } if (!LYSelectPopups) { LYMultiBookmarks = LYChooseEnum(LYMultiBookmarks, L_HOME, C_MULTI, mbm_choices); } else { LYMultiBookmarks = LYChoosePopup(LYMultiBookmarks, L_HOME, (C_MULTI - 1), mbm_choices, 3, FALSE, FALSE); } #if defined(VMS) || defined(USE_SLANG) if (LYSelectPopups) { LYmove(L_HOME, C_MULTI); LYclrtoeol(); LYaddstr(mbm_choices[LYMultiBookmarks]); } #endif /* VMS || USE_SLANG */ #if !defined(VMS) && !defined(USE_SLANG) if (!LYSelectPopups) #endif /* !VMS && !USE_SLANG */ { LYmove(L_HOME, B_BOOK); LYclrtoeol(); if (LYMultiBookmarks != MBM_OFF) { LYaddstr(gettext("review/edit B)ookmarks files")); } else { LYaddstr(gettext("B)ookmark file: ")); LYaddstr(non_empty(bookmark_page) ? bookmark_page : "NONE"); } } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; case 'B': /* Change the bookmark page location. */ /* * Anonymous users should not be allowed to change the bookmark * page. */ if (!no_bookmark) { if (LYMultiBookmarks != MBM_OFF) { edit_bookmarks(); signal(SIGINT, terminate_options); goto draw_options; } if (non_empty(bookmark_page)) { BStrCopy0(my_data, bookmark_page); } else { /* clear the NONE */ LYmove(L_HOME, C_DEFAULT); LYclrtoeol(); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA); LYmove(L_HOME, C_DEFAULT); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_HOME, C_DEFAULT); BStrAlloc(my_data, my_data->len + LY_MAXPATH); /* lengthen */ if (term_options || ch == -1 || isBEmpty(my_data)) { LYaddstr(non_empty(bookmark_page) ? bookmark_page : "NONE"); } else if (!LYPathOffHomeOK(my_data->str, (size_t) my_data->len)) { LYaddstr(non_empty(bookmark_page) ? bookmark_page : "NONE"); LYclrtoeol(); _statusline(USE_PATH_OFF_HOME); response = ' '; break; } else { StrAllocCopy(bookmark_page, my_data->str); StrAllocCopy(MBM_A_subbookmark[0], bookmark_page); LYaddstr(bookmark_page); } LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } } else { /* anonymous */ _statusline(BOOKMARK_CHANGE_DISALLOWED); } response = ' '; break; case 'F': /* Change ftp directory sorting. */ if (!LYSelectPopups) { HTfileSortMethod = LYChooseEnum(HTfileSortMethod, L_FTPSTYPE, -1, fileSort_choices); } else { HTfileSortMethod = LYChoosePopup(HTfileSortMethod, L_FTPSTYPE, -1, fileSort_choices, 4, FALSE, FALSE); #if defined(VMS) || defined(USE_SLANG) LYmove(L_FTPSTYPE, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(fileSort_choices[HTfileSortMethod]); #endif /* VMS || USE_SLANG */ } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; case 'P': /* Change personal mail address for From headers. */ if (non_empty(personal_mail_address)) { BStrCopy0(my_data, personal_mail_address); } else { /* clear the NONE */ LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES); LYaddstr(" "); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA); LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES); if (term_options || ch == -1) { LYaddstr((personal_mail_address && *personal_mail_address) ? personal_mail_address : "NONE"); } else if (isBEmpty(my_data)) { FREE(personal_mail_address); LYaddstr("NONE"); } else { StrAllocCopy(personal_mail_address, my_data->str); LYaddstr(personal_mail_address); } LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } response = ' '; break; case 'S': /* Change case sensitivity for searches. */ LYcase_sensitive = LYChooseBoolean(LYcase_sensitive, L_SSEARCH, -1, caseless_choices); response = ' '; break; case '\001': /* Change assume_charset setting. */ if (use_assume_charset) { int i, curval; const char **assume_list; assume_list = typecallocn(const char *, (unsigned) (LYNumCharsets + 1)); if (!assume_list) { outofmem(__FILE__, "options"); } for (i = 0; i < LYNumCharsets; i++) { assume_list[i] = LYCharSet_UC[i].MIMEname; } curval = UCLYhndl_for_unspec; if (curval == current_char_set && UCAssume_MIMEcharset) { curval = UCGetLYhndl_byMIME(UCAssume_MIMEcharset); } if (curval < 0) curval = LYRawMode ? current_char_set : 0; if (!LYSelectPopups) { #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN UCLYhndl_for_unspec = assumed_doc_charset_map[(LYChooseEnum(charset_subsets[curval].assumed_idx, L_ASSUME_CHARSET, -1, assumed_charset_choices) ? 1 : 0)]; #else UCLYhndl_for_unspec = LYChooseEnum(curval, L_ASSUME_CHARSET, -1, assume_list); #endif } else { #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN UCLYhndl_for_unspec = assumed_doc_charset_map[(LYChoosePopup(charset_subsets[curval].assumed_idx, L_ASSUME_CHARSET, -1, assumed_charset_choices, 0, FALSE, FALSE) ? 1 : 0)]; #else UCLYhndl_for_unspec = LYChoosePopup(curval, L_ASSUME_CHARSET, -1, assume_list, 0, FALSE, FALSE); #endif #if defined(VMS) || defined(USE_SLANG) LYmove(L_ASSUME_CHARSET, COL_OPTION_VALUES); LYclrtoeol(); if (UCLYhndl_for_unspec >= 0) LYaddstr(LYCharSet_UC[UCLYhndl_for_unspec].MIMEname); #endif /* VMS || USE_SLANG */ } /* * Set the raw 8-bit or CJK mode defaults and character set if * changed. - FM */ if (CurrentAssumeCharSet != UCLYhndl_for_unspec || UCLYhndl_for_unspec != curval) { if (UCLYhndl_for_unspec != CurrentAssumeCharSet) { StrAllocCopy(UCAssume_MIMEcharset, LYCharSet_UC[UCLYhndl_for_unspec].MIMEname); } if (HTCJK != JAPANESE) LYRawMode = (BOOLEAN) (UCLYhndl_for_unspec == current_char_set); HTMLSetUseDefaultRawMode(current_char_set, LYRawMode); HTMLSetCharacterHandling(current_char_set); CurrentAssumeCharSet = UCLYhndl_for_unspec; CurrentRawMode = LYRawMode; #if !defined(VMS) && !defined(USE_SLANG) if (!LYSelectPopups) #endif /* !VMS && !USE_SLANG */ { LYmove(L_Rawmode, COL_OPTION_VALUES); LYclrtoeol(); ShowBool(LYRawMode); } } FREE(assume_list); response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } } else { _statusline(NEED_ADVANCED_USER_MODE); AddValueAccepted = FALSE; } break; case 'C': /* Change display charset setting. */ if (!LYSelectPopups) { #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN displayed_display_charset_idx = LYChooseEnum(displayed_display_charset_idx, L_Charset, -1, display_charset_choices); current_char_set = display_charset_map[displayed_display_charset_idx]; #else current_char_set = LYChooseEnum(current_char_set, L_Charset, -1, LYchar_set_names); #endif } else { #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN displayed_display_charset_idx = LYChoosePopup(displayed_display_charset_idx, L_Charset, -1, display_charset_choices, 0, FALSE, FALSE); current_char_set = display_charset_map[displayed_display_charset_idx]; #else current_char_set = LYChoosePopup(current_char_set, L_Charset, -1, LYchar_set_names, 0, FALSE, FALSE); #endif #if defined(VMS) || defined(USE_SLANG) LYmove(L_Charset, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(LYchar_set_names[current_char_set]); #endif /* VMS || USE_SLANG */ } /* * Set the raw 8-bit or CJK mode defaults and character set if * changed. - FM */ if (CurrentCharSet != current_char_set) { LYUseDefaultRawMode = TRUE; HTMLUseCharacterSet(current_char_set); CurrentCharSet = current_char_set; CurrentRawMode = LYRawMode; #if !defined(VMS) && !defined(USE_SLANG) if (!LYSelectPopups) #endif /* !VMS && !USE_SLANG */ { LYmove(L_Rawmode, COL_OPTION_VALUES); LYclrtoeol(); ShowBool(LYRawMode); } #ifdef CAN_SWITCH_DISPLAY_CHARSET /* Deduce whether the user wants autoswitch: */ switch_display_charsets = (current_char_set == auto_display_charset || current_char_set == auto_other_display_charset); #endif } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; case 'O': /* Change raw mode setting. */ LYRawMode = LYChooseBoolean(LYRawMode, L_Rawmode, -1, bool_choices); /* * Set the LYUseDefaultRawMode value and character handling if * LYRawMode was changed. - FM */ if (CurrentRawMode != LYRawMode) { HTMLSetUseDefaultRawMode(current_char_set, LYRawMode); HTMLSetCharacterHandling(current_char_set); CurrentRawMode = LYRawMode; } response = ' '; break; case 'G': /* Change language preference. */ if (non_empty(language)) { BStrCopy0(my_data, language); } else { /* clear the NONE */ LYmove(L_LANGUAGE, COL_OPTION_VALUES); LYaddstr(" "); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA); LYmove(L_LANGUAGE, COL_OPTION_VALUES); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_LANGUAGE, COL_OPTION_VALUES); if (term_options || ch == -1) { LYaddstr(non_empty(language) ? language : "NONE"); } else if (isBEmpty(my_data)) { FREE(language); LYaddstr("NONE"); } else { StrAllocCopy(language, my_data->str); LYaddstr(language); } LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } response = ' '; break; case 'H': /* Change charset preference. */ if (non_empty(pref_charset)) { BStrCopy0(my_data, pref_charset); } else { /* clear the NONE */ LYmove(L_PREF_CHARSET, COL_OPTION_VALUES); LYaddstr(" "); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA); LYmove(L_PREF_CHARSET, COL_OPTION_VALUES); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_PREF_CHARSET, COL_OPTION_VALUES); if (term_options || ch == -1) { LYaddstr(non_empty(pref_charset) ? pref_charset : "NONE"); } else if (isBEmpty(my_data)) { FREE(pref_charset); LYaddstr("NONE"); } else { StrAllocCopy(pref_charset, my_data->str); LYaddstr(pref_charset); } LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } response = ' '; break; case 'V': /* Change VI keys setting. */ vi_keys = LYChooseBoolean(vi_keys, L_Bool_A, C_VIKEYS, bool_choices); if (vi_keys) { set_vi_keys(); } else { reset_vi_keys(); } response = ' '; break; case 'M': /* Change emacs keys setting. */ emacs_keys = LYChooseBoolean(emacs_keys, L_Bool_A, C_EMACSKEYS, bool_choices); if (emacs_keys) { set_emacs_keys(); } else { reset_emacs_keys(); } response = ' '; break; case 'W': /* Change show dotfiles setting. */ if (no_dotfiles) { _statusline(DOTFILE_ACCESS_DISABLED); } else { show_dotfiles = LYChooseBoolean(show_dotfiles, L_Bool_A, C_SHOW_DOTFILES, bool_choices); } response = ' '; break; case 'T': /* Change select popups setting. */ LYSelectPopups = LYChooseBoolean(LYSelectPopups, L_Bool_B, C_SELECT_POPUPS, bool_choices); response = ' '; break; #if defined(USE_SLANG) || defined(COLOR_CURSES) case '&': /* Change show color setting. */ if (no_option_save) { #if defined(COLOR_CURSES) if (!has_colors()) { char *terminal = LYGetEnv("TERM"); if (terminal) HTUserMsg2(COLOR_TOGGLE_DISABLED_FOR_TERM, terminal); else HTUserMsg(COLOR_TOGGLE_DISABLED); break; } #endif LYShowColor = LYChooseEnum((LYShowColor - 1), L_Color, C_COLOR, bool_choices); if (LYShowColor == 0) { LYShowColor = SHOW_COLOR_OFF; } else { LYShowColor = SHOW_COLOR_ON; } } else { /* !no_option_save */ BOOLEAN again = FALSE; int chosen; /* * Copy strings into choice array. */ choices[0] = NULL; StrAllocCopy(choices[0], "NEVER "); choices[1] = NULL; StrAllocCopy(choices[1], "OFF "); choices[2] = NULL; StrAllocCopy(choices[2], "ON "); choices[3] = NULL; #if defined(COLOR_CURSES) if (!has_colors()) StrAllocCopy(choices[3], "Always try"); else #endif StrAllocCopy(choices[3], "ALWAYS "); choices[4] = NULL; do { if (!LYSelectPopups) { chosen = LYChooseEnum(LYChosenShowColor, L_Color, C_COLOR, choices); } else { chosen = LYChoosePopup(LYChosenShowColor, L_Color, C_COLOR, choices, 4, FALSE, FALSE); } #if defined(COLOR_CURSES) again = (BOOLEAN) (chosen == SHOW_COLOR_ON && !has_colors()); if (again) { char *terminal = LYGetEnv("TERM"); if (terminal) HTUserMsg2(COLOR_TOGGLE_DISABLED_FOR_TERM, terminal); else HTUserMsg(COLOR_TOGGLE_DISABLED); } #endif } while (again); LYChosenShowColor = chosen; #if defined(VMS) if (LYSelectPopups) { LYmove(L_Color, C_COLOR); LYclrtoeol(); LYaddstr(choices[LYChosenShowColor]); } #endif /* VMS */ #if defined(COLOR_CURSES) if (has_colors()) #endif LYShowColor = chosen; FREE(choices[0]); FREE(choices[1]); FREE(choices[2]); FREE(choices[3]); } if (CurrentShowColor != LYShowColor) { lynx_force_repaint(); } CurrentShowColor = LYShowColor; #ifdef USE_SLANG SLtt_Use_Ansi_Colors = (LYShowColor > SHOW_COLOR_OFF ? TRUE : FALSE); #endif response = ' '; if (LYSelectPopups && !no_option_save) { HANDLE_LYOPTIONS; } break; #endif /* USE_SLANG or COLOR_CURSES */ case '@': /* Change show cursor setting. */ LYShowCursor = LYChooseBoolean(LYShowCursor, L_Bool_B, C_SHOW_CURSOR, bool_choices); response = ' '; break; case 'K': /* Change keypad mode. */ if (!LYSelectPopups) { keypad_mode = LYChooseEnum(keypad_mode, L_Keypad, -1, keypad_choices); } else { keypad_mode = LYChoosePopup(keypad_mode, L_Keypad, -1, keypad_choices, 3, FALSE, FALSE); #if defined(VMS) || defined(USE_SLANG) LYmove(L_Keypad, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(keypad_choices[keypad_mode]); #endif /* VMS || USE_SLANG */ } if (keypad_mode == NUMBERS_AS_ARROWS) { set_numbers_as_arrows(); } else { reset_numbers_as_arrows(); } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; case 'N': /* Change line editor key bindings. */ if (!LYSelectPopups) { current_lineedit = LYChooseEnum(current_lineedit, L_Lineed, -1, LYEditorNames); } else { current_lineedit = LYChoosePopup(current_lineedit, L_Lineed, -1, LYEditorNames, 0, FALSE, FALSE); #if defined(VMS) || defined(USE_SLANG) LYmove(L_Lineed, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(LYEditorNames[current_lineedit]); #endif /* VMS || USE_SLANG */ } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; #ifdef EXP_KEYBOARD_LAYOUT case 'Y': /* Change keyboard layout */ if (!LYSelectPopups) { current_layout = LYChooseEnum(current_layout, L_Layout, -1, LYKbLayoutNames); } else { current_layout = LYChoosePopup(current_layout, L_Layout, -1, LYKbLayoutNames, 0, FALSE, FALSE); #if defined(VMS) || defined(USE_SLANG) LYmove(L_Layout, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(LYKbLayoutNames[current_layout]); #endif /* VMS || USE_SLANG */ } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; #endif /* EXP_KEYBOARD_LAYOUT */ #ifdef DIRED_SUPPORT case 'I': /* Change local directory sorting. */ if (!LYSelectPopups) { dir_list_style = LYChooseEnum(dir_list_style, L_Dired, -1, dirList_choices); } else { dir_list_style = LYChoosePopup(dir_list_style, L_Dired, -1, dirList_choices, 3, FALSE, FALSE); #if defined(VMS) || defined(USE_SLANG) LYmove(L_Dired, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(dirList_choices[dir_list_style]); #endif /* VMS || USE_SLANG */ } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; #endif /* DIRED_SUPPORT */ case 'U': /* Change user mode. */ if (!LYSelectPopups) { user_mode = LYChooseEnum(user_mode, L_User_Mode, -1, userMode_choices); use_assume_charset = (BOOLEAN) (user_mode >= 2); } else { user_mode = LYChoosePopup(user_mode, L_User_Mode, -1, userMode_choices, 3, FALSE, FALSE); use_assume_charset = (BOOLEAN) (user_mode >= 2); #if defined(VMS) || defined(USE_SLANG) if (use_assume_charset == old_use_assume_charset) { LYmove(L_User_Mode, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(userMode_choices[user_mode]); } #endif /* VMS || USE_SLANG */ } LYSetDisplayLines(); response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; case '!': if (!LYSelectPopups) { verbose_img = LYChooseBoolean(verbose_img, L_VERBOSE_IMAGES, C_VERBOSE_IMAGES, bool_choices); } else { verbose_img = (BOOLEAN) LYChoosePopup(verbose_img, L_VERBOSE_IMAGES, C_VERBOSE_IMAGES, bool_choices, 2, FALSE, FALSE); } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; case 'A': /* Change user agent string. */ if (!no_useragent) { if (non_empty(LYUserAgent)) { BStrCopy0(my_data, LYUserAgent); } else { /* clear the NONE */ LYmove(L_HOME, COL_OPTION_VALUES); LYaddstr(" "); BStrCopy0(my_data, ""); } _statusline(ACCEPT_DATA_OR_DEFAULT); LYmove(L_User_Agent, COL_OPTION_VALUES); lynx_start_bold(); ch = LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); LYmove(L_User_Agent, COL_OPTION_VALUES); if (term_options || ch == -1) { LYaddstr((LYUserAgent && *LYUserAgent) ? LYUserAgent : "NONE"); } else if (isBEmpty(my_data)) { StrAllocCopy(LYUserAgent, LYUserAgentDefault); LYaddstr((LYUserAgent && *LYUserAgent) ? LYUserAgent : "NONE"); } else { StrAllocCopy(LYUserAgent, my_data->str); LYaddstr(LYUserAgent); } LYclrtoeol(); if (ch == -1) { HTInfoMsg(CANCELLED); HTInfoMsg(""); } else if (!LYCheckUserAgent()) { _statusline(UA_PLEASE_USE_LYNX); } else { _statusline(VALUE_ACCEPTED); } } else { /* disallowed */ _statusline(UA_CHANGE_DISABLED); } response = ' '; break; #if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)) case 'X': /* Change local exec restriction. */ if (exec_frozen && !LYSelectPopups) { _statusline(CHANGE_OF_SETTING_DISALLOWED); response = ' '; break; } #ifndef NEVER_ALLOW_REMOTE_EXEC if (local_exec) { itmp = 2; } else #endif /* !NEVER_ALLOW_REMOTE_EXEC */ { if (local_exec_on_local_files) { itmp = 1; } else { itmp = 0; } } if (!LYSelectPopups) { itmp = LYChooseEnum(itmp, L_Exec, -1, exec_choices); } else { itmp = LYChoosePopup(itmp, L_Exec, -1, exec_choices, 0, (exec_frozen ? TRUE : FALSE), FALSE); #if defined(VMS) || defined(USE_SLANG) LYmove(L_Exec, COL_OPTION_VALUES); LYclrtoeol(); LYaddstr(exec_choices[itmp]); #endif /* VMS || USE_SLANG */ } if (!exec_frozen) { switch (itmp) { case 0: local_exec = FALSE; local_exec_on_local_files = FALSE; break; case 1: local_exec = FALSE; local_exec_on_local_files = TRUE; break; #ifndef NEVER_ALLOW_REMOTE_EXEC case 2: local_exec = TRUE; local_exec_on_local_files = FALSE; break; #endif /* !NEVER_ALLOW_REMOTE_EXEC */ } /* end switch */ } response = ' '; if (LYSelectPopups) { HANDLE_LYOPTIONS; } break; #endif /* ENABLE_OPTS_CHANGE_EXEC */ case '>': /* Save current options to RC file. */ if (!no_option_save) { HTInfoMsg(SAVING_OPTIONS); LYrcShowColor = LYChosenShowColor; if (save_rc(NULL)) { HTInfoMsg(OPTIONS_SAVED); } else { HTAlert(OPTIONS_NOT_SAVED); } } else { HTInfoMsg(R_TO_RETURN_TO_LYNX); /* * Change response so that we don't exit the options menu. */ response = ' '; } break; case 'R': /* Return to document (quit options menu). */ break; default: if (!no_option_save) { HTInfoMsg(SAVE_OR_R_TO_RETURN_TO_LYNX); } else { HTInfoMsg(R_TO_RETURN_TO_LYNX); } } /* end switch */ } /* end while */ term_options = FALSE; LYStatusLine = -1; /* let user_mode have some of the screen */ signal(SIGINT, cleanup_sig); BStrFree(my_data); return; } static int widest_choice(STRING2PTR choices) { int n, width = 0; for (n = 0; choices[n] != NULL; ++n) { int len = (int) strlen(choices[n]); if (width < len) width = len; } return width; } static void show_choice(const char *choice, int width) { int len = (int) strlen(choice); LYaddstr(choice); while (len++ < width) LYaddch(' '); } /* * Take a status code, prompt the user for a new status, and return it. */ static int boolean_choice(int cur_choice, int line, int column, STRING2PTR choices) { int response = 0; int cmd = 0; int number = 0; int col = (column >= 0 ? column : COL_OPTION_VALUES); int orig_choice = cur_choice; int width = widest_choice(choices); /* * Get the number of choices and then make number zero-based. */ for (number = 0; choices[number] != NULL; number++) ; /* empty loop body */ number--; /* * Update the statusline. */ _statusline(ANY_KEY_CHANGE_RET_ACCEPT); /* * Highlight the current choice. */ LYmove(line, col); lynx_start_reverse(); show_choice(choices[cur_choice], width); if (LYShowCursor) LYmove(line, (col - 1)); LYrefresh(); /* * Get the keyboard entry, and leave the cursor at the choice, to indicate * that it can be changed, until the user accepts the current choice. */ term_options = FALSE; while (1) { LYmove(line, col); if (term_options == FALSE) { response = LYgetch_single(); } if (term_options || LYCharIsINTERRUPT_NO_letter(response)) { /* * Control-C or Control-G. */ response = '\n'; term_options = TRUE; cur_choice = orig_choice; } #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; response = '\n'; term_options = TRUE; cur_choice = orig_choice; } #endif /* VMS */ if ((response != '\n' && response != '\r') && (cmd = LKC_TO_LAC(keymap, response)) != LYK_ACTIVATE) { switch (cmd) { case LYK_HOME: cur_choice = 0; break; case LYK_END: cur_choice = number; break; case LYK_REFRESH: lynx_force_repaint(); LYrefresh(); break; case LYK_QUIT: case LYK_ABORT: case LYK_PREV_DOC: cur_choice = orig_choice; term_options = TRUE; break; case LYK_PREV_PAGE: case LYK_UP_HALF: case LYK_UP_TWO: case LYK_PREV_LINK: case LYK_LPOS_PREV_LINK: case LYK_FASTBACKW_LINK: case LYK_UP_LINK: case LYK_LEFT_LINK: if (cur_choice == 0) cur_choice = number; /* go back to end */ else cur_choice--; break; case LYK_1: case LYK_2: case LYK_3: case LYK_4: case LYK_5: case LYK_6: case LYK_7: case LYK_8: case LYK_9: if ((cmd - LYK_1 + 1) <= number) { cur_choice = cmd - LYK_1 + 1; break; } /* else fall through! */ default: if (cur_choice == number) cur_choice = 0; /* go over the top and around */ else cur_choice++; } /* end of switch */ show_choice(choices[cur_choice], width); if (LYShowCursor) LYmove(line, (col - 1)); LYrefresh(); } else { /* * Unhighlight choice. */ LYmove(line, col); lynx_stop_reverse(); show_choice(choices[cur_choice], width); if (term_options) { term_options = FALSE; HTInfoMsg(CANCELLED); HTInfoMsg(""); } else { _statusline(VALUE_ACCEPTED); } return cur_choice; } } } #endif /* !NO_OPTION_MENU */ static void terminate_options(int sig GCC_UNUSED) { term_options = TRUE; /* * Reassert the AST. */ signal(SIGINT, terminate_options); #ifdef VMS /* * Refresh the screen to get rid of the "interrupt" message. */ if (!dump_output_immediately) { lynx_force_repaint(); LYrefresh(); } #endif /* VMS */ } /* * Multi-Bookmark On-Line editing support. - FMG & FM */ void edit_bookmarks(void) { int response = 0, def_response = 0; int MBM_current = 1; #define MULTI_OFFSET 8 int a; /* misc counter */ bstring *my_data = NULL; /* * We need (MBM_V_MAXFILES + MULTI_OFFSET) lines to display the whole list * at once. Otherwise break it up into two segments. We know it won't be * less than that because 'o'ptions needs 23-24 at LEAST. */ term_options = FALSE; signal(SIGINT, terminate_options); draw_bookmark_list: /* * Display menu of bookmarks. NOTE that we avoid printw()'s to increase * the chances that any non-ASCII or multibyte/CJK characters will be * handled properly. - FM */ #if defined(FANCY_CURSES) || defined (USE_SLANG) if (enable_scrollback) { LYclear(); } else { LYerase(); } #else LYclear(); #endif /* FANCY_CURSES || USE_SLANG */ LYmove(0, 5); lynx_start_h1_color(); if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) { char *ehead_buffer = 0; HTSprintf0(&ehead_buffer, MULTIBOOKMARKS_EHEAD_MASK, MBM_current); LYaddstr(ehead_buffer); FREE(ehead_buffer); } else { LYaddstr(MULTIBOOKMARKS_EHEAD); } lynx_stop_h1_color(); if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) { for (a = ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)); a <= (MBM_current * MBM_V_MAXFILES / 2); a++) { LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 5); LYaddch(UCH(LYindex2MBM(a))); LYaddstr(" : "); if (MBM_A_subdescript[a]) LYaddstr(MBM_A_subdescript[a]); LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 35); LYaddstr("| "); if (MBM_A_subbookmark[a]) { LYaddstr(MBM_A_subbookmark[a]); } } } else { for (a = 0; a <= MBM_V_MAXFILES; a++) { LYmove(3 + a, 5); LYaddch(UCH(LYindex2MBM(a))); LYaddstr(" : "); if (MBM_A_subdescript[a]) LYaddstr(MBM_A_subdescript[a]); LYmove(3 + a, 35); LYaddstr("| "); if (MBM_A_subbookmark[a]) { LYaddstr(MBM_A_subbookmark[a]); } } } /* * Only needed when we have 2 screens. */ if (LYlines < MBM_V_MAXFILES + MULTI_OFFSET) { LYmove((LYlines - 4), 0); LYaddstr("'"); lynx_start_bold(); LYaddstr("["); lynx_stop_bold(); LYaddstr("' "); LYaddstr(PREVIOUS); LYaddstr(", '"); lynx_start_bold(); LYaddstr("]"); lynx_stop_bold(); LYaddstr("' "); LYaddstr(NEXT_SCREEN); } LYmove((LYlines - 3), 0); if (!no_option_save) { LYaddstr("'"); lynx_start_bold(); LYaddstr(">"); lynx_stop_bold(); LYaddstr("'"); LYaddstr(TO_SAVE_SEGMENT); } LYaddstr(OR_SEGMENT); LYaddstr("'"); lynx_start_bold(); LYaddstr("^G"); lynx_stop_bold(); LYaddstr("'"); LYaddstr(TO_RETURN_SEGMENT); while (!term_options && !LYisNonAlnumKeyname(response, LYK_PREV_DOC) && !LYCharIsINTERRUPT_NO_letter(response) && response != '>') { LYmove((LYlines - 2), 0); lynx_start_prompt_color(); LYaddstr(MULTIBOOKMARKS_LETTER); lynx_stop_prompt_color(); LYrefresh(); response = (def_response ? def_response : LYgetch_single()); def_response = 0; /* * Check for a cancel. */ if (term_options || LYCharIsINTERRUPT_NO_letter(response) || LYisNonAlnumKeyname(response, LYK_PREV_DOC)) continue; /* * Check for a save. */ if (response == '>') { if (!no_option_save) { HTInfoMsg(SAVING_OPTIONS); if (save_rc(NULL)) HTInfoMsg(OPTIONS_SAVED); else HTAlert(OPTIONS_NOT_SAVED); } else { HTInfoMsg(R_TO_RETURN_TO_LYNX); /* * Change response so that we don't exit the options menu. */ response = ' '; } continue; } /* * Check for a refresh. */ if (LYisNonAlnumKeyname(response, LYK_REFRESH)) { lynx_force_repaint(); continue; } /* * Move between the screens - if we can't show it all at once. */ if ((response == ']' || LYisNonAlnumKeyname(response, LYK_NEXT_PAGE)) && LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) { MBM_current++; if (MBM_current >= 3) MBM_current = 1; goto draw_bookmark_list; } if ((response == '[' || LYisNonAlnumKeyname(response, LYK_PREV_PAGE)) && LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) { MBM_current--; if (MBM_current <= 0) MBM_current = 2; goto draw_bookmark_list; } /* * Instead of using 26 case statements, we set up a scan through the * letters and edit the lines that way. */ for (a = 0; a <= MBM_V_MAXFILES; a++) { if (LYMBM2index(response) == a) { if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) { if (MBM_current == 1 && a > (MBM_V_MAXFILES / 2)) { MBM_current = 2; def_response = response; goto draw_bookmark_list; } if (MBM_current == 2 && a < (MBM_V_MAXFILES / 2)) { MBM_current = 1; def_response = response; goto draw_bookmark_list; } } _statusline(ACCEPT_DATA); if (a > 0) { lynx_start_bold(); if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 9); else LYmove((3 + a), 9); BStrCopy0(my_data, (!MBM_A_subdescript[a] ? "" : MBM_A_subdescript[a])); (void) LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); if (isBEmpty(my_data)) { FREE(MBM_A_subdescript[a]); } else { StrAllocCopy(MBM_A_subdescript[a], my_data->str); } if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 5); else LYmove((3 + a), 5); LYaddch(UCH(LYindex2MBM(a))); LYaddstr(" : "); if (MBM_A_subdescript[a]) LYaddstr(MBM_A_subdescript[a]); LYclrtoeol(); LYrefresh(); } if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 35); else LYmove((3 + a), 35); LYaddstr("| "); lynx_start_bold(); BStrCopy0(my_data, NonNull(MBM_A_subbookmark[a])); (void) LYgetBString(&my_data, FALSE, 0, NORECALL); lynx_stop_bold(); if (isBEmpty(my_data)) { if (a == 0) StrAllocCopy(MBM_A_subbookmark[a], bookmark_page); else FREE(MBM_A_subbookmark[a]); } else { BStrAlloc(my_data, my_data->len + LY_MAXPATH); if (!LYPathOffHomeOK(my_data->str, (size_t) my_data->len)) { LYMBM_statusline(USE_PATH_OFF_HOME); LYSleepAlert(); } else { StrAllocCopy(MBM_A_subbookmark[a], my_data->str); if (a == 0) { StrAllocCopy(bookmark_page, MBM_A_subbookmark[a]); } } } if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 35); else LYmove((3 + a), 35); LYaddstr("| "); if (MBM_A_subbookmark[a]) LYaddstr(MBM_A_subbookmark[a]); LYclrtoeol(); LYParkCursor(); break; } } /* end for */ } /* end while */ BStrFree(my_data); term_options = FALSE; signal(SIGINT, cleanup_sig); } #if defined(USE_CURSES_PADS) || !defined(NO_OPTION_MENU) || (defined(USE_MOUSE) && (defined(NCURSES) || defined(PDCURSES))) /* * This function offers the choices for values of an option via a popup window * which functions like that for selection of options in a form. - FM * * Also used for mouse popups with ncurses; this is indicated by for_mouse. */ int popup_choice(int cur_choice, int line, int column, STRING2PTR choices, int i_length, int disabled, int for_mouse) { if (column < 0) column = (COL_OPTION_VALUES - 1); term_options = FALSE; cur_choice = LYhandlePopupList(cur_choice, line, column, (STRING2PTR) choices, -1, i_length, disabled, for_mouse); switch (cur_choice) { case LYK_QUIT: case LYK_ABORT: case LYK_PREV_DOC: term_options = TRUE; if (!for_mouse) { HTUserMsg(CANCELLED); } break; } if (disabled || term_options) { _statusline(""); } else if (!for_mouse) { _statusline(VALUE_ACCEPTED); } return (cur_choice); } #endif /* !NO_OPTION_MENU */ #ifndef NO_OPTION_FORMS /* * I'm paranoid about mistyping strings. Also, this way they get combined * so we don't have to worry about the intelligence of the compiler. * We don't need to burn memory like it's cheap. We're better than that. */ #define SELECTED(flag) (flag) ? selected_string : "" #define DISABLED(flag) (flag) ? disabled_string : "" typedef struct { int value; const char *LongName; const char *HtmlName; } OptValues; typedef struct { char *tag; char *value; } PostPair; static const char selected_string[] = "selected"; static const char disabled_string[] = "disabled"; static const char on_string[] = N_("ON"); static const char off_string[] = N_("OFF"); static const char never_string[] = N_("NEVER"); static const char always_string[] = N_("ALWAYS"); static OptValues bool_values[] = { {FALSE, N_("OFF"), "OFF"}, {TRUE, N_("ON"), "ON"}, {0, 0, 0} }; static const char *secure_string = "secure"; static char *secure_value = NULL; static const char *save_options_string = "save_options"; /* * Personal Preferences */ static const char *cookies_string = RC_SET_COOKIES; static const char *cookies_ignore_all_string = N_("ignore"); static const char *cookies_up_to_user_string = N_("ask user"); static const char *cookies_accept_all_string = N_("accept all"); static const char *x_display_string = RC_DISPLAY; static const char *editor_string = RC_FILE_EDITOR; static const char *emacs_keys_string = RC_EMACS_KEYS; #if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)) #define EXEC_ALWAYS 2 #define EXEC_LOCAL 1 #define EXEC_NEVER 0 static const char *exec_links_string = RC_RUN_ALL_EXECUTION_LINKS; static OptValues exec_links_values[] = { {EXEC_NEVER, N_("ALWAYS OFF"), "ALWAYS OFF"}, {EXEC_LOCAL, N_("FOR LOCAL FILES ONLY"), "FOR LOCAL FILES ONLY"}, #ifndef NEVER_ALLOW_REMOTE_EXEC {EXEC_ALWAYS, N_("ALWAYS ON"), "ALWAYS ON"}, #endif {0, 0, 0} }; #endif /* ENABLE_OPTS_CHANGE_EXEC */ #ifdef EXP_KEYBOARD_LAYOUT static const char *kblayout_string = RC_KBLAYOUT; #endif static const char *keypad_mode_string = RC_KEYPAD_MODE; static OptValues keypad_mode_values[] = { {NUMBERS_AS_ARROWS, N_("Numbers act as arrows"), "number_arrows"}, {LINKS_ARE_NUMBERED, N_("Links are numbered"), "links_numbered"}, {LINKS_AND_FIELDS_ARE_NUMBERED, N_("Links and form fields are numbered"), "links_and_forms"}, {FIELDS_ARE_NUMBERED, N_("Form fields are numbered"), "forms_numbered"}, {0, 0, 0} }; static const char *lineedit_mode_string = RC_LINEEDIT_MODE; static const char *mail_address_string = RC_PERSONAL_MAIL_ADDRESS; static const char *personal_name_string = RC_PERSONAL_MAIL_NAME; static const char *search_type_string = RC_CASE_SENSITIVE_SEARCHING; #ifndef DISABLE_FTP static const char *anonftp_password_string = RC_ANONFTP_PASSWORD; #endif static OptValues search_type_values[] = { {FALSE, N_("Case insensitive"), "case_insensitive"}, {TRUE, N_("Case sensitive"), "case_sensitive"}, {0, 0, 0} }; #if defined(USE_SLANG) || defined(COLOR_CURSES) static const char *show_color_string = RC_SHOW_COLOR; static OptValues show_color_values[] = { {SHOW_COLOR_NEVER, never_string, never_string}, {SHOW_COLOR_OFF, off_string, off_string}, {SHOW_COLOR_ON, on_string, on_string}, {SHOW_COLOR_ALWAYS, always_string, always_string}, {0, 0, 0} }; #endif #ifdef USE_COLOR_STYLE static const char *color_style_string = RC_COLOR_STYLE; static OptValues *color_style_values; static HTList *color_style_list; #endif #ifdef USE_DEFAULT_COLORS static const char *default_colors_string = RC_DEFAULT_COLORS; #endif static const char *show_cursor_string = RC_SHOW_CURSOR; static const char *underline_links_string = RC_UNDERLINE_LINKS; #ifdef USE_SCROLLBAR static const char *show_scrollbar_string = RC_SCROLLBAR; #endif static const char prompt_dft_string[] = N_("prompt normally"); static const char prompt_yes_string[] = N_("force yes-response"); static const char prompt_no_string[] = N_("force no-response"); static OptValues prompt_values[] = { {FORCE_PROMPT_DFT, prompt_dft_string, prompt_dft_string}, {FORCE_PROMPT_YES, prompt_yes_string, prompt_yes_string}, {FORCE_PROMPT_NO, prompt_no_string, prompt_no_string}, {0, 0, 0} }; static const char *cookie_prompt_string = RC_FORCE_COOKIE_PROMPT; #ifdef USE_SSL static const char *ssl_prompt_string = RC_FORCE_SSL_PROMPT; #endif static const char *user_mode_string = RC_USER_MODE; static OptValues user_mode_values[] = { {NOVICE_MODE, N_("Novice"), "Novice"}, {INTERMEDIATE_MODE, N_("Intermediate"), "Intermediate"}, {ADVANCED_MODE, N_("Advanced"), "Advanced"}, {0, 0, 0} }; static const char *vi_keys_string = RC_VI_KEYS; static const char *visited_links_string = RC_VISITED_LINKS; static OptValues visited_links_values[] = { {VISITED_LINKS_AS_FIRST_V, N_("By First Visit"), "first_visited"}, {VISITED_LINKS_AS_FIRST_V | VISITED_LINKS_REVERSE, N_("By First Visit Reversed"), "first_visited_reversed"}, {VISITED_LINKS_AS_TREE, N_("As Visit Tree"), "visit_tree"}, {VISITED_LINKS_AS_LATEST, N_("By Last Visit"), "last_visited"}, {VISITED_LINKS_AS_LATEST | VISITED_LINKS_REVERSE, N_("By Last Visit Reversed"), "last_visited_reversed"}, {0, 0, 0} }; /* * Document Layout */ static const char *DTD_recovery_string = RC_TAGSOUP; static OptValues DTD_type_values[] = { /* Old_DTD variable */ {TRUE, N_("relaxed (TagSoup mode)"), "tagsoup"}, {FALSE, N_("strict (SortaSGML mode)"), "sortasgml"}, {0, 0, 0} }; static const char *bad_html_string = RC_BAD_HTML; static OptValues bad_html_values[] = { {BAD_HTML_IGNORE, N_("Ignore"), "ignore"}, {BAD_HTML_TRACE, N_("Add to trace-file"), "trace"}, {BAD_HTML_MESSAGE, N_("Add to LYNXMESSAGES"), "message"}, {BAD_HTML_WARN, N_("Warn, point to trace-file"), "warn"}, {0, 0, 0} }; static const char *select_popups_string = RC_SELECT_POPUPS; static const char *images_string = "images"; static const char *images_ignore_all_string = N_("ignore"); static const char *images_use_label_string = N_("as labels"); static const char *images_use_links_string = N_("as links"); static const char *verbose_images_string = RC_VERBOSE_IMAGES; static OptValues verbose_images_type_values[] = { /* verbose_img variable */ {FALSE, N_("OFF"), "OFF"}, {TRUE, N_("show filename"), "ON"}, {0, 0, 0} }; /* * Bookmark Options */ static const char *mbm_string = RC_MULTI_BOOKMARK; static OptValues mbm_values[] = { {MBM_OFF, N_("OFF"), "OFF"}, {MBM_STANDARD, N_("STANDARD"), "STANDARD"}, {MBM_ADVANCED, N_("ADVANCED"), "ADVANCED"}, {0, 0, 0} }; static const char *single_bookmark_string = RC_BOOKMARK_FILE; #ifdef USE_SESSIONS static const char *auto_session_string = RC_AUTO_SESSION; static const char *single_session_string = RC_SESSION_FILE; #endif /* * Character Set Options */ static const char *assume_char_set_string = RC_ASSUME_CHARSET; static const char *display_char_set_string = RC_CHARACTER_SET; static const char *raw_mode_string = RC_RAW_MODE; #ifdef USE_LOCALE_CHARSET static const char *locale_charset_string = RC_LOCALE_CHARSET; #endif static const char *html5_charsets_string = RC_HTML5_CHARSETS; /* * File Management Options */ static const char *show_dotfiles_string = RC_SHOW_DOTFILES; static const char *no_pause_string = RC_NO_PAUSE; #ifdef DIRED_SUPPORT static const char *dired_list_string = RC_DIR_LIST_STYLE; static OptValues dired_list_values[] = { {DIRS_FIRST, N_("Directories first"), "dired_dir"}, {FILES_FIRST, N_("Files first"), "dired_files"}, {MIXED_STYLE, N_("Mixed style"), "dired_mixed"}, {0, 0, 0} }; #ifdef LONG_LIST static const char *dired_sort_string = RC_DIR_LIST_ORDER; static OptValues dired_sort_values[] = { {ORDER_BY_NAME, N_("By Name"), "dired_by_name"}, {ORDER_BY_TYPE, N_("By Type"), "dired_by_type"}, {ORDER_BY_SIZE, N_("By Size"), "dired_by_size"}, {ORDER_BY_DATE, N_("By Date"), "dired_by_date"}, {ORDER_BY_MODE, N_("By Mode"), "dired_by_mode"}, #ifndef NO_GROUPS {ORDER_BY_USER, N_("By User"), "dired_by_user"}, {ORDER_BY_GROUP, N_("By Group"), "dired_by_group"}, #endif {0, 0, 0} }; #endif /* LONG_LIST */ #endif /* DIRED_SUPPORT */ #ifndef DISABLE_FTP static const char *passive_ftp_string = RC_FTP_PASSIVE; static const char *ftp_sort_string = RC_FILE_SORTING_METHOD; static OptValues ftp_sort_values[] = { {FILE_BY_NAME, N_("By Name"), "ftp_by_name"}, {FILE_BY_TYPE, N_("By Type"), "ftp_by_type"}, {FILE_BY_SIZE, N_("By Size"), "ftp_by_size"}, {FILE_BY_DATE, N_("By Date"), "ftp_by_date"}, {0, 0, 0} }; #endif #ifdef USE_READPROGRESS static const char *show_rate_string = RC_SHOW_KB_RATE; static OptValues rate_values[] = { {rateOFF, N_("Do not show rate"), "rate_off"}, {rateBYTES, N_("Show %s/sec rate"), "rate_bytes"}, {rateKB, N_("Show %s/sec rate"), "rate_kb"}, #ifdef USE_READPROGRESS {rateEtaBYTES, N_("Show %s/sec, ETA"), "rate_eta_bytes"}, {rateEtaKB, N_("Show %s/sec, ETA"), "rate_eta_kb"}, {rateEtaBYTES2, N_("Show %s/sec (2-digits), ETA"), "rate_eta_bytes2"}, {rateEtaKB2, N_("Show %s/sec (2-digits), ETA"), "rate_eta_kb2"}, #endif #ifdef USE_PROGRESSBAR {rateBAR, N_("Show progressbar"), "rate_bar"}, #endif {0, 0, 0} }; #endif /* USE_READPROGRESS */ /* * Presentation (MIME) types used in "Accept". */ static const char *preferred_media_string = RC_PREFERRED_MEDIA_TYPES; static OptValues media_values[] = { {mediaOpt1, N_("Accept lynx's internal types"), "media_opt1"}, {mediaOpt2, N_("Also accept lynx.cfg's types"), "media_opt2"}, {mediaOpt3, N_("Also accept user's types"), "media_opt3"}, {mediaOpt4, N_("Also accept system's types"), "media_opt4"}, {mediaALL, N_("Accept all types"), "media_all"}, {0, 0, 0} }; static const char *preferred_encoding_string = RC_PREFERRED_ENCODING; static OptValues encoding_values[] = { {encodingNONE, N_("None"), "encoding_none"}, #if defined(USE_ZLIB) || defined(GZIP_PATH) {encodingGZIP, N_("gzip"), "encoding_gzip"}, {encodingDEFLATE, N_("deflate"), "encoding_deflate"}, #endif #if defined(USE_ZLIB) || defined(COMPRESS_PATH) {encodingCOMPRESS, N_("compress"), "encoding_compress"}, #endif #if defined(USE_BZLIB) || defined(BZIP2_PATH) {encodingBZIP2, N_("bzip2"), "encoding_bzip2"}, #endif {encodingALL, N_("All"), "encoding_all"}, {0, 0, 0} }; /* * Headers transferred to remote server */ static const char *preferred_doc_char_string = RC_PREFERRED_CHARSET; static const char *preferred_doc_lang_string = RC_PREFERRED_LANGUAGE; static const char *send_user_agent_string = RC_SEND_USERAGENT; static const char *user_agent_string = RC_USERAGENT; #define PutHeader(fp, Name) \ fprintf(fp, "\n%s%s\n", MARGIN_STR, LYEntifyTitle(&buffer, Name)); #define PutCheckBox(fp, Name, Value, disable) \ fprintf(fp,\ "\n",\ Name, Value ? "checked" : "", disable_all?disabled_string:disable) #define PutTextInput(fp, Name, Value, Size, disable) \ fprintf(fp,\ "\n",\ (int) Size, Name, Value, disable_all?disabled_string:disable) #define PutOption(fp, flag, html, name) \ fprintf(fp,"