diff --git a/AUTHORS b/AUTHORS index c22dd7c..b80e979 100644 --- a/AUTHORS +++ b/AUTHORS @@ -74,3 +74,15 @@ Contributions are always welcome! + + +--------------- +Non-Dillo code: +--------------- + +* dw/fltkcomplexbutton.(cc|hh) contain code from the FLTK project whose + copyright is held by Bill Spitzak and others. +* src/binaryconst.h contains code by Tom Torfs that the author placed in the + public domain. +* src/md5.[ch] contain code by L. Peter Deutsch whose copyright is held by + Aladdin Enterprises. diff --git a/ChangeLog b/ChangeLog index 7d34732..bb1d7b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,28 @@ ============================================================================= Dillo project ============================================================================= + +Here we list changes that are relatively significant and/or visible to the +user. For a history of changes in full detail, see our Mercurial repository +at http://hg.dillo.org/dillo + + +dillo-3.0.2 [December 05, 2011] + ++- Digest authentication + Patch: Justus Winter, corvid ++- text-transform property + - If not following redirection, show body of redirecting page. + - Middle click on stylesheet menu item opens in new tab/window. + - Improve handling of combining characters. + - Locale-independent ASCII character case handling (fixes Turkic locales). + Patches: corvid ++- Rework line breaking and fix white-space:nowrap handling. + Patch: Johannes Hofmann ++- Bind Ctrl-{PageUp,PageDown} to tab-{previous,next}. + Patch: Jeremy Henty + +----------------------------------------------------------------------------- dillo-3.0.1 [September 21, 2011] @@ -52,10 +74,6 @@ Patch: Johannes Hofmann +- Default binding for left-tab changed to Shift-Ctrl-Tab. Patch: Jeremy Henty - -Note: these are user-visible changes. The full Changelog is kept - in our Mercurial repository at http://hg.dillo.org/dillo - ----------------------------------------------------------------------------- diff --git a/README b/README index e05981d..545f4d9 100644 --- a/README +++ b/README @@ -2,29 +2,26 @@ Dillo web browser =================== -Dillo-3.0 is the first release of the newest branch, which is -based on the already released fltk-1.3 toolkit. This is big news -because it clears the way for Dillo to return to those -distributions which had excluded Dillo2 due to FLTK2 never -being officially released. +Dillo is a multi-platform graphical web browser, known for its +speed and small footprint, that is developed with a focus on +personal security and privacy. -The development effort shifted to the dillo-3.x branch long ago, -and it became the active one; dillo-2.x received less and less -attention, and dillo-2.2.1 became the last of its series. +The dillo3 series uses version 1.3.x of the FLTK GUI toolkit +(http://fltk.org). -The core team will focus on implementing the CSS feature of -floating elements. This will greatly improve dillo's web page -rendering since many sites have adopted floats instead of tables. - -The new dillo3 has shown excellent stability. +The core team currently plans to focus on implementing the CSS +feature of floating elements. This will greatly improve +dillo's web page rendering since many sites have adopted floats +instead of tables. The core team welcomes developers willing to join our workforce. NOTE: With FLTK-1.3, when running on X with Xft enabled (the -default), font naming is more restricted than it was with FLTK2. -If your font_* preferences are no longer working well, please try -the fc-list command as given in dillorc. +default), font naming is more restricted than it was with FLTK2 +as used by dillo2. If your font_* preferences are no longer +working well, please try the fc-list command as it is shown in +dillorc. Here's a list of some old well-known problems of dillo: @@ -34,7 +31,7 @@ ------- -FLTK1.3 +FLTK-1.3 ------- You can get FLTK-1.3 here: @@ -59,8 +56,8 @@ 2.- Then dillo3: - tar jxvf dillo-3.0.1.tar.bz2 - cd dillo-3.0.1 + tar jxvf dillo-3.0.2.tar.bz2 + cd dillo-3.0.2 ./configure; make sudo make install-strip diff --git a/config.guess b/config.guess index c2246a4..dc84c68 100755 --- a/config.guess +++ b/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-12-30' +timestamp='2009-11-20' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -56,9 +56,8 @@ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free -Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." diff --git a/config.sub b/config.sub index 8518609..2a55a50 100755 --- a/config.sub +++ b/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-12-31' +timestamp='2009-11-20' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -75,9 +75,8 @@ version="\ GNU config.sub ($timestamp) -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free -Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -1436,8 +1435,6 @@ -dicos*) os=-dicos ;; - -nacl*) - ;; -none) ;; *) diff --git a/configure b/configure index a72c762..447510d 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for dillo 3.0.1. +# Generated by GNU Autoconf 2.68 for dillo 3.0.2. # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -557,8 +557,8 @@ # Identity of this package. PACKAGE_NAME='dillo' PACKAGE_TARNAME='dillo' -PACKAGE_VERSION='3.0.1' -PACKAGE_STRING='dillo 3.0.1' +PACKAGE_VERSION='3.0.2' +PACKAGE_STRING='dillo 3.0.2' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1287,7 +1287,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures dillo 3.0.1 to adapt to many kinds of systems. +\`configure' configures dillo 3.0.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1358,7 +1358,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of dillo 3.0.1:";; + short | recursive ) echo "Configuration of dillo 3.0.2:";; esac cat <<\_ACEOF @@ -1464,7 +1464,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -dillo configure 3.0.1 +dillo configure 3.0.2 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. @@ -2172,7 +2172,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by dillo $as_me 3.0.1, which was +It was created by dillo $as_me 3.0.2, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -3099,7 +3099,7 @@ # Define the identity of the package. PACKAGE='dillo' - VERSION='3.0.1' + VERSION='3.0.2' cat >>confdefs.h <<_ACEOF @@ -6984,7 +6984,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by dillo $as_me 3.0.1, which was +This file was extended by dillo $as_me 3.0.2, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -7050,7 +7050,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -dillo config.status 3.0.1 +dillo config.status 3.0.2 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index df7325b..d8865bc 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ dnl Process this file with aclocal, autoconf and automake. -AC_INIT([dillo], [3.0.1]) +AC_INIT([dillo], [3.0.2]) dnl Detect the canonical target build environment AC_CANONICAL_TARGET @@ -47,8 +47,8 @@ dnl AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) -AC_CHECK_SIZEOF(long) -AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(void *) AC_TYPE_INT16_T @@ -304,7 +304,7 @@ dnl ---------------------- dnl Check if we need to -dnl support the old +dnl support the old dnl iconv interface dnl ---------------------- if test "x$iconv_ok" = "xyes"; then @@ -370,9 +370,9 @@ else AC_MSG_RESULT(no. Now we will try some libraries.) - AC_SEARCH_LIBS(pthread_create, pthread, - LIBPTHREADS_LIBS="-lpthread", - AC_SEARCH_LIBS(pthread_create, pthreads, + AC_SEARCH_LIBS(pthread_create, pthread, + LIBPTHREADS_LIBS="-lpthread", + AC_SEARCH_LIBS(pthread_create, pthreads, LIBPTHREADS_LIBS="-lpthreads", AC_SEARCH_LIBS(pthread_create, c_r, LIBPTHREADS_LIBS="-lc_r", thread_ok=no))) diff --git a/dillorc b/dillorc index 20e9f1b..48b6665 100644 --- a/dillorc +++ b/dillorc @@ -165,9 +165,9 @@ # # See http://zytrax.com/tech/web/browser_ids.htm for a compilation of strings. # -# http_user_agent="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6" -# http_user_agent="Wget/1.11.4" -#The default is Dillo/(current version number) +# http_user_agent="Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1" +# http_user_agent="Wget/1.12 (linux-gnu)" +#The default is "Dillo/"+current_version_number #------------------------------------------------------------------------- # COLORS SECTION @@ -240,7 +240,7 @@ # A mouse's middle click over a tab closes the Tab. # With mousewheel mouses, right click feels way better (set to YES). -#right_click_closes_tab=NO +#right_click_closes_tab=NO # Mouse middle click by default drives drag-scrolling. # To paste an URL into the window instead of scrolling, set it to NO. diff --git a/dlib/dlib.c b/dlib/dlib.c index f2ef2dd..002adcd 100644 --- a/dlib/dlib.c +++ b/dlib/dlib.c @@ -134,6 +134,15 @@ } /* + * Clear the contents of the string + */ +void dStrshred(char *s) +{ + if (s) + memset(s, 0, strlen(s)); +} + +/* * Return a new string of length 'len' filled with 'c' characters */ char *dStrnfill(size_t len, char c) @@ -164,16 +173,21 @@ } /* + * ASCII functions to avoid the case difficulties introduced by I/i in + * Turkic locales. + */ + +/* * Case insensitive strstr */ -char *dStristr(const char *haystack, const char *needle) +char *dStriAsciiStr(const char *haystack, const char *needle) { int i, j; char *ret = NULL; if (haystack && needle) { for (i = 0, j = 0; haystack[i] && needle[j]; ++i) - if (tolower(haystack[i]) == tolower(needle[j])) { + if (D_ASCII_TOLOWER(haystack[i]) == D_ASCII_TOLOWER(needle[j])) { ++j; } else if (j) { i -= j; @@ -185,6 +199,29 @@ return ret; } +int dStrAsciiCasecmp(const char *s1, const char *s2) +{ + int ret = 0; + + while ((*s1 || *s2) && + !(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) { + s1++; + s2++; + } + return ret; +} + +int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n) +{ + int ret = 0; + + while (n-- && (*s1 || *s2) && + !(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) { + s1++; + s2++; + } + return ret; +} /* *- dStr ---------------------------------------------------------------------- @@ -333,6 +370,15 @@ ds->str[len] = 0; ds->len = len; } +} + +/* + * Clear a Dstr. + */ +void dStr_shred (Dstr *ds) +{ + if (ds && ds->sz > 0) + memset(ds->str, '\0', ds->sz); } /* diff --git a/dlib/dlib.h b/dlib/dlib.h index ae9c728..8dc324a 100644 --- a/dlib/dlib.h +++ b/dlib/dlib.h @@ -5,7 +5,6 @@ #include /* for size_t */ #include /* for va_list */ #include /* for strerror */ -#include /* for strcasecmp, strncasecmp (POSIX 2001) */ #include "d_size.h" @@ -34,6 +33,8 @@ #define dIsspace(c) isspace((uchar_t)(c)) #define dIsalnum(c) isalnum((uchar_t)(c)) +#define D_ASCII_TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 0x20 : (c)) +#define D_ASCII_TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) + 0x20 : (c)) /* *-- Casts ------------------------------------------------------------------- */ @@ -86,11 +87,11 @@ char *dStrstrip(char *s); char *dStrnfill(size_t len, char c); char *dStrsep(char **orig, const char *delim); -char *dStristr(const char *haystack, const char *needle); +void dStrshred(char *s); +char *dStriAsciiStr(const char *haystack, const char *needle); +int dStrAsciiCasecmp(const char *s1, const char *s2); +int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n); -/* these are in POSIX 2001. Could be implemented if a port requires it */ -#define dStrcasecmp strcasecmp -#define dStrncasecmp strncasecmp #define dStrerror strerror /* @@ -114,6 +115,7 @@ void dStr_insert (Dstr *ds, int pos_0, const char *s); void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l); void dStr_truncate (Dstr *ds, int len); +void dStr_shred (Dstr *ds); void dStr_erase (Dstr *ds, int pos_0, int len); void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp); void dStr_vsprintf (Dstr *ds, const char *format, va_list argp); diff --git a/doc/dillo.1 b/doc/dillo.1 index 2342d76..4d1968f 100644 --- a/doc/dillo.1 +++ b/doc/dillo.1 @@ -1,4 +1,4 @@ -.TH dillo 1 "August 5, 2010" "version 2.2" "USER COMMANDS" +.TH dillo 1 "October 13, 2011" "" "USER COMMANDS" .SH NAME dillo \- web browser .SH SYNOPSIS @@ -17,7 +17,8 @@ downloader. .PP Dillo displays HTML, text, PNG, JPEG, and GIF files. -It handles cookies, basic authentication, proxying, and some CSS. +It handles cookies, HTTP authentication (basic and digest), proxying (basic), +and some CSS. .PP Framesets are displayed as links to frames, and there is currently no support for javascript or video. diff --git a/doc/user_help.html b/doc/user_help.html index 4b35e6e..acf129f 100644 --- a/doc/user_help.html +++ b/doc/user_help.html @@ -3,7 +3,7 @@ - Dillo Web Browser :: + <title> Dillo Web Browser :: Help for New Users @@ -54,7 +54,7 @@ </ul> </td></tr> </table> - + <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3> <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat"> <h4><font color="green">Usage:</font></h4> @@ -77,8 +77,8 @@ <li> <b>Configuration:</b> If you want to change Dillo's appearance or behaviour, look at the options in your <b><font color="#5040a0">dillorc</font></b> - file (if you don't have a copy in your ~/.dillo/ directory, get it - <a href='http://www.dillo.org/dillorc'>here</a>). + file (if you don't have a copy in your ~/.dillo/ directory, get it + <a href='http://www.dillo.org/dillorc'>here</a>). <li> Clicking the "Reload" button always requests an end-to-end reload of the page currently viewed, but it will *not* reload embedded @@ -195,7 +195,7 @@ That is, if you just compile and use dillo, it will reject every single cookie sent to it! <p> - If you want to enable cookies in dillo, please read + If you want to enable cookies in dillo, please read <a href="Cookies.txt">Cookies.txt</a>. It's very easy -- just a matter of setting up a <code>cookiesrc</code> file). </td></tr> @@ -252,7 +252,7 @@ (<a href='http://www.dillo.org/dpi1.html'>dpi</a>) framework. This should be transparent to the end user. Please note that: <ul> - <li>It is a good idea to keep a backup of your + <li>It is a good idea to keep a backup of your <code>~/.dillo/bm.txt</code> <li>The server will stay alive after closing dillo. <li>You can stop the server by sending it a KILL signal. Dillo @@ -308,8 +308,10 @@ <tr><td>Back or "<b>,</b>" <td>< <td>previous page <tr><td>Shift-Back or "<b>.</b>" <td>> <td>next page <tr><td>Alt-F <td>File <td>file menu -<tr><td>Ctrl-TabKey <td>TabKey <td>Next tab -<tr><td>Ctrl-Shift-TabKey <td>TabKey <td>Previous tab +<tr><td>Ctrl-TabKey or + Ctrl-PageDown <td>TabKey <td>Next tab +<tr><td>Ctrl-Shift-TabKey or + Ctrl-PageUp <td>TabKey <td>Previous tab <tr><td>Esc <td>escape <td>close dialog, close findbar,<br> Hide/show control panels diff --git a/dpi/bookmarks.c b/dpi/bookmarks.c index 6e9cb3d..32dc424 100644 --- a/dpi/bookmarks.c +++ b/dpi/bookmarks.c @@ -442,7 +442,7 @@ if (*e == '+') { *p = ' '; } else if (*e == '%') { - if (dStrncasecmp(e, "%0D%0A", 6) == 0) { + if (dStrnAsciiCasecmp(e, "%0D%0A", 6) == 0) { *p = '\n'; e += 5; } else { @@ -1626,16 +1626,17 @@ dFree(cmd); url = a_Dpip_get_attr_l(Buf, BufSize, "url"); - if (strcmp(url, "dpi:/bm/modify") == 0) { - st = Bmsrv_send_modify_answer(sh, url); - dFree(url); - return st; - - } else if (strncmp(url, "dpi:/bm/modify?", 15) == 0) { - /* process request */ - st = Bmsrv_process_modify_request(sh, url); - dFree(url); - return st; + if (dStrnAsciiCasecmp(url, "dpi:", 4) == 0) { + if (strcmp(url+4, "/bm/modify") == 0) { + st = Bmsrv_send_modify_answer(sh, url); + dFree(url); + return st; + } else if (strncmp(url+4, "/bm/modify?", 11) == 0) { + /* process request */ + st = Bmsrv_process_modify_request(sh, url); + dFree(url); + return st; + } } diff --git a/dpi/cookies.c b/dpi/cookies.c index 734dc01..a3cb53c 100644 --- a/dpi/cookies.c +++ b/dpi/cookies.c @@ -158,7 +158,7 @@ { const DomainNode *n1 = v1, *n2 = v2; - return dStrcasecmp(n1->domain, n2->domain); + return dStrAsciiCasecmp(n1->domain, n2->domain); } /* @@ -169,7 +169,7 @@ const DomainNode *node = v1; const char *domain = v2; - return dStrcasecmp(node->domain, domain); + return dStrAsciiCasecmp(node->domain, domain); } /* @@ -468,7 +468,7 @@ int i; for (i = 0; i < 12; i++) { - if (!dStrncasecmp(months[i], month_name, 3)) + if (!dStrnAsciiCasecmp(months[i], month_name, 3)) return i; } return -1; @@ -821,15 +821,15 @@ cookie->expires_at = mktime(tm); if (cookie->expires_at == (time_t) -1) cookie->expires_at = cookies_future_time; - } else if (dStrcasecmp(attr, "Path") == 0) { + } else if (dStrAsciiCasecmp(attr, "Path") == 0) { value = Cookies_parse_value(&str); dFree(cookie->path); cookie->path = value; - } else if (dStrcasecmp(attr, "Domain") == 0) { + } else if (dStrAsciiCasecmp(attr, "Domain") == 0) { value = Cookies_parse_value(&str); dFree(cookie->domain); cookie->domain = value; - } else if (dStrcasecmp(attr, "Max-Age") == 0) { + } else if (dStrAsciiCasecmp(attr, "Max-Age") == 0) { value = Cookies_parse_value(&str); if (isdigit(*value) || *value == '-') { time_t now = time(NULL); @@ -845,7 +845,7 @@ expires = max_age = TRUE; } dFree(value); - } else if (dStrcasecmp(attr, "Expires") == 0) { + } else if (dStrAsciiCasecmp(attr, "Expires") == 0) { if (!max_age) { value = Cookies_parse_value(&str); Cookies_unquote_string(value); @@ -870,10 +870,10 @@ } else { Cookies_eat_value(&str); } - } else if (dStrcasecmp(attr, "Secure") == 0) { + } else if (dStrAsciiCasecmp(attr, "Secure") == 0) { cookie->secure = TRUE; Cookies_eat_value(&str); - } else if (dStrcasecmp(attr, "HttpOnly") == 0) { + } else if (dStrAsciiCasecmp(attr, "HttpOnly") == 0) { Cookies_eat_value(&str); } else { MSG("Cookie contains unknown attribute: '%s'\n", attr); @@ -920,10 +920,10 @@ _MSG("an IPv4 address\n"); return TRUE; } - if (*domain == '[' && - (len == strspn(domain, "0123456789abcdefABCDEF:.[]"))) { + if (strchr(domain, ':') && + (len == strspn(domain, "0123456789abcdefABCDEF:."))) { /* The precise format is shown in section 3.2.2 of rfc 3986 */ - _MSG("an IPv6 address\n"); + MSG("an IPv6 address\n"); return TRUE; } return FALSE; @@ -992,7 +992,7 @@ * don't, so: No. */ - if (!dStrcasecmp(A, B)) + if (!dStrAsciiCasecmp(A, B)) return TRUE; if (Cookies_domain_is_ip(B)) @@ -1002,7 +1002,7 @@ if (diff > 0) { /* B is the tail of A, and the match is preceded by a '.' */ - return (dStrcasecmp(A + diff, B) == 0 && A[diff - 1] == '.'); + return (dStrAsciiCasecmp(A + diff, B) == 0 && A[diff - 1] == '.'); } else { return FALSE; } @@ -1050,7 +1050,7 @@ for (i = 0; i < tld_num; i++) { if (strlen(tlds[i]) == (uint_t) tld_len && - !dStrncasecmp(tlds[i], host + start, tld_len)) { + !dStrnAsciiCasecmp(tlds[i], host + start, tld_len)) { _MSG("TLD code matched %s\n", tlds[i]); ret++; break; @@ -1221,7 +1221,7 @@ matching_cookies = dList_new(8); /* Check if the protocol is secure or not */ - is_ssl = (!dStrcasecmp(url_scheme, "https")); + is_ssl = (!dStrAsciiCasecmp(url_scheme, "https")); is_ip_addr = Cookies_domain_is_ip(url_host); @@ -1350,11 +1350,11 @@ rule[j++] = line[i++]; rule[j] = '\0'; - if (dStrcasecmp(rule, "ACCEPT") == 0) + if (dStrAsciiCasecmp(rule, "ACCEPT") == 0) cc.action = COOKIE_ACCEPT; - else if (dStrcasecmp(rule, "ACCEPT_SESSION") == 0) + else if (dStrAsciiCasecmp(rule, "ACCEPT_SESSION") == 0) cc.action = COOKIE_ACCEPT_SESSION; - else if (dStrcasecmp(rule, "DENY") == 0) + else if (dStrAsciiCasecmp(rule, "DENY") == 0) cc.action = COOKIE_DENY; else { MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n", @@ -1363,7 +1363,7 @@ } cc.domain = dStrdup(domain); - if (dStrcasecmp(cc.domain, "DEFAULT") == 0) { + if (dStrAsciiCasecmp(cc.domain, "DEFAULT") == 0) { /* Set the default action */ default_action = cc.action; dFree(cc.domain); @@ -1404,13 +1404,13 @@ if (ccontrol[i].domain[0] == '.') { diff = strlen(domain) - strlen(ccontrol[i].domain); if (diff >= 0) { - if (dStrcasecmp(domain + diff, ccontrol[i].domain) != 0) + if (dStrAsciiCasecmp(domain + diff, ccontrol[i].domain) != 0) continue; } else { continue; } } else { - if (dStrcasecmp(domain, ccontrol[i].domain) != 0) + if (dStrAsciiCasecmp(domain, ccontrol[i].domain) != 0) continue; } diff --git a/dpi/datauri.c b/dpi/datauri.c index 6d7acfa..f8e2ad9 100644 --- a/dpi/datauri.c +++ b/dpi/datauri.c @@ -44,7 +44,7 @@ MSG("len=%d{%s}\n", strlen((char*)str), str); for (p = s; (*p = *s); ++s) { - if (isalnum(*p) || strchr("+/=", *p)) + if (isascii(*p) && (isalnum(*p) || strchr("+/=", *p))) ++p; } @@ -226,14 +226,14 @@ char *mime_type = NULL, *p; size_t len = 0; - if (dStrncasecmp(url, "data:", 5) == 0) { + if (dStrnAsciiCasecmp(url, "data:", 5) == 0) { if ((p = strchr(url, ',')) && p - url < 256) { url += 5; len = p - url; strncpy(buf, url, len); buf[len] = 0; /* strip ";base64" */ - if (len >= 7 && dStrcasecmp(buf + len - 7, ";base64") == 0) { + if (len >= 7 && dStrAsciiCasecmp(buf + len - 7, ";base64") == 0) { len -= 7; buf[len] = 0; } @@ -242,7 +242,7 @@ /* that's it, now handle omitted types */ if (len == 0) { mime_type = dStrdup("text/plain;charset=US-ASCII"); - } else if (!dStrncasecmp(buf, "charset", 7)) { + } else if (!dStrnAsciiCasecmp(buf, "charset", 7)) { mime_type = dStrconcat("text/plain", buf, NULL); } else { mime_type = dStrdup(buf); @@ -262,7 +262,7 @@ unsigned char *data = NULL; if ((p = strchr(url, ',')) && p - url >= 12 && /* "data:;base64" */ - dStrncasecmp(p - 7, ";base64", 7) == 0) { + dStrnAsciiCasecmp(p - 7, ";base64", 7) == 0) { is_base64 = 1; } diff --git a/dpi/downloads.cc b/dpi/downloads.cc index 9ed929a..418dbd1 100644 --- a/dpi/downloads.cc +++ b/dpi/downloads.cc @@ -327,7 +327,7 @@ /* escape "'" character for the shell. Is it necessary? */ esc_url = Escape_uri_str(url, "'"); /* avoid malicious SMTP relaying with FTP urls */ - if (dStrncasecmp(esc_url, "ftp:/", 5) == 0) + if (dStrnAsciiCasecmp(esc_url, "ftp:/", 5) == 0) Filter_smtp_hack(esc_url); dl_argv = new char*[8]; int i = 0; @@ -354,81 +354,81 @@ gw = 400, gh = 70; group = new Fl_Group(0,0,gw,gh); group->begin(); - prTitle = new Fl_Box(24, 7, 290, 23); - prTitle->box(FL_RSHADOW_BOX); - prTitle->color(FL_WHITE); - prTitle->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP); - prTitle->copy_label(shortname); - // Attach this 'log_text' to the tooltip - log_text_add("Target File: ", 13); - log_text_add(fullname, strlen(fullname)); - log_text_add("\n\n", 2); - - prBar = new ProgressBar(24, 40, 92, 20); - prBar->box(FL_THIN_UP_BOX); - prBar->tooltip("Progress Status"); - - int ix = 122, iy = 37, iw = 50, ih = 14; - Fl_Widget *o = new Fl_Box(ix,iy,iw,ih, "Got"); - o->box(FL_RFLAT_BOX); - o->color(FL_DARK2); - o->labelsize(12); - o->tooltip("Downloaded Size"); - prGot = new Fl_Box(ix,iy+14,iw,ih, "0KB"); - prGot->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); - prGot->labelcolor(FL_BLUE); - prGot->labelsize(12); - prGot->box(FL_NO_BOX); - - ix += iw; - o = new Fl_Box(ix,iy,iw,ih, "Size"); - o->box(FL_RFLAT_BOX); - o->color(FL_DARK2); - o->labelsize(12); - o->tooltip("Total Size"); - prSize = new Fl_Box(ix,iy+14,iw,ih, "??"); - prSize->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); - prSize->labelsize(12); - prSize->box(FL_NO_BOX); - - ix += iw; - o = new Fl_Box(ix,iy,iw,ih, "Rate"); - o->box(FL_RFLAT_BOX); - o->color(FL_DARK2); - o->labelsize(12); - o->tooltip("Current transfer Rate (KBytes/sec)"); - prRate = new Fl_Box(ix,iy+14,iw,ih, "??"); - prRate->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); - prRate->labelsize(12); - prRate->box(FL_NO_BOX); - - ix += iw; - o = new Fl_Box(ix,iy,iw,ih, "~Rate"); - o->box(FL_RFLAT_BOX); - o->color(FL_DARK2); - o->labelsize(12); - o->tooltip("Average transfer Rate (KBytes/sec)"); - pr_Rate = new Fl_Box(ix,iy+14,iw,ih, "??"); - pr_Rate->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); - pr_Rate->labelsize(12); - pr_Rate->box(FL_NO_BOX); - - ix += iw; - prETAt = o = new Fl_Box(ix,iy,iw,ih, "ETA"); - o->box(FL_RFLAT_BOX); - o->color(FL_DARK2); - o->labelsize(12); - o->tooltip("Estimated Time of Arrival"); - prETA = new Fl_Box(ix,iy+14,iw,ih, "??"); - prETA->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); - prETA->labelsize(12); - prETA->box(FL_NO_BOX); - - prButton = new Fl_Button(326, 9, 44, 19, "Stop"); - prButton->tooltip("Stop this transfer"); - prButton->box(FL_UP_BOX); - prButton->clear_visible_focus(); - prButton->callback(prButton_scb, this); + prTitle = new Fl_Box(24, 7, 290, 23); + prTitle->box(FL_RSHADOW_BOX); + prTitle->color(FL_WHITE); + prTitle->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP); + prTitle->copy_label(shortname); + // Attach this 'log_text' to the tooltip + log_text_add("Target File: ", 13); + log_text_add(fullname, strlen(fullname)); + log_text_add("\n\n", 2); + + prBar = new ProgressBar(24, 40, 92, 20); + prBar->box(FL_THIN_UP_BOX); + prBar->tooltip("Progress Status"); + + int ix = 122, iy = 37, iw = 50, ih = 14; + Fl_Widget *o = new Fl_Box(ix,iy,iw,ih, "Got"); + o->box(FL_RFLAT_BOX); + o->color(FL_DARK2); + o->labelsize(12); + o->tooltip("Downloaded Size"); + prGot = new Fl_Box(ix,iy+14,iw,ih, "0KB"); + prGot->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + prGot->labelcolor(FL_BLUE); + prGot->labelsize(12); + prGot->box(FL_NO_BOX); + + ix += iw; + o = new Fl_Box(ix,iy,iw,ih, "Size"); + o->box(FL_RFLAT_BOX); + o->color(FL_DARK2); + o->labelsize(12); + o->tooltip("Total Size"); + prSize = new Fl_Box(ix,iy+14,iw,ih, "??"); + prSize->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + prSize->labelsize(12); + prSize->box(FL_NO_BOX); + + ix += iw; + o = new Fl_Box(ix,iy,iw,ih, "Rate"); + o->box(FL_RFLAT_BOX); + o->color(FL_DARK2); + o->labelsize(12); + o->tooltip("Current transfer Rate (KBytes/sec)"); + prRate = new Fl_Box(ix,iy+14,iw,ih, "??"); + prRate->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + prRate->labelsize(12); + prRate->box(FL_NO_BOX); + + ix += iw; + o = new Fl_Box(ix,iy,iw,ih, "~Rate"); + o->box(FL_RFLAT_BOX); + o->color(FL_DARK2); + o->labelsize(12); + o->tooltip("Average transfer Rate (KBytes/sec)"); + pr_Rate = new Fl_Box(ix,iy+14,iw,ih, "??"); + pr_Rate->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + pr_Rate->labelsize(12); + pr_Rate->box(FL_NO_BOX); + + ix += iw; + prETAt = o = new Fl_Box(ix,iy,iw,ih, "ETA"); + o->box(FL_RFLAT_BOX); + o->color(FL_DARK2); + o->labelsize(12); + o->tooltip("Estimated Time of Arrival"); + prETA = new Fl_Box(ix,iy+14,iw,ih, "??"); + prETA->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + prETA->labelsize(12); + prETA->box(FL_NO_BOX); + + prButton = new Fl_Button(326, 9, 44, 19, "Stop"); + prButton->tooltip("Stop this transfer"); + prButton->box(FL_UP_BOX); + prButton->clear_visible_focus(); + prButton->callback(prButton_scb, this); group->box(FL_ROUNDED_BOX); group->end(); @@ -1087,6 +1087,29 @@ } /* + * A Scroll class that resizes its resizable widget to its width. + * see http://seriss.com/people/erco/fltk/#ScrollableWidgetBrowser + */ +class DlScroll : public Fl_Scroll +{ +public: + void resize(int x_, int y_, int w_, int h_) + { + Fl_Widget *resizable_ = resizable(); + if (resizable_) + resizable_->resize(resizable_->x(), + resizable_->y(), + w() - scrollbar_size(), + resizable_->h()); + Fl_Scroll::resize(x_, y_, w_, h_); + } + DlScroll(int x, int y, int w, int h, const char *l = 0) + : Fl_Scroll(x, y, w, h, l) + { + } +}; + +/* * Create the main window and an empty list of requests. */ DLWin::DLWin(int ww, int wh) { @@ -1097,12 +1120,13 @@ // Create the empty main window mWin = new Fl_Window(ww, wh, "Downloads:"); mWin->begin(); - mScroll = new Fl_Scroll(0,0,ww,wh); - mScroll->begin(); - mPG = new Fl_Pack(0,0,ww-18,wh); - mPG->end(); - mScroll->end(); - mScroll->type(Fl_Scroll::VERTICAL); + mScroll = new DlScroll(0,0,ww,wh); + mScroll->begin(); + mPG = new Fl_Pack(0,0,ww-18,wh); + mPG->end(); + mScroll->end(); + mScroll->type(Fl_Scroll::VERTICAL); + mScroll->resizable(mPG); mWin->end(); mWin->resizable(mScroll); mWin->callback(dlwin_esc_cb, NULL); diff --git a/dpi/dpiutil.c b/dpi/dpiutil.c index e29d529..45100be 100644 --- a/dpi/dpiutil.c +++ b/dpi/dpiutil.c @@ -68,8 +68,10 @@ if (strchr(s, '%')) { for (p = buf; (*p = *s); ++s, ++p) { if (*p == '%' && isxdigit(s[1]) && isxdigit(s[2])) { - *p = (isdigit(s[1]) ? (s[1] - '0') : toupper(s[1]) - 'A' + 10)*16; - *p += isdigit(s[2]) ? (s[2] - '0') : toupper(s[2]) - 'A' + 10; + *p = (isdigit(s[1]) ? (s[1] - '0') + : D_ASCII_TOUPPER(s[1]) - 'A' + 10) * 16; + *p += isdigit(s[2]) ? (s[2] - '0') + : D_ASCII_TOUPPER(s[2]) - 'A' + 10; s += 2; } } @@ -121,7 +123,7 @@ for (i = 0, j = 0; str[i]; ++i) { if (str[i] == '&') { for (k = 0; k < 5; ++k) { - if (!dStrncasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) { + if (!dStrnAsciiCasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) { i += unsafe_rep_len[k] - 1; break; } @@ -154,7 +156,8 @@ memmove(url + i, url + i + 1, strlen(url + i)); --i; } else if (c == '%' && url[i+1] == '0' && - (tolower(url[i+2]) == 'a' || tolower(url[i+2]) == 'd')) { + (D_ASCII_TOLOWER(url[i+2]) == 'a' || + D_ASCII_TOLOWER(url[i+2]) == 'd')) { memmove(url + i, url + i + 3, strlen(url + i + 2)); --i; } diff --git a/dpi/file.c b/dpi/file.c index 64f06bf..5f1459a 100644 --- a/dpi/file.c +++ b/dpi/file.c @@ -15,7 +15,7 @@ * With new HTML layout. */ -#include <ctype.h> /* for tolower */ +#include <ctype.h> /* for isspace */ #include <errno.h> /* for errno */ #include <stdio.h> #include <stdlib.h> @@ -142,21 +142,21 @@ /* HTML try */ for (i = 0; i < Size && dIsspace(p[i]); ++i); - if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) || - (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) || - (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) || - (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) || + if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) || + (Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) || + (Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) || + (Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) || /* this line is workaround for FTP through the Squid proxy */ - (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) { + (Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) { Type = 1; /* Images */ - } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) { + } else if (Size >= 4 && !strncmp(p, "GIF8", 4)) { Type = 3; - } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) { + } else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) { Type = 4; - } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) { + } else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) { /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking * at the character representation should be machine independent. */ Type = 5; @@ -502,18 +502,18 @@ e++; - if (!dStrcasecmp(e, "gif")) { + if (!dStrAsciiCasecmp(e, "gif")) { return "image/gif"; - } else if (!dStrcasecmp(e, "jpg") || - !dStrcasecmp(e, "jpeg")) { + } else if (!dStrAsciiCasecmp(e, "jpg") || + !dStrAsciiCasecmp(e, "jpeg")) { return "image/jpeg"; - } else if (!dStrcasecmp(e, "png")) { + } else if (!dStrAsciiCasecmp(e, "png")) { return "image/png"; - } else if (!dStrcasecmp(e, "html") || - !dStrcasecmp(e, "htm") || - !dStrcasecmp(e, "shtml")) { + } else if (!dStrAsciiCasecmp(e, "html") || + !dStrAsciiCasecmp(e, "htm") || + !dStrAsciiCasecmp(e, "shtml")) { return "text/html"; - } else if (!dStrcasecmp(e, "txt")) { + } else if (!dStrAsciiCasecmp(e, "txt")) { return "text/plain"; } else { return NULL; @@ -712,7 +712,8 @@ /* Check for gzipped file */ namelen = strlen(client->filename); - if (namelen > 3 && !dStrcasecmp(client->filename + namelen - 3, ".gz")) { + if (namelen > 3 && + !dStrAsciiCasecmp(client->filename + namelen - 3, ".gz")) { gzipped = TRUE; namelen -= 3; } @@ -799,12 +800,12 @@ dReturn_val_if (orig == NULL, ret); /* Make sure the string starts with "file:/" */ - if (strncmp(str, "file:/", 5) != 0) + if (dStrnAsciiCasecmp(str, "file:/", 5) != 0) return ret; str += 5; /* Skip "localhost" */ - if (dStrncasecmp(str, "//localhost/", 12) == 0) + if (dStrnAsciiCasecmp(str, "//localhost/", 12) == 0) str += 11; /* Skip packed slashes, and leave just one */ @@ -941,7 +942,8 @@ DPIBYE = 1; MSG("(pid %d): Got DpiBye.\n", (int)getpid()); client->flags |= FILE_DONE; - } else if (url && strcmp(url, "dpi:/file/toggle") == 0) { + } else if (url && dStrnAsciiCasecmp(url, "dpi:", 4) == 0 && + strcmp(url+4, "/file/toggle") == 0) { File_toggle_html_style(client); } else if (path) { File_get(client, path, url); diff --git a/dpi/ftp.c b/dpi/ftp.c index 6b1e2f3..460c2f0 100644 --- a/dpi/ftp.c +++ b/dpi/ftp.c @@ -98,23 +98,23 @@ /* HTML try */ for (i = 0; i < Size && dIsspace(p[i]); ++i); - if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) || - (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) || - (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) || - (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) || + if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) || + (Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) || + (Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) || + (Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) || /* this line is workaround for FTP through the Squid proxy */ - (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) { + (Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) { Type = 1; st = 0; /* Images */ - } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) { + } else if (Size >= 4 && !strncmp(p, "GIF8", 4)) { Type = 3; st = 0; - } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) { + } else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) { Type = 4; st = 0; - } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) { + } else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) { /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking * at the character representation should be machine independent. */ Type = 5; diff --git a/dpi/https.c b/dpi/https.c index bea9de1..c2becda 100644 --- a/dpi/https.c +++ b/dpi/https.c @@ -360,9 +360,9 @@ char * url_look_up = NULL; /*Determine how much of url we chop off as unneeded*/ - if (dStrncasecmp(url, "https://", 8) == 0){ + if (dStrnAsciiCasecmp(url, "https://", 8) == 0){ url_offset = 8; - } else if (dStrncasecmp(url, "http://", 7) == 0) { + } else if (dStrnAsciiCasecmp(url, "http://", 7) == 0) { url_offset = 7; portnum = 80; } diff --git a/dpi/vsource.c b/dpi/vsource.c index 113aaa9..adf08a8 100644 --- a/dpi/vsource.c +++ b/dpi/vsource.c @@ -101,7 +101,8 @@ int bytes_read = 0, old_line = 0, line = 1; char *p, *q, *src_str, line_str[128]; - if (strncmp(url, "dpi:/vsource/:", 14) == 0) + if (dStrnAsciiCasecmp(url, "dpi:", 4) == 0 && + strncmp(url+4, "/vsource/:", 10) == 0) url += 14; /* Send HTTP header for plain text MIME type */ diff --git a/dpid/dpid.c b/dpid/dpid.c index d8bfeb9..ff95b70 100644 --- a/dpid/dpid.c +++ b/dpid/dpid.c @@ -845,7 +845,7 @@ if (A->name[A_len - 1] == '*') len = A_len - 1; - return(dStrncasecmp(A->name, B, len)); + return(dStrnAsciiCasecmp(A->name, B, len)); } /*! diff --git a/dw/fltkcomplexbutton.cc b/dw/fltkcomplexbutton.cc index 8929519..0f124cf 100644 --- a/dw/fltkcomplexbutton.cc +++ b/dw/fltkcomplexbutton.cc @@ -1,5 +1,6 @@ -// fltkcomplexbutton.cc contains code from FLTK 1.3's src/Fl_Button.cxx -// that is Copyright 1998-2010 by Bill Spitzak and others. +// fltkcomplexbutton.cc is derived from src/Fl_Button.cxx from FLTK's 1.3 +// branch at http://fltk.org in early 2011. +// src/Fl_Button.cxx is Copyright 1998-2010 by Bill Spitzak and others. /* * This program is free software; you can redistribute it and/or modify @@ -108,7 +109,8 @@ return 1; } else return 0; case FL_KEYBOARD : - if (Fl::focus() == this && Fl::event_key() == ' ' && + if (Fl::focus() == this && + (Fl::event_key() == ' ' || Fl::event_key() == FL_Enter) && !(Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT | FL_META))) { set_changed(); Fl_Widget_Tracker wp(this); diff --git a/dw/fltkcomplexbutton.hh b/dw/fltkcomplexbutton.hh index 3a14cfb..43be6b5 100644 --- a/dw/fltkcomplexbutton.hh +++ b/dw/fltkcomplexbutton.hh @@ -1,6 +1,7 @@ -// fltkcomplexbutton.hh contains code from FLTK 1.3's FL/Fl_Button.H -// that is Copyright 1998-2010 by Bill Spitzak and others. +// fltkcomplexbutton.hh is derived from FL/Fl_Button.H from FLTK's 1.3 branch +// at http://fltk.org in early 2011. +// FL/Fl_Button.H is Copyright 1998-2010 by Bill Spitzak and others. /* * This program is free software; you can redistribute it and/or modify diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc index 68819c9..099c449 100644 --- a/dw/fltkplatform.cc +++ b/dw/fltkplatform.cc @@ -141,7 +141,7 @@ static void strstrip(char *big, const char *little) { if (strlen(big) >= strlen(little) && - strcasecmp(big + strlen(big) - strlen(little), little) == 0) + misc::AsciiStrcasecmp(big + strlen(big) - strlen(little), little) == 0) *(big + strlen(big) - strlen(little)) = '\0'; } @@ -525,14 +525,18 @@ if ((cu = fl_toupper(c)) == c) { /* already uppercase, just draw the character */ fl_font(ff->font, ff->size); - width += font->letterSpacing; - width += (int)fl_width(text + curr, next - curr); + if (fl_nonspacing(cu) == 0) { + width += font->letterSpacing; + width += (int)fl_width(text + curr, next - curr); + } } else { /* make utf8 string for converted char */ nb = fl_utf8encode(cu, chbuf); fl_font(ff->font, sc_fontsize); - width += font->letterSpacing; - width += (int)fl_width(chbuf, nb); + if (fl_nonspacing(cu) == 0) { + width += font->letterSpacing; + width += (int)fl_width(chbuf, nb); + } } } } else { @@ -544,13 +548,45 @@ while (next < len) { next = nextGlyph(text, curr); - width += font->letterSpacing; + c = fl_utf8decode(text + curr, text + next, &nb); + if (fl_nonspacing(c) == 0) + width += font->letterSpacing; curr = next; } } } return width; +} + +char *FltkPlatform::textToUpper (const char *text, int len) +{ + char *newstr = NULL; + + if (len > 0) { + int newlen; + + newstr = (char*) malloc(3 * len + 1); + newlen = fl_utf_toupper((const unsigned char*)text, len, newstr); + assert(newlen <= 3 * len); + newstr[newlen] = '\0'; + } + return newstr; +} + +char *FltkPlatform::textToLower (const char *text, int len) +{ + char *newstr = NULL; + + if (len > 0) { + int newlen; + + newstr = (char*) malloc(3 * len + 1); + newlen = fl_utf_tolower((const unsigned char*)text, len, newstr); + assert(newlen <= 3 * len); + newstr[newlen] = '\0'; + } + return newstr; } int FltkPlatform::nextGlyph (const char *text, int idx) diff --git a/dw/fltkplatform.hh b/dw/fltkplatform.hh index db4bc79..7b4272e 100644 --- a/dw/fltkplatform.hh +++ b/dw/fltkplatform.hh @@ -153,6 +153,8 @@ void detachView (core::View *view); int textWidth (core::style::Font *font, const char *text, int len); + char *textToUpper (const char *text, int len); + char *textToLower (const char *text, int len); int nextGlyph (const char *text, int idx); int prevGlyph (const char *text, int idx); float dpiX (); diff --git a/dw/fltkui.cc b/dw/fltkui.cc index c0c8ff4..66a9669 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -266,6 +266,24 @@ // ---------------------------------------------------------------------- +class EnterButton : public Fl_Button { +public: + EnterButton (int x,int y,int w,int h, const char* label = 0) : + Fl_Button (x,y,w,h,label) {}; + int handle(int e); +}; + +int EnterButton::handle(int e) +{ + if (e == FL_KEYBOARD && Fl::focus() == this && Fl::event_key() == FL_Enter){ + set_changed(); + simulate_key_action(); + do_callback(); + return 1; + } + return Fl_Button::handle(e); +} + FltkLabelButtonResource::FltkLabelButtonResource (FltkPlatform *platform, const char *label): FltkSpecificResource <dw::core::ui::LabelButtonResource> (platform) @@ -283,8 +301,8 @@ *allocation) { Fl_Button *button = - new Fl_Button (allocation->x, allocation->y, allocation->width, - allocation->ascent + allocation->descent, label); + new EnterButton (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent, label); button->callback (widgetCallback, this); button->when (FL_WHEN_RELEASE); return button; diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc index bf3aba2..1d385cf 100644 --- a/dw/fltkviewbase.cc +++ b/dw/fltkviewbase.cc @@ -548,7 +548,7 @@ viewY = translateCanvasYToViewY (Y); int curr = 0, next = 0, nb; char chbuf[4]; - int c, cu; + int c, cu, width; if (font->fontVariant == core::style::FONT_VARIANT_SMALL_CAPS) { int sc_fontsize = lout::misc::roundInt(ff->size * 0.78); @@ -558,24 +558,30 @@ if ((cu = fl_toupper(c)) == c) { /* already uppercase, just draw the character */ fl_font(ff->font, ff->size); + width = (int)fl_width(text + curr, next - curr); + if (curr && width) + viewX += font->letterSpacing; fl_draw(text + curr, next - curr, viewX, viewY); - viewX += font->letterSpacing; - viewX += (int)fl_width(text + curr, next - curr); + viewX += width; } else { /* make utf8 string for converted char */ nb = fl_utf8encode(cu, chbuf); fl_font(ff->font, sc_fontsize); + width = (int)fl_width(chbuf, nb); + if (curr && width) + viewX += font->letterSpacing; fl_draw(chbuf, nb, viewX, viewY); - viewX += font->letterSpacing; - viewX += (int)fl_width(chbuf, nb); + viewX += width; } } } else { while (next < len) { next = theLayout->nextGlyph(text, curr); + width = (int)fl_width(text + curr, next - curr); + if (curr && width) + viewX += font->letterSpacing; fl_draw(text + curr, next - curr, viewX, viewY); - viewX += font->letterSpacing + - (int)fl_width(text + curr,next - curr); + viewX += width; curr = next; } } diff --git a/dw/layout.hh b/dw/layout.hh index d08eb36..4518749 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -297,6 +297,16 @@ return platform->textWidth (font, text, len); } + inline char *textToUpper (const char *text, int len) + { + return platform->textToUpper (text, len); + } + + inline char *textToLower (const char *text, int len) + { + return platform->textToLower (text, len); + } + inline int nextGlyph (const char *text, int idx) { return platform->nextGlyph (text, idx); diff --git a/dw/platform.hh b/dw/platform.hh index b79b534..cb71458 100644 --- a/dw/platform.hh +++ b/dw/platform.hh @@ -56,6 +56,16 @@ * \brief Return the width of a text, with a given length and font. */ virtual int textWidth (style::Font *font, const char *text, int len) = 0; + + /** + * \brief Return the string resulting from transforming text to uppercase. + */ + virtual char *textToUpper (const char *text, int len) = 0; + + /** + * \brief Return the string resulting from transforming text to lowercase. + */ + virtual char *textToLower (const char *text, int len) = 0; /** * \brief Return the index of the next glyph in string text. diff --git a/dw/style.cc b/dw/style.cc index b5549d3..a2240a9 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -42,6 +42,7 @@ textDecoration = TEXT_DECORATION_NONE; textAlign = TEXT_ALIGN_LEFT; textAlignChar = '.'; + textTransform = TEXT_TRANSFORM_NONE; listStylePosition = LIST_STYLE_POSITION_OUTSIDE; listStyleType = LIST_STYLE_TYPE_DISC; valign = VALIGN_BASELINE; @@ -117,6 +118,7 @@ textAlign == otherAttrs->textAlign && valign == otherAttrs->valign && textAlignChar == otherAttrs->textAlignChar && + textTransform == otherAttrs->textTransform && hBorderSpacing == otherAttrs->hBorderSpacing && vBorderSpacing == otherAttrs->vBorderSpacing && wordSpacing == otherAttrs->wordSpacing && @@ -154,6 +156,7 @@ textAlign + valign + textAlignChar + + textTransform + hBorderSpacing + vBorderSpacing + wordSpacing + @@ -244,6 +247,7 @@ textAlign = attrs->textAlign; valign = attrs->valign; textAlignChar = attrs->textAlignChar; + textTransform = attrs->textTransform; hBorderSpacing = attrs->hBorderSpacing; vBorderSpacing = attrs->vBorderSpacing; wordSpacing = attrs->wordSpacing; @@ -885,10 +889,10 @@ *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, *const roman_I3[] = { "","M","MM","MMM","MMMM" }; -static void strtolower (char *s) +static void strAsciiTolower (char *s) { for ( ; *s; s++) - *s = tolower (*s); + *s = misc::AsciiTolower (*s); } /** @@ -943,7 +947,7 @@ buf[buflen - 1] = '\0'; if (low) - strtolower(buf); + strAsciiTolower(buf); } diff --git a/dw/style.hh b/dw/style.hh index 4ebb7bb..dffb84b 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -245,6 +245,13 @@ VALIGN_SUPER, VALIGN_TEXT_TOP, VALIGN_TEXT_BOTTOM, +}; + +enum TextTransform { + TEXT_TRANSFORM_NONE, + TEXT_TRANSFORM_CAPITALIZE, + TEXT_TRANSFORM_UPPERCASE, + TEXT_TRANSFORM_LOWERCASE, }; /** @@ -441,6 +448,7 @@ TextAlignType textAlign; VAlignType valign; char textAlignChar; /* In future, strings will be supported. */ + TextTransform textTransform; int hBorderSpacing, vBorderSpacing, wordSpacing; Length width, height, lineHeight, textIndent; diff --git a/dw/table.cc b/dw/table.cc index 8a46ce7..c21b7a0 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -270,18 +270,19 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) { + const int maxspan = 100; Child *child; int colspanEff; - // We limit the values for colspan and rowspan to 50, to avoid + // We limit the values for colspan and rowspan to avoid // attacks by malicious web pages. - if (colspan > 50 || colspan < 0) { - MSG_WARN("colspan = %d is set to 50.\n", colspan); - colspan = 50; - } - if (rowspan > 50 || rowspan <= 0) { - MSG_WARN("rowspan = %d is set to 50.\n", rowspan); - rowspan = 50; + if (colspan > maxspan || colspan < 0) { + MSG_WARN("colspan = %d is set to %d.\n", colspan, maxspan); + colspan = maxspan; + } + if (rowspan > maxspan || rowspan <= 0) { + MSG_WARN("rowspan = %d is set to %d.\n", rowspan, maxspan); + rowspan = maxspan; } if (numRows == 0) { diff --git a/dw/textblock.cc b/dw/textblock.cc index 23465d2..b47ae64 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -70,17 +70,16 @@ words = new misc::SimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); - //DBG_OBJ_SET_NUM(page, "num_lines", num_lines); + //DBG_OBJ_SET_NUM(this, "num_lines", num_lines); lastLineWidth = 0; - lastLineParMin = 0; lastLineParMax = 0; wrapRef = -1; - //DBG_OBJ_SET_NUM(page, "last_line_width", last_line_width); - //DBG_OBJ_SET_NUM(page, "last_line_par_min", last_line_par_min); - //DBG_OBJ_SET_NUM(page, "last_line_par_max", last_line_par_max); - //DBG_OBJ_SET_NUM(page, "wrap_ref", wrap_ref); + //DBG_OBJ_SET_NUM(this, "last_line_width", last_line_width); + //DBG_OBJ_SET_NUM(this, "last_line_par_min", last_line_par_min); + //DBG_OBJ_SET_NUM(this, "last_line_par_max", last_line_par_max); + //DBG_OBJ_SET_NUM(this, "wrap_ref", wrap_ref); hoverLink = -1; @@ -129,7 +128,7 @@ parent class destructor. (???) */ words = NULL; - //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines); + //DBG_OBJ_SET_NUM(this, "num_lines", lines->size ()); } /** @@ -202,12 +201,11 @@ { core::Extremes wordExtremes; Line *line; - Word *word; + Word *word, *prevWord = NULL; int wordIndex, lineIndex; - int parMin, parMax; - bool nowrap; - - //DBG_MSG (widget, "extremes", 0, "Dw_page_get_extremes"); + int parMax; + + //DBG_MSG (widget, "extremes", 0, "getExtremesImpl"); //DBG_MSG_START (widget); if (lines->size () == 0) { @@ -217,11 +215,7 @@ } else if (wrapRef == -1) { /* no rewrap necessary -> values in lines are up to date */ line = lines->getRef (lines->size () - 1); - /* Historical note: The former distinction between lines with and without - * words[first_word]->nowrap set is no longer necessary, since - * Dw_page_real_word_wrap sets max_word_min to the correct value in any - * case. */ - extremes->minWidth = line->maxWordMin; + extremes->minWidth = line->maxParMin; extremes->maxWidth = misc::max (line->maxParMax, lastLineParMax); //DBG_MSG (widget, "extremes", 0, "simple case"); } else { @@ -232,13 +226,11 @@ if (wrapRef == 0) { extremes->minWidth = 0; extremes->maxWidth = 0; - parMin = 0; parMax = 0; } else { line = lines->getRef (wrapRef); - extremes->minWidth = line->maxWordMin; + extremes->minWidth = line->maxParMin; extremes->maxWidth = line->maxParMax; - parMin = line->parMin; parMax = line->parMax; //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin); @@ -250,15 +242,9 @@ for (lineIndex = wrapRef; lineIndex < lines->size (); lineIndex++) { //DBG_MSGF (widget, "extremes", 0, "line %d", lineIndex); //DBG_MSG_START (widget); - core::style::WhiteSpace ws; + int parMin = 0; line = lines->getRef (lineIndex); - ws = words->getRef(line->firstWord)->style->whiteSpace; - nowrap = ws == core::style::WHITE_SPACE_PRE || - ws == core::style::WHITE_SPACE_NOWRAP; - - //DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n", - // lineIndex, page->num_lines, nowrap); for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { @@ -269,27 +255,35 @@ wordExtremes.minWidth += line1OffsetEff; wordExtremes.maxWidth += line1OffsetEff; //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1, - // " (next plus %d)\n", page->line1_offset); + // " (next plus %d)\n", line1OffsetEff); } - if (nowrap) { - parMin += prevWordSpace + wordExtremes.minWidth; - //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin); - } else { - if (extremes->minWidth < wordExtremes.minWidth) - extremes->minWidth = wordExtremes.minWidth; - } + + if (extremes->minWidth < wordExtremes.minWidth) + extremes->minWidth = wordExtremes.minWidth; _MSG("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n", parMax, wordExtremes.maxWidth, prevWordSpace); if (word->content.type != core::Content::BREAK) parMax += prevWordSpace; parMax += wordExtremes.maxWidth; + + if (prevWord && !canBreakAfter(prevWord)) { + parMin += prevWordSpace + wordExtremes.minWidth; + } else { + parMin = wordExtremes.minWidth; + } + + if (extremes->minWidth < parMin) { + extremes->minWidth = parMin; + } + prevWordSpace = word->origSpace; + prevWord = word; //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1, // " word %s: maxWidth = %d\n", - // a_Dw_content_text (&word->content), + // word->content.text, // word_extremes.maxWidth); } @@ -300,24 +294,12 @@ //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2, // " parMax = %d, after word %d (%s)\n", // parMax, line->last_word - 1, - // a_Dw_content_text (&word->content)); + // word->content.text); if (extremes->maxWidth < parMax) extremes->maxWidth = parMax; - if (nowrap) { - //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin); - if (extremes->minWidth < parMin) - extremes->minWidth = parMin; - - //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2, - // " parMin = %d, after word %d (%s)\n", - // parMin, line->last_word - 1, - // a_Dw_content_text (&word->content)); - } - prevWordSpace = 0; - parMin = 0; parMax = 0; } @@ -329,7 +311,7 @@ } //DBG_MSGF (widget, "extremes", 0, "width difference: %d + %d", - // page->inner_padding, p_Dw_style_box_diff_width (widget->style)); + // innerPadding, getStyle()->boxDiffWidth ()); int diff = innerPadding + getStyle()->boxDiffWidth (); extremes->minWidth += diff; @@ -480,26 +462,26 @@ void Textblock::markChange (int ref) { if (ref != -1) { - //DBG_MSGF (page, "wrap", 0, "Dw_page_mark_size_change (ref = %d)", ref); + //DBG_MSGF (page, "wrap", 0, "markChange (ref = %d)", ref); if (wrapRef == -1) wrapRef = ref; else wrapRef = misc::min (wrapRef, ref); - //DBG_OBJ_SET_NUM (page, "wrap_ref", page->wrap_ref); + //DBG_OBJ_SET_NUM (this, "wrap_ref", wrapRef); } } void Textblock::setWidth (int width) { - /* If limitTextWidth is set to YES, a queue_resize may also be + /* If limitTextWidth is set to YES, a queueResize() may also be * necessary. */ if (availWidth != width || limitTextWidth) { //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "Dw_page_set_width: Calling p_Dw_widget_queue_resize, " + // "setWidth: Calling queueResize, " // "in page with %d word(s)\n", - // page->num_words); + // words->size()); availWidth = width; queueResize (0, false); @@ -512,9 +494,9 @@ { if (availAscent != ascent) { //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "Dw_page_set_ascent: Calling p_Dw_widget_queue_resize, " + // "setAscent: Calling queueResize, " // "in page with %d word(s)\n", - // page->num_words); + // words->size()); availAscent = ascent; queueResize (0, false); @@ -526,9 +508,9 @@ { if (availDescent != descent) { //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "Dw_page_set_descent: Calling p_Dw_widget_queue_resize, " + // "setDescent: Calling queueResize, " // "in page with %d word(s)\n", - // page->num_words); + // words->size()); availDescent = descent; queueResize (0, false); @@ -621,102 +603,113 @@ core::MousePositionEvent *event) { core::Iterator *it; - Line *line, *lastLine; - int nextWordStartX, wordStartX, wordX, nextWordX, yFirst, yLast; - int charPos = 0, link = -1, prevPos, wordIndex, lineIndex; - Word *word; - bool found, r; + int wordIndex; + int charPos = 0, link = -1; + bool r; if (words->size () == 0) { wordIndex = -1; } else { - lastLine = lines->getRef (lines->size () - 1); - yFirst = lineYOffsetCanvasI (0); - yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent + - lastLine->boxDescent; + Line *lastLine = lines->getRef (lines->size () - 1); + int yFirst = lineYOffsetCanvasI (0); + int yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent + + lastLine->boxDescent; if (event->yCanvas < yFirst) { // Above the first line: take the first word. wordIndex = 0; - charPos = 0; } else if (event->yCanvas >= yLast) { // Below the last line: take the last word. wordIndex = words->size () - 1; - word = words->getRef (wordIndex); - charPos = word->content.type == core::Content::TEXT ? - strlen (word->content.text) : 0; + charPos = core::SelectionState::END_OF_WORD; } else { - lineIndex = findLineIndex (event->yWidget); - line = lines->getRef (lineIndex); + Line *line = lines->getRef (findLineIndex (event->yWidget)); // Pointer within the break space? if (event->yWidget > (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) { // Choose this break. wordIndex = line->lastWord; - charPos = 0; + charPos = core::SelectionState::END_OF_WORD; } else if (event->xWidget < lineXOffsetWidget (line)) { // Left of the first word in the line. wordIndex = line->firstWord; - charPos = 0; } else { - nextWordStartX = lineXOffsetWidget (line); - found = false; + int nextWordStartX = lineXOffsetWidget (line); + for (wordIndex = line->firstWord; - !found && wordIndex <= line->lastWord; + wordIndex <= line->lastWord; wordIndex++) { - word = words->getRef (wordIndex); - wordStartX = nextWordStartX; + Word *word = words->getRef (wordIndex); + int wordStartX = nextWordStartX; + nextWordStartX += word->size.width + word->effSpace; if (event->xWidget >= wordStartX && event->xWidget < nextWordStartX) { // We have found the word. - if (word->content.type == core::Content::TEXT) { - // Search the character the mouse pointer is in. - // nextWordX is the right side of this character. - charPos = 0; - while ((nextWordX = wordStartX + - layout->textWidth (word->style->font, - word->content.text, charPos)) - <= event->xWidget) - charPos = layout->nextGlyph (word->content.text, - charPos); - // The left side of this character. - prevPos = layout->prevGlyph (word->content.text, charPos); - wordX = wordStartX + layout->textWidth (word->style->font, - word->content.text, - prevPos); - - // If the mouse pointer is left from the middle, use the - // left position, otherwise, use the right one. - if (event->xWidget <= (wordX + nextWordX) / 2) - charPos = prevPos; + int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; + + if (event->xWidget >= nextWordStartX - word->effSpace) { + charPos = core::SelectionState::END_OF_WORD; + if (wordIndex < line->lastWord && + (words->getRef(wordIndex + 1)->content.type != + core::Content::BREAK) && + (event->yWidget <= + yWidgetBase + word->spaceStyle->font->descent) && + (event->yWidget > + yWidgetBase - word->spaceStyle->font->ascent)) { + link = word->spaceStyle->x_link; + } } else { - // Depends on whether the pointer is within the left or - // right half of the (non-text) word. - if (event->xWidget >= - (wordStartX + nextWordStartX) / 2) - charPos = core::SelectionState::END_OF_WORD; - else - charPos = 0; + if (event->yWidget <= yWidgetBase + word->size.descent && + event->yWidget > yWidgetBase - word->size.ascent) { + link = word->style->x_link; + } + if (word->content.type == core::Content::TEXT) { + int glyphX = wordStartX; + + while (1) { + int nextCharPos = + layout->nextGlyph (word->content.text, charPos); + int glyphWidth = + textWidth (word->content.text, charPos, + nextCharPos - charPos, word->style); + if (event->xWidget > glyphX + glyphWidth) { + glyphX += glyphWidth; + charPos = nextCharPos; + continue; + } else if (event->xWidget >= glyphX + glyphWidth/2){ + // On the right half of a character; + // now just look for combining chars + charPos = nextCharPos; + while (word->content.text[charPos]) { + nextCharPos = + layout->nextGlyph (word->content.text, + charPos); + if (textWidth (word->content.text, charPos, + nextCharPos - charPos, + word->style)) + break; + charPos = nextCharPos; + } + } + break; + } + } else { + // Depends on whether the pointer is within the left or + // right half of the (non-text) word. + if (event->xWidget >= (wordStartX + nextWordStartX) /2) + charPos = core::SelectionState::END_OF_WORD; + } } - - found = true; - link = word->style ? word->style->x_link : -1; break; } } - - if (!found) { + if (wordIndex > line->lastWord) { // No word found in this line (i.e. we are on the right side), // take the last of this line. wordIndex = line->lastWord; - if (wordIndex >= words->size ()) - wordIndex--; - word = words->getRef (wordIndex); - charPos = word->content.type == core::Content::TEXT ? - strlen (word->content.text) : - (int)core::SelectionState::END_OF_WORD; + charPos = core::SelectionState::END_OF_WORD; } } } @@ -770,8 +763,8 @@ words->getRef(i)->effSpace = words->getRef(i)->origSpace + (effSpaceDiffCum - lastEffSpaceDiffCum); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", i, - // page->words[i].eff_space); + //DBG_OBJ_ARRSET_NUM (this, "words.%d.effSpace", i, + // words->getRef(i)->effSpace); lastEffSpaceDiffCum = effSpaceDiffCum; } @@ -783,21 +776,21 @@ { Line *lastLine; - //DBG_MSG (page, "wrap", 0, "Dw_page_add_line"); + //DBG_MSG (page, "wrap", 0, "addLine"); //DBG_MSG_START (page); lines->increase (); - //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines); + //DBG_OBJ_SET_NUM(this, "num_lines", lines->size ()); //DEBUG_MSG (DEBUG_REWRAP_LEVEL, "--- new line %d in %p, with word %d of %d" - // "\n", page->num_lines - 1, page, word_ind, page->num_words); + // "\n", lines->size () - 1, page, word_ind, words->size()); lastLine = lines->getRef (lines->size () - 1); if (lines->size () == 1) { lastLine->top = 0; lastLine->maxLineWidth = line1OffsetEff; - lastLine->maxWordMin = 0; + lastLine->maxParMin = 0; lastLine->maxParMax = 0; } else { Line *prevLine = lines->getRef (lines->size () - 2); @@ -805,21 +798,21 @@ lastLine->top = prevLine->top + prevLine->boxAscent + prevLine->boxDescent + prevLine->breakSpace; lastLine->maxLineWidth = prevLine->maxLineWidth; - lastLine->maxWordMin = prevLine->maxWordMin; + lastLine->maxParMin = prevLine->maxParMin; lastLine->maxParMax = prevLine->maxParMax; } - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.top", lines->size () - 1, // lastLine->top); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxLineWidth", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxLineWidth", lines->size () - 1, // lastLine->maxLineWidth); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxWordMin", page->num_lines - 1, - // lastLine->maxWordMin); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxParMin", lines->size () - 1, + // lastLine->maxParMin); + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxParMax", lines->size () - 1, // lastLine->maxParMax); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMin", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMin", lines->size () - 1, // lastLine->parMin); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMax", lines->size () - 1, // lastLine->parMax); lastLine->firstWord = wordIndex; @@ -829,9 +822,9 @@ lastLine->breakSpace = 0; lastLine->leftOffset = 0; - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.ascent", lines->size () - 1, // lastLine->boxAscent); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.descent", lines->size () - 1, // lastLine->boxDescent); /* update values in line */ @@ -844,33 +837,23 @@ if (newPar) { lastLine->maxParMax = misc::max (lastLine->maxParMax, lastLineParMax); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxParMax", lines->size () - 1, // lastLine->maxParMax); - /* The following code looks questionable (especially since the values - * will be overwritten). In any case, line1OffsetEff is probably - * supposed to go into lastLinePar*, not lastLine->par*. - */ if (lines->size () > 1) { - lastLine->parMin = 0; - lastLine->parMax = 0; + lastLineParMax = 0; } else { - lastLine->parMin = line1OffsetEff; - lastLine->parMax = line1OffsetEff; - } - lastLineParMin = 0; - lastLineParMax = 0; - - //DBG_OBJ_SET_NUM(page, "lastLineParMin", page->lastLineParMin); - //DBG_OBJ_SET_NUM(page, "lastLineParMax", page->lastLineParMax); - } - - lastLine->parMin = lastLineParMin; + lastLineParMax = line1OffsetEff; + } + + //DBG_OBJ_SET_NUM(this, "lastLineParMax", lastLineParMax); + } + lastLine->parMax = lastLineParMax; - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMin", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMin", lines->size () - 1, // lastLine->parMin); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMax", lines->size () - 1, // lastLine->parMax); //DBG_MSG_END (page); @@ -887,12 +870,12 @@ Line *lastLine; Word *word; int availWidth, lastSpace, leftOffset, len; - bool newLine = false, newPar = false; + bool newLine = false, newPar = false, canBreakBefore = true; core::Extremes wordExtremes; - //DBG_MSGF (page, "wrap", 0, "Dw_page_real_word_wrap (%d): %s, width = %d", - // word_ind, a_Dw_content_html (&page->words[word_ind].content), - // page->words[word_ind].size.width); + //DBG_MSGF (page, "wrap", 0, "wordWrap (%d): %s, width = %d", + // wordIndex, words->getRef(wordIndex)->content.text), + // words->getRef(wordIndex)->size.width); //DBG_MSG_START (page); availWidth = this->availWidth - getStyle()->boxDiffWidth() - innerPadding; @@ -942,23 +925,37 @@ /* previous word is a break */ newLine = true; newPar = true; - } else if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP || - word->style->whiteSpace == core::style::WHITE_SPACE_PRE) { - //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)", - // word->style->white_space); + } else if (!canBreakAfter (prevWord)) { + canBreakBefore = false; + // no break within nowrap newLine = false; newPar = false; + if (lastLineWidth + prevWord->origSpace + word->size.width > + availWidth) + markChange (lines->size () - 1); } else if (lastLine->firstWord != wordIndex) { - /* Does new word fit into the last line? */ - //DBG_MSGF (page, "wrap", 0, - // "word %d (%s) fits? (%d + %d + %d <= %d)...", - // word_ind, a_Dw_content_html (&word->content), - // page->lastLine_width, prevWord->orig_space, - // word->size.width, availWidth); - newLine = lastLineWidth + prevWord->origSpace + word->size.width > - availWidth; - //DBG_MSGF (page, "wrap", 0, "... %s.", - // newLine ? "No" : "Yes"); + // check if we need to break because nowrap sequence is following + newLine = false; + int lineWidthNeeded = lastLineWidth + prevWord->origSpace; + for (int i = wordIndex; i < words->size (); i++) { + Word *w = words->getRef (i); + + if (w->content.type == core::Content::BREAK || + (word->content.type == core::Content::WIDGET && + word->content.widget->blockLevel())) + break; + + lineWidthNeeded += w->size.width; + + if (lineWidthNeeded > availWidth) { + newLine = true; + break; + } else if (canBreakAfter (w)) { + break; + } + + lineWidthNeeded += w->origSpace; + } } } @@ -984,9 +981,9 @@ len += word->style->font->ascent / 3; lastLine->contentDescent = misc::max (lastLine->contentDescent, len); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.ascent", lines->size () - 1, // lastLine->boxAscent); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.descent", lines->size () - 1, // lastLine->boxDescent); if (word->content.type == core::Content::WIDGET) { @@ -1001,7 +998,7 @@ word->content.widget->blockLevel () && getStyle ()->borderWidth.top == 0 && getStyle ()->padding.top == 0) { - // collapse top margin of parent element with top margin of first child + // collapse top margins of parent element and its first child // see: http://www.w3.org/TR/CSS21/box.html#collapsing-margins collapseMarginTop = getStyle ()->margin.top; } @@ -1029,36 +1026,31 @@ if (!newLine) lastLineWidth += lastSpace; if (!newPar) { - lastLineParMin += lastSpace; lastLineParMax += lastSpace; } lastLineWidth += word->size.width; getWordExtremes (word, &wordExtremes); - lastLineParMin += wordExtremes.maxWidth; /* Why maxWidth? */ lastLineParMax += wordExtremes.maxWidth; - if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP || - word->style->whiteSpace == core::style::WHITE_SPACE_PRE) { - lastLine->parMin += wordExtremes.minWidth + lastSpace; + if (!canBreakBefore) { + lastLineParMin += wordExtremes.minWidth + lastSpace; /* This may also increase the accumulated minimum word width. */ - lastLine->maxWordMin = - misc::max (lastLine->maxWordMin, lastLine->parMin); - /* NOTE: Most code relies on that all values of nowrap are equal for all - * words within one line. */ + lastLine->maxParMin = misc::max (lastLine->maxParMin, lastLineParMin); } else { - lastLine->maxWordMin = - misc::max (lastLine->maxWordMin, wordExtremes.minWidth); - } - - //DBG_OBJ_SET_NUM(page, "lastLine_par_min", page->lastLine_par_min); - //DBG_OBJ_SET_NUM(page, "lastLine_par_max", page->lastLine_par_max); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1, + lastLineParMin = wordExtremes.minWidth; + lastLine->maxParMin = + misc::max (lastLine->maxParMin, wordExtremes.minWidth); + } + + //DBG_OBJ_SET_NUM(this, "lastLine_par_min", lastLineParMin); + //DBG_OBJ_SET_NUM(this, "lastLine_par_max", lastLineParMax); + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.par_min", lines->size () - 1, // lastLine->par_min); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.par_max", lines->size () - 1, // lastLine->par_max); - //DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1, + //DBG_OBJ_ARRSET_NUM (this, "lines.%d.max_word_min", lines->size () - 1, // lastLine->max_word_min); /* Align the line. @@ -1087,15 +1079,7 @@ if (leftOffset < 0) leftOffset = 0; - if (hasListitemValue && lastLine == lines->getRef (0)) { - /* List item markers are always on the left. */ - lastLine->leftOffset = 0; - words->getRef(0)->effSpace = words->getRef(0)->origSpace + leftOffset; - //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0, - // page->words[0].eff_space); - } else { - lastLine->leftOffset = leftOffset; - } + lastLine->leftOffset = leftOffset; } mustQueueResize = true; @@ -1177,26 +1161,26 @@ return; //DBG_MSGF (page, "wrap", 0, - // "Dw_page_rewrap: page->wrap_ref = %d, in page with %d word(s)", - // page->wrap_ref, page->num_words); + // "rewrap: wrapRef = %d, in page with %d word(s)", + // wrapRef, words->size()); //DBG_MSG_START (page); - /* All lines up from page->wrap_ref will be rebuild from the word list, + /* All lines up from wrapRef will be rebuild from the word list, * the line list up from this position is rebuild. */ lines->setSize (wrapRef); lastLineWidth = 0; - //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines); - //DBG_OBJ_SET_NUM(page, "lastLine_width", page->lastLine_width); + lastLineParMin = 0; + //DBG_OBJ_SET_NUM(this, "num_lines", lines->size ()); + //DBG_OBJ_SET_NUM(this, "lastLine_width", lastLineWidth); /* In the word list, start at the last word plus one in the line before. */ if (wrapRef > 0) { - /* Note: In this case, Dw_page_real_word_wrap will immediately find - * the need to rewrap the line, since we start with the last one (plus - * one). This is also the reason, why page->lastLine_width is set + /* Note: In this case, wordWrap() will immediately find the need + * to rewrap the line, since we start with the last one (plus one). + * This is also the reason, why lastLineWidth is set * to the length of the line. */ lastLine = lines->getRef (lines->size () - 1); - lastLineParMin = lastLine->parMin; lastLineParMax = lastLine->parMax; wordIndex = lastLine->lastWord + 1; @@ -1205,7 +1189,6 @@ words->getRef(i)->origSpace); lastLineWidth += words->getRef(lastLine->lastWord)->size.width; } else { - lastLineParMin = 0; lastLineParMax = 0; wordIndex = 0; @@ -1227,14 +1210,14 @@ //DEBUG_MSG(DEBUG_REWRAP_LEVEL, // "Assigning parent_ref = %d to rewrapped word %d, " // "in page with %d word(s)\n", - // page->num_lines - 1, wordIndex, page->num_words); + // lines->size () - 1, wordIndex, words->size()); /* todo_refactoring: if (word->content.type == DW_CONTENT_ANCHOR) p_Dw_gtk_viewport_change_anchor (widget, word->content.anchor, Dw_page_line_total_y_offset (page, - &page->lines[page->num_lines - 1])); + &page->lines[lines->size () - 1])); */ } @@ -1270,9 +1253,66 @@ } /* + * Draw a string of text + */ +void Textblock::drawText(core::View *view, core::style::Style *style, + core::style::Color::Shading shading, int x, int y, + const char *text, int start, int len) +{ + if (len > 0) { + char *str = NULL; + + switch (style->textTransform) { + case core::style::TEXT_TRANSFORM_NONE: + default: + break; + case core::style::TEXT_TRANSFORM_UPPERCASE: + str = layout->textToUpper(text + start, len); + break; + case core::style::TEXT_TRANSFORM_LOWERCASE: + str = layout->textToLower(text + start, len); + break; + case core::style::TEXT_TRANSFORM_CAPITALIZE: + { + /* \bug No way to know about non-ASCII punctuation. */ + bool initial_seen = false; + + for (int i = 0; i < start; i++) + if (!ispunct(text[i])) + initial_seen = true; + if (initial_seen) + break; + + int after = 0; + text += start; + while (ispunct(text[after])) + after++; + if (text[after]) + after = layout->nextGlyph(text, after); + if (after > len) + after = len; + + char *initial = layout->textToUpper(text, after); + int newlen = strlen(initial) + len-after; + str = (char *)malloc(newlen + 1); + strcpy(str, initial); + strncpy(str + strlen(str), text+after, len-after); + str[newlen] = '\0'; + free(initial); + break; + } + } + view->drawText(style->font, style->color, shading, x, y, + str ? str : text + start, str ? strlen(str) : len); + if (str) + free(str); + } +} + +/* * Draw a word of text. */ -void Textblock::drawText(int wordIndex, core::View *view,core::Rectangle *area, +void Textblock::drawWord(int wordIndex, core::View *view,core::Rectangle *area, int xWidget, int yWidgetBase) { Word *word = words->getRef(wordIndex); @@ -1288,9 +1328,8 @@ } yWorldBase = yWidgetBase + allocation.y; - view->drawText (style->font, style->color, - core::style::Color::SHADING_NORMAL, xWorld, yWorldBase, - word->content.text, strlen (word->content.text)); + drawText (view, style, core::style::Color::SHADING_NORMAL, xWorld, + yWorldBase, word->content.text, 0, strlen (word->content.text)); if (style->textDecoration) decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld, @@ -1312,14 +1351,12 @@ xStart = xWorld; if (firstCharIdx) - xStart += layout->textWidth (style->font, word->content.text, - firstCharIdx); + xStart += textWidth (word->content.text, 0, firstCharIdx, style); if (firstCharIdx == 0 && lastCharIdx == wordLen) width = word->size.width; else - width = layout->textWidth (style->font, - word->content.text + firstCharIdx, - lastCharIdx - firstCharIdx); + width = textWidth (word->content.text, firstCharIdx, + lastCharIdx - firstCharIdx, style); if (width > 0) { /* Highlight text */ core::style::Color *wordBgColor; @@ -1334,10 +1371,9 @@ style->font->ascent + style->font->descent); /* Highlight the text. */ - view->drawText (style->font, style->color, - core::style::Color::SHADING_INVERSE, xStart, - yWorldBase, word->content.text + firstCharIdx, - lastCharIdx - firstCharIdx); + drawText (view, style, core::style::Color::SHADING_INVERSE, xStart, + yWorldBase, word->content.text, firstCharIdx, + lastCharIdx - firstCharIdx); if (style->textDecoration) decorateText(view, style, core::style::Color::SHADING_INVERSE, @@ -1435,7 +1471,7 @@ yWidgetBase - line->boxAscent, word->size.width, line->boxAscent + line->boxDescent, false); } - drawText(wordIndex, view, area, xWidget, yWidgetBase); + drawWord(wordIndex, view, area, xWidget, yWidgetBase); } } if (word->effSpace > 0 && wordIndex < line->lastWord && @@ -1535,11 +1571,22 @@ word = words->getRef (wordIndex); lastXCursor = xCursor; xCursor += word->size.width + word->effSpace; - if (lastXCursor <= x && xCursor > x && - y > yWidgetBase - word->size.ascent && - y <= yWidgetBase + word->size.descent) { - *inSpace = x >= xCursor - word->effSpace; - return word; + if (lastXCursor <= x && xCursor > x) { + if (x >= xCursor - word->effSpace) { + if (wordIndex < line->lastWord && + (words->getRef(wordIndex + 1)->content.type != + core::Content::BREAK) && + y > yWidgetBase - word->spaceStyle->font->ascent && + y <= yWidgetBase + word->spaceStyle->font->descent) { + *inSpace = true; + return word; + } + } else { + if (y > yWidgetBase - word->size.ascent && + y <= yWidgetBase + word->size.descent) + return word; + } + break; } } @@ -1581,18 +1628,19 @@ word->origSpace = 0; word->effSpace = 0; word->content.space = false; - - //DBG_OBJ_ARRSET_NUM (page, "words.%d.size.width", page->num_words - 1, + word->content.breakType = core::Content::BREAK_NO; + + //DBG_OBJ_ARRSET_NUM (this, "words.%d.size.width", words->size() - 1, // word->size.width); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.size.descent", page->num_words - 1, + //DBG_OBJ_ARRSET_NUM (this, "words.%d.size.descent", words->size() - 1, // word->size.descent); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.size.ascent", page->num_words - 1, + //DBG_OBJ_ARRSET_NUM (this, "words.%d.size.ascent", words->size() - 1, // word->size.ascent); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", page->num_words - 1, + //DBG_OBJ_ARRSET_NUM (this, "words.%d.orig_space", words->size() - 1, // word->orig_space); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", page->num_words - 1, + //DBG_OBJ_ARRSET_NUM (this, "words.%d.effSpace", words->size() - 1, // word->eff_space); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", page->num_words - 1, + //DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", words->size() - 1, // word->content.space); word->style = style; @@ -1603,6 +1651,63 @@ return word; } +/* + * Get the width of a string of text. + */ +int Textblock::textWidth(const char *text, int start, int len, + core::style::Style *style) +{ + int ret = 0; + + if (len > 0) { + char *str = NULL; + + switch (style->textTransform) { + case core::style::TEXT_TRANSFORM_NONE: + default: + ret = layout->textWidth(style->font, text+start, len); + break; + case core::style::TEXT_TRANSFORM_UPPERCASE: + str = layout->textToUpper(text+start, len); + ret = layout->textWidth(style->font, str, strlen(str)); + break; + case core::style::TEXT_TRANSFORM_LOWERCASE: + str = layout->textToLower(text+start, len); + ret = layout->textWidth(style->font, str, strlen(str)); + break; + case core::style::TEXT_TRANSFORM_CAPITALIZE: + { + /* \bug No way to know about non-ASCII punctuation. */ + bool initial_seen = false; + + for (int i = 0; i < start; i++) + if (!ispunct(text[i])) + initial_seen = true; + if (initial_seen) { + ret = layout->textWidth(style->font, text+start, len); + } else { + int after = 0; + + text += start; + while (ispunct(text[after])) + after++; + if (text[after]) + after = layout->nextGlyph(text, after); + if (after > len) + after = len; + str = layout->textToUpper(text, after); + ret = layout->textWidth(style->font, str, strlen(str)) + + layout->textWidth(style->font, text+after, len-after); + } + break; + } + } + if (str) + free(str); + } + return ret; +} + /** * Calculate the size of a text word. */ @@ -1610,7 +1715,7 @@ core::style::Style *style, core::Requisition *size) { - size->width = layout->textWidth (style->font, text, len); + size->width = textWidth (text, 0, len, style); size->ascent = style->font->ascent; size->descent = style->font->descent; @@ -1669,7 +1774,7 @@ word->content.type = core::Content::TEXT; word->content.text = layout->textZone->strndup(text, len); - //DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1, + //DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", words->size() - 1, // word->content.text); wordWrap (words->size () - 1); @@ -1698,7 +1803,7 @@ word->content.type = core::Content::WIDGET; word->content.widget = widget; - //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", page->num_words - 1, + //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1, // word->content.widget); wordWrap (words->size () - 1); @@ -1709,9 +1814,8 @@ //DEBUG_MSG(DEBUG_REWRAP_LEVEL, // "Assigning parent_ref = %d to added word %d, " // "in page with %d word(s)\n", - // page->num_lines - 1, page->num_words - 1, page->num_words); -} - + // lines->size () - 1, words->size() - 1, words->size()); +} /** * Add an anchor to the page. "name" is copied, so no strdup is necessary for @@ -1764,24 +1868,31 @@ if (wordIndex >= 0) { Word *word = words->getRef(wordIndex); + // According to http://www.w3.org/TR/CSS2/text.html#white-space-model: + // "line breaking opportunities are determined based on the text prior + // to the white space collapsing steps". + // So we call addBreakOption () for each Textblock::addSpace () call. + // This is important e.g. to be able to break between foo and bar in: + // <span style="white-space:nowrap">foo </span> bar + addBreakOption (style); + if (!word->content.space) { word->content.space = true; word->effSpace = word->origSpace = style->font->spaceWidth + style->wordSpacing; - //DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw, - // page->words[nw].orig_space); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", nw, - // page->words[nw].eff_space); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw, - // page->words[nw].content.space); + //DBG_OBJ_ARRSET_NUM (this, "words.%d.origSpace", wordIndex, + // word->origSpace); + //DBG_OBJ_ARRSET_NUM (this, "words.%d.effSpace", wordIndex, + // word->effSpace); + //DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", wordIndex, + // word->content.space); word->spaceStyle->unref (); word->spaceStyle = style; style->ref (); } } } - /** * Cause a paragraph break @@ -1942,9 +2053,9 @@ } /* - * Any words added by a_Dw_page_add_... are not immediately (queued to + * Any words added by addWord() are not immediately (queued to * be) drawn, instead, this function must be called. This saves some - * calls to p_Dw_widget_queue_resize. + * calls to queueResize(). * */ void Textblock::flush () @@ -2185,9 +2296,8 @@ allocation->x += w->size.width + w->effSpace; } if (start > 0 && word->content.type == core::Content::TEXT) { - allocation->x += textblock->layout->textWidth (word->style->font, - word->content.text, - start); + allocation->x += textblock->textWidth (word->content.text, 0, start, + word->style); } allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent - word->size.ascent; @@ -2199,9 +2309,8 @@ if (start > 0 || end < wordEnd) { end = misc::min(end, wordEnd); /* end could be INT_MAX */ allocation->width = - textblock->layout->textWidth (word->style->font, - word->content.text + start, - end - start); + textblock->textWidth (word->content.text, start, end - start, + word->style); } } allocation->ascent = word->size.ascent; diff --git a/dw/textblock.hh b/dw/textblock.hh index 96cb40e..3f98f87 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -148,11 +148,8 @@ /* The following members contain accumulated values, from the top * down to the line before. */ int maxLineWidth; /* maximum of all line widths */ - int maxWordMin; /* maximum of all word minima */ + int maxParMin; /* maximum of all paragraph minima */ int maxParMax; /* maximum of all paragraph maxima */ - int parMin; /* the minimal total width down from the last - * paragraph start, to the *beginning* of the - * line */ int parMax; /* the maximal total width down from the last * paragraph start, to the *beginning* of the * line */ @@ -240,7 +237,8 @@ int availWidth, availAscent, availDescent; int lastLineWidth; - int lastLineParMin; + int lastLineParMin; /* width of the current non-breakable word sequence + * used by wordWrap () */ int lastLineParMax; int wrapRef; /* [0 based] */ @@ -256,6 +254,10 @@ void queueDrawRange (int index1, int index2); void getWordExtremes (Word *word, core::Extremes *extremes); + inline bool canBreakAfter (Word *word) + { + return word->content.breakType == core::Content::BREAK_OK; + } void markChange (int ref); void justifyLine (Line *line, int availWidth); Line *addLine (int wordInd, bool newPar); @@ -264,7 +266,10 @@ void decorateText(core::View *view, core::style::Style *style, core::style::Color::Shading shading, int x, int yBase, int width); - void drawText(int wordIndex, core::View *view, core::Rectangle *area, + void drawText(core::View *view, core::style::Style *style, + core::style::Color::Shading shading, int x, int y, + const char *text, int start, int len); + void drawWord(int wordIndex, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase); void drawSpace(int wordIndex, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase); @@ -275,9 +280,10 @@ Word *addWord (int width, int ascent, int descent, core::style::Style *style); + int textWidth (const char *text, int start, int len, + core::style::Style *style); void calcTextSize (const char *text, size_t len, core::style::Style *style, core::Requisition *size); - /** * \brief Returns the x offset (the indentation plus any offset needed for @@ -382,6 +388,14 @@ void addWidget (core::Widget *widget, core::style::Style *style); bool addAnchor (const char *name, core::style::Style *style); void addSpace(core::style::Style *style); + inline void addBreakOption (core::style::Style *style) + { + int wordIndex = words->size () - 1; + if (wordIndex >= 0 && + style->whiteSpace != core::style::WHITE_SPACE_NOWRAP && + style->whiteSpace != core::style::WHITE_SPACE_PRE) + words->getRef(wordIndex)->content.breakType = core::Content::BREAK_OK; + } void addParbreak (int space, core::style::Style *style); void addLinebreak (core::style::Style *style); diff --git a/dw/types.hh b/dw/types.hh index a8c244c..eac834f 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -194,11 +194,16 @@ REAL_CONTENT = 0xff ^ (START | END), SELECTION_CONTENT = TEXT | WIDGET | BREAK }; + enum BreakType { + BREAK_NO, + BREAK_OK + }; /* Content is embedded in struct Word therefore we * try to be space efficient. */ short type; bool space; + unsigned char breakType; union { const char *text; Widget *widget; diff --git a/install-sh b/install-sh index 68b8100..6781b98 100755 --- a/install-sh +++ b/install-sh @@ -1,23 +1,38 @@ #!/bin/sh -# -# $NetBSD: install-sh.in,v 1.5 2010/10/08 19:57:05 tez Exp $ -# This script now also installs multiple files, but might choke on installing -# multiple files with spaces in the file names. -# # install - install a program, script, or datafile -# This comes from X11R5 (mit/util/scripts/install.sh). -# -# Copyright 1991 by the Massachusetts Institute of Technology -# -# Permission to use, copy, modify, distribute, and sell this software and its -# documentation for any purpose is hereby granted without fee, provided that -# the above copyright notice appear in all copies and that both that -# copyright notice and this permission notice appear in supporting -# documentation, and that the name of M.I.T. not be used in advertising or -# publicity pertaining to distribution of the software without specific, -# written prior permission. M.I.T. makes no representations about the -# suitability of this software for any purpose. It is provided "as is" -# without express or implied warranty. + +scriptversion=2009-04-28.21; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it @@ -26,266 +41,480 @@ # This script is compatible with the BSD install script, but was written # from scratch. +nl=' +' +IFS=" "" $nl" + # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -awkprog="${AWKPROG-awk}" -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -instcmd="$cpprog" -pathcompchmodcmd="$chmodprog 755" -chmodcmd="$chmodprog 755" -chowncmd="" -chgrpcmd="" -stripcmd="" -stripflags="" +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -msrc="" -dst="" -dir_arg="" -suffix="" -suffixfmt="" - -while [ x"$1" != x ]; do - case $1 in - -b) suffix=".old" - shift - continue;; - - -B) suffixfmt="$2" - shift - shift - continue;; - - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -S) stripcmd="$stripprog" - stripflags="-S $2 $stripflags" - shift - shift - continue;; - - *) if [ x"$msrc" = x ] +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + trap '(exit $?); exit' 1 2 13 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dst_arg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then - msrc="$dst" + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" else - msrc="$msrc $dst" + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi - src="$dst" - dst="$1" - shift - continue;; + trap '' 0;; + esac;; esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + -*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test -z "$d" && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi done -if [ x"$dir_arg" = x ] -then - dstisfile="" - if [ ! -d "$dst" ] - then - if [ x"$msrc" = x"$src" ] - then - dstisfile=true - else - echo "install: destination is not a directory" - exit 1 - fi - fi -else - msrc="$msrc $dst" -fi - -if [ x"$msrc" = x ] -then - echo "install: no destination specified" - exit 1 -fi - -for srcarg in $msrc; do - -if [ x"$dir_arg" != x ]; then - - dstarg="$srcarg" -else - dstarg="$dst" - -# Waiting for this to be detected by the "$instcmd $srcarg $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f "$srcarg" ] - then - doinst="$instcmd" - elif [ -d "$srcarg" ] - then - echo "install: $srcarg: not a regular file" - exit 1 - elif [ "$srcarg" = "/dev/null" ] - then - doinst="$cpprog" - else - echo "install: $srcarg does not exist" - exit 1 - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d "$dstarg" ] - then - dstarg="$dstarg"/`basename "$srcarg"` - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo "$dstarg" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $doit $mkdirprog "${pathcomp}" - if [ x"$chowncmd" != x ]; then $doit $chowncmd "${pathcomp}"; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "${pathcomp}"; else true ; fi && - if [ x"$pathcompchmodcmd" != x ]; then $doit $pathcompchmodcmd "${pathcomp}"; else true ; fi - - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - - if [ x"$dir_arg" != x ] - then - if [ -d "$dstarg" ]; then - true - else - $doit $mkdirprog "$dstarg" && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dstarg"; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dstarg"; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dstarg"; else true ; fi - fi - else - - if [ x"$dstisfile" = x ] - then - file=$srcarg - else - file=$dst - fi - - dstfile=`basename "$file"` - dstfinal="$dstdir/$dstfile" - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Make a backup file name in the proper directory. - case x$suffixfmt in - *%*) suffix=`echo x | - $awkprog -v bname="$dstfinal" -v fmt="$suffixfmt" ' - { cnt = 0; - do { - sfx = sprintf(fmt, cnt++); - name = bname sfx; - } while (system("test -f " name) == 0); - print sfx; }' -`;; - x) ;; - *) suffix="$suffixfmt";; - esac - dstbackup="$dstfinal$suffix" - -# Move or copy the file name to the temp name - - $doit $doinst $srcarg "$dsttmp" && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $stripflags "$dsttmp"; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else true;fi && - -# Now rename the file to the real destination. - - if [ x"$suffix" != x ] && [ -f "$dstfinal" ] - then - $doit $mvcmd "$dstfinal" "$dstbackup" - else - $doit $rmcmd -f "$dstfinal" - fi && - $doit $mvcmd "$dsttmp" "$dstfinal" - fi - -done && - - -exit 0 +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/lout/container.hh b/lout/container.hh index 68a15da..8a9e3e0 100644 --- a/lout/container.hh +++ b/lout/container.hh @@ -7,7 +7,7 @@ * \brief This namespace contains a framework for container classes, which * members are instances of object::Object. * - * A common problem in languanges without garbage collection is, where the + * A common problem in languages without garbage collection is, where the * children belong to, and so, who is responsible to delete them (instantiation * is always done by the caller). This information is here told to the * collections, each container has a constructor with the parameter diff --git a/lout/misc.hh b/lout/misc.hh index e78e757..2a8584e 100644 --- a/lout/misc.hh +++ b/lout/misc.hh @@ -41,6 +41,27 @@ inline int roundInt(double d) { return (int) ((d > 0) ? (d + 0.5) : (d - 0.5)); +} + +inline int AsciiTolower(char c) +{ + return ((c >= 'A' && c <= 'Z') ? c + 0x20 : c); +} + +inline int AsciiToupper(char c) +{ + return ((c >= 'a' && c <= 'z') ? c - 0x20 : c); +} + +inline int AsciiStrcasecmp(const char *s1, const char *s2) +{ + int ret = 0; + + while ((*s1 || *s2) && !(ret = AsciiTolower(*s1) - AsciiTolower(*s2))) { + s1++; + s2++; + } + return ret; } /** diff --git a/src/IO/about.c b/src/IO/about.c index 0d9214f..4ccd00e 100644 --- a/src/IO/about.c +++ b/src/IO/about.c @@ -156,7 +156,7 @@ " <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n" " <tr>\n" " <td>  \n" -" <td><a href='http://freshmeat.net/'>FreshMeat</a>\n" +" <td><a href='http://freecode.com/'>Freecode</a>\n" " <tr>\n" " <td>  \n" " <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU\n" @@ -211,8 +211,8 @@ " <td bgcolor='#FFFFFF'>\n" " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n" " <p>\n" -" Dillo is Free Software under the terms of version 3 of the\n" -" <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n" +" The Dillo web browser is Free Software under the terms of version 3 of\n" +" the <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n" " This means you have four basic freedoms:\n" " <ul>\n" " <li>Freedom to use the program any way you see fit.\n" @@ -234,26 +234,24 @@ "<tr>\n" " <td bgcolor='#CCCCCC'>\n" " <h4>Release overview</h4>\n" -" September 21, 2011\n" +" December 05, 2011\n" "<tr>\n" " <td bgcolor='#FFFFFF'>\n" " <table border='0' cellspacing='0' cellpadding='5'>\n" " <tr>\n" " <td>\n" "<p>\n" -"The dillo-3.0.1 release brings improved privacy and some new options for\n" -"configuring the user interface.\n" -"<p>\n" -"Dillo3 is a port to FLTK-1.3, which is big news because FLTK-1.3.0 was\n" -"<a href='http://fltk.org/articles.php?L1086'>released</a>\n" -"in June, clearing the way for Dillo to return to those distributions\n" -"which had excluded Dillo2 due to FLTK2 never being officially released.\n" -"<p>\n" -"After this release, the core team will focus on implementing the CSS\n" +"dillo-3.0.2 brings you some new bits and pieces, as listed below :)\n" +"<p>\n" +"After this release, the core team plans to focus on implementing the CSS\n" "feature of floating elements. This will <em>greatly</em> improve dillo's\n" "web page rendering since many sites have adopted floats instead of tables.\n" "<p>\n" -"The new dillo3 has shown excellent stability in our experience.\n" +"Dillo3 uses the FLTK GUI toolkit's 1.3.x series. In June, fltk-1.3.0 was\n" +"<a href='http://fltk.org/articles.php?L1086'>released</a>,\n" +"clearing the way for Dillo to return to those distributions\n" +"which had excluded Dillo2 due to FLTK2 never being officially released.\n" +"<p>\n" "The core team welcomes developers willing to join our workforce.\n" "<p>\n" " </table>\n" @@ -275,49 +273,12 @@ " <table border='0' cellspacing='0' cellpadding='5'>\n" " <tr>\n" " <td>\n" -"dillo-3.0.1:\n" "<ul>\n" -"<li>Privacy -- never send cookies when making third-party requests, and\n" -" never accept cookies in the responses to these requests.\n" -"<li>Add preference for UI theme.\n" -"<li>Allow key bindings for paging left/right.\n" -"<li>Add show_quit_dialog dillorc option.\n" -"</ul>\n" -"dillo-3.0:\n" -"<ul>\n" -"<li>Ported Dillo to FLTK-1.3.\n" -"<li>Native build on OSX.\n" -"<li>Default binding for close-all changed from Alt-q to Ctrl-q.\n" -"<li>Default binding for close-tab changed from Ctrl-q to Ctrl-w.\n" -"<li>Default binding for left-tab changed to Shift-Ctrl-Tab.\n" -"<li>Rewrote the User Interface: much simpler design and event handling.\n" -"<li>Added on-the-fly panel resize (tiny/small/medium and normal/small icons).\n" -"<li>'hide-panels' key action now hides the findbar if present,\n" -" and toggles display of the control panels otherwise.\n" -"<li>Allow multiple search engines to be set in dillorc, with a menu\n" -" in the web search dialog to select between them.\n" -"<li>Added an optional label to dillorc's search_url.\n" -" Format: \"[<label> ]<url>\"\n" -"<li>Add right_click_closes_tab preference (default is middle click).\n" -"<li>Allow binding to non-ASCII keys and multimedia keys.\n" -"<li>Avoid a certificate dialog storm on some HTTPS sites (BUG#868).\n" -"<li>Enable line wrapping for <textarea>. (BUG#903)\n" -"<li>Avoid double render after going Back or Forward\n" -" (it takes half the time now!)\n" -"<li>Implemented a custom tabs handler (to allow fine control of it).\n" -"<li>Rewrote dw's crossing-events dispatcher (avoids redundant events).\n" -"<li>Fixed a years old bug: stamped tooltips when scrolling with keyboard.\n" -"<li>Fixed a border case in URL resolver: empty path + {query|fragment}\n" -" (BUG#948)\n" -"<li>Cancel the expected URL after offering a download (BUG#982)\n" -"<li>Eliminated a pack of 22 compiler warnings (gcc-4.6.1 amd64)\n" -"<li>Removed 'large' option of panel_size preference.\n" -"<li>Removed --enable-ansi configure option.\n" -"<li>Limit saved cookie size.\n" -"<li>Wrap image alt text.\n" -"<li>Added support for CSS adjacent sibling selectors.\n" -"<li>Fix redraw loops and reenabled limit_text_width dillorc option.\n" -"<li>Collapse parent's and first child's top margin.\n" +"<li>Digest authentication.\n" +"<li>Rework line breaking and fix white-space:nowrap handling.\n" +"<li>text-transform property.\n" +"<li>Locale-independent ASCII character case handling (fixes Turkic locales).\n" +"<li>Bind Ctrl-{PageUp,PageDown} to tab-{previous,next}.\n" "</ul>\n" " </table>\n" "</table>\n" @@ -338,12 +299,12 @@ "<ul>\n" " <li> There's a\n" " <a href='http://www.dillo.org/dillorc'>dillorc</a>\n" -" (readable config) file within the tarball; It is well-commented\n" +" (readable config) file inside the tarball. It is well-commented\n" " and has plenty of options to customize dillo, so <STRONG>copy\n" " it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n" " modify it to your taste.\n" " <li> Documentation for developers is in the <CODE>/doc</CODE>\n" -" dir within the tarball; you can find directions on everything\n" +" dir inside the tarball; you can find directions on everything\n" " else at the home page.\n" " <li> The right mouse button brings up a context-sensitive menu\n" " (available on pages, links, images, forms, the Back and Forward buttons,\n" diff --git a/src/IO/http.c b/src/IO/http.c index 41ee137..3e87f91 100644 --- a/src/IO/http.c +++ b/src/IO/http.c @@ -171,10 +171,11 @@ } else if (a_Web_valid(sd->web)) { /* start connecting the socket */ if (Http_connect_socket(sd->Info) < 0) { + ChainLink *Info = sd->Info; MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err)); - a_Chain_bfcb(OpAbort, sd->Info, NULL, "Both"); - dFree(sd->Info); - Http_socket_free(VOIDP2INT(sd->Info->LocalKey)); + a_Chain_bfcb(OpAbort, Info, NULL, "Both"); + Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */ + dFree(Info); } else { sd->connected_to = hc->host; hc->active_connections++; @@ -202,7 +203,7 @@ Http_connect_queued_sockets(hc); if (hc->active_connections == 0) Http_host_connection_remove(hc); - } + } dFree(S); } } @@ -274,23 +275,22 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, bool_t use_proxy) { - const char *auth; - char *ptr, *cookies, *referer; + char *ptr, *cookies, *referer, *auth; Dstr *query = dStr_new(""), - *full_path = dStr_new(""), + *request_uri = dStr_new(""), *proxy_auth = dStr_new(""); if (use_proxy) { - dStr_sprintfa(full_path, "%s%s", + dStr_sprintfa(request_uri, "%s%s", URL_STR(url), (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/"); - if ((ptr = strrchr(full_path->str, '#'))) - dStr_truncate(full_path, ptr - full_path->str); + if ((ptr = strrchr(request_uri->str, '#'))) + dStr_truncate(request_uri, ptr - request_uri->str); if (HTTP_Proxy_Auth_base64) dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n", HTTP_Proxy_Auth_base64); } else { - dStr_sprintfa(full_path, "%s%s%s%s", + dStr_sprintfa(request_uri, "%s%s%s%s", URL_PATH(url), URL_QUERY_(url) ? "?" : "", URL_QUERY(url), @@ -298,7 +298,7 @@ } cookies = a_Cookies_get_query(url, requester); - auth = a_Auth_get_auth_str(url); + auth = a_Auth_get_auth_str(url, request_uri->str); referer = Http_get_referer(url); if (URL_FLAGS(url) & URL_Post) { Dstr *content_type = Http_make_content_type(url); @@ -319,7 +319,7 @@ "Content-Type: %s\r\n" "%s" /* cookies */ "\r\n", - full_path->str, HTTP_Language_hdr, auth ? auth : "", + request_uri->str, HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent, (long)URL_DATA(url)->len, content_type->str, cookies); @@ -342,7 +342,7 @@ "User-Agent: %s\r\n" "%s" /* cookies */ "\r\n", - full_path->str, + request_uri->str, (URL_FLAGS(url) & URL_E2EQuery) ? "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "", HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url), @@ -350,8 +350,9 @@ } dFree(referer); dFree(cookies); - - dStr_free(full_path, TRUE); + dFree(auth); + + dStr_free(request_uri, TRUE); dStr_free(proxy_auth, TRUE); _MSG("Query: {%s}\n", dStr_printable(query, 8192)); return query; @@ -479,7 +480,7 @@ for (p = np; (tok = dStrsep(&p, " ")); ) { int start = host_len - strlen(tok); - if (start >= 0 && dStrcasecmp(host + start, tok) == 0) { + if (start >= 0 && dStrAsciiCasecmp(host + start, tok) == 0) { /* no_proxy token is suffix of host string */ ret = 0; break; @@ -718,7 +719,7 @@ for (i = 0; i < dList_length(host_connections); i++) { hc = (HostConnection_t*) dList_nth_data(host_connections, i); - if (dStrcasecmp(host, hc->host) == 0) + if (dStrAsciiCasecmp(host, hc->host) == 0) return hc; } diff --git a/src/IO/mime.c b/src/IO/mime.c index 19dc601..3606d23 100644 --- a/src/IO/mime.c +++ b/src/IO/mime.c @@ -67,7 +67,7 @@ if (Size) { for ( i = 0; i < MimeMinItemsSize; ++i ) - if (dStrncasecmp(Key, MimeMinItems[i].Name, Size) == 0) + if (dStrnAsciiCasecmp(Key, MimeMinItems[i].Name, Size) == 0) return MimeMinItems[i].Data; } return NULL; @@ -83,7 +83,7 @@ if (Size) { for ( i = 0; i < MimeMajItemsSize; ++i ) - if (dStrncasecmp(Key, MimeMajItems[i].Name, Size) == 0) + if (dStrnAsciiCasecmp(Key, MimeMajItems[i].Name, Size) == 0) return MimeMajItems[i].Data; } return NULL; diff --git a/src/Makefile.am b/src/Makefile.am index 9de7eed..5e887d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,6 +35,10 @@ cookies.h \ auth.c \ auth.h \ + md5.c \ + md5.h \ + digest.c \ + digest.h \ colors.c \ colors.h \ binaryconst.h \ diff --git a/src/Makefile.in b/src/Makefile.in index 543907b..34a83d1 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -51,14 +51,14 @@ PROGRAMS = $(bin_PROGRAMS) am_dillo_OBJECTS = dillo.$(OBJEXT) paths.$(OBJEXT) ui.$(OBJEXT) \ uicmd.$(OBJEXT) bw.$(OBJEXT) cookies.$(OBJEXT) auth.$(OBJEXT) \ - colors.$(OBJEXT) misc.$(OBJEXT) history.$(OBJEXT) \ - prefs.$(OBJEXT) prefsparser.$(OBJEXT) keys.$(OBJEXT) \ - url.$(OBJEXT) bitvec.$(OBJEXT) klist.$(OBJEXT) chain.$(OBJEXT) \ - utf8.$(OBJEXT) timeout.$(OBJEXT) dialog.$(OBJEXT) \ - web.$(OBJEXT) nav.$(OBJEXT) cache.$(OBJEXT) decode.$(OBJEXT) \ - dicache.$(OBJEXT) capi.$(OBJEXT) css.$(OBJEXT) \ - cssparser.$(OBJEXT) styleengine.$(OBJEXT) plain.$(OBJEXT) \ - html.$(OBJEXT) form.$(OBJEXT) table.$(OBJEXT) \ + md5.$(OBJEXT) digest.$(OBJEXT) colors.$(OBJEXT) misc.$(OBJEXT) \ + history.$(OBJEXT) prefs.$(OBJEXT) prefsparser.$(OBJEXT) \ + keys.$(OBJEXT) url.$(OBJEXT) bitvec.$(OBJEXT) klist.$(OBJEXT) \ + chain.$(OBJEXT) utf8.$(OBJEXT) timeout.$(OBJEXT) \ + dialog.$(OBJEXT) web.$(OBJEXT) nav.$(OBJEXT) cache.$(OBJEXT) \ + decode.$(OBJEXT) dicache.$(OBJEXT) capi.$(OBJEXT) \ + css.$(OBJEXT) cssparser.$(OBJEXT) styleengine.$(OBJEXT) \ + plain.$(OBJEXT) html.$(OBJEXT) form.$(OBJEXT) table.$(OBJEXT) \ bookmark.$(OBJEXT) dns.$(OBJEXT) gif.$(OBJEXT) jpeg.$(OBJEXT) \ png.$(OBJEXT) imgbuf.$(OBJEXT) image.$(OBJEXT) menu.$(OBJEXT) \ dpiapi.$(OBJEXT) findbar.$(OBJEXT) xembed.$(OBJEXT) @@ -296,6 +296,10 @@ cookies.h \ auth.c \ auth.h \ + md5.c \ + md5.h \ + digest.c \ + digest.h \ colors.c \ colors.h \ binaryconst.h \ @@ -475,6 +479,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dicache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/digest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dillo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpiapi.Po@am__quote@ @@ -488,6 +493,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/klist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nav.Po@am__quote@ diff --git a/src/auth.c b/src/auth.c index 8943c3f..d6ff081 100644 --- a/src/auth.c +++ b/src/auth.c @@ -2,6 +2,7 @@ * File: auth.c * * Copyright 2008 Jeremy Henty <onepoint@starurchin.org> + * Copyright 2009 Justus Winter <4winter@informatik.uni-hamburg.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,19 +21,20 @@ #include "msg.h" #include "misc.h" #include "dialog.hh" +#include "digest.h" #include "../dlib/dlib.h" - typedef struct { int ok; + enum AuthParseHTTPAuthType_t type; const char *realm; + const char *nonce; + const char *opaque; + int stale; + enum AuthParseDigestAlgorithm_t algorithm; + const char *domain; + enum AuthParseDigestQOP_t qop; } AuthParse_t; - -typedef struct { - char *name; - Dlist *paths; /* stripped of any trailing '/', so the root path is "" */ - char *authorization; /* the authorization request header */ -} AuthRealm_t; typedef struct { char *scheme; @@ -41,7 +43,7 @@ } AuthHost_t; typedef struct { - const char *realm_name; + const AuthParse_t *auth_parse; const DilloUrl *url; } AuthDialogData_t; @@ -62,7 +64,14 @@ { AuthParse_t *auth_parse = dNew(AuthParse_t, 1); auth_parse->ok = 0; + auth_parse->type = TYPENOTSET; auth_parse->realm = NULL; + auth_parse->nonce = NULL; + auth_parse->opaque = NULL; + auth_parse->stale = 0; + auth_parse->algorithm = ALGORITHMNOTSET; + auth_parse->domain = NULL; + auth_parse->qop = QOPNOTSET; return auth_parse; } @@ -70,6 +79,9 @@ { if (auth_parse) { dFree((void *)auth_parse->realm); + dFree((void *)auth_parse->nonce); + dFree((void *)auth_parse->opaque); + dFree((void *)auth_parse->domain); dFree(auth_parse); } } @@ -92,191 +104,250 @@ static int Auth_is_token_char(char c) { const char *invalid = "\"()<>@,;:\\[]?=/{} \t"; - return (strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1; -} - -/* - * Unquote the content of a quoted string. + return (!isascii(c) || strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1; +} + +/* + * Unquote the content of a (potentially) quoted string. * Return: newly allocated unquoted content. * * Arguments: - * quoted: pointer to the first char *after* the initial double quote. - * size: the number of chars in the result, *not* including a final '\0'. + * valuep: pointer to a pointer to the first char. * * Preconditions: - * quoted points to a correctly quoted and escaped string. - * size is the number of characters in the quoted string, *after* - * removing escape characters. - * - */ -static const char *Auth_unquote_value(const char *quoted, int size) -{ - char c, *value, *value_ptr; - value_ptr = value = dNew(char, size + 1); - while ((c = *quoted++) != '"') - *value_ptr++ = (c == '\\') ? *quoted++ : c; - *value_ptr = '\0'; - return value; -} - -/* - * Parse a quoted string. Save the result as the auth realm if required. + * *valuep points to a correctly quoted and escaped string. + * + * Postconditions: + * *valuep points to the first not processed char. + * + */ +static Dstr *Auth_unquote_value(char **valuep) +{ + char c, quoted; + char *value = *valuep; + Dstr *result; + + while (*value == ' ' || *value == '\t') + value++; + + if ((quoted = *value == '"')) + value++; + + result = dStr_new(NULL); + while ((c = *value) && + (( quoted && c != '"') || + (!quoted && Auth_is_token_char(c)))) { + dStr_append_c(result, (c == '\\' && value[1]) ? *++value : c); + value++; + } + + if (quoted && *value == '\"') + value++; + *valuep = value; + return result; +} + +typedef int (Auth_parse_token_value_callback_t)(AuthParse_t *auth_parse, + char *token, + const char *value); + + +/* + * Parse authentication challenge into token-value pairs + * and feed them into the callback function. + * + * The parsing is aborted should the callback function return 0. + * * Return: 1 if the parse succeeds, 0 otherwise. */ -static int Auth_parse_quoted_string(AuthParse_t *auth_parse, int set_realm, - char **auth) -{ - char *value; - int size; - - /* parse the '"' */ - switch (*(*auth)++) { - case '"': - break; - case '\0': - case ',': - MSG("auth.c: missing Basic auth token value after '='\n"); - return 0; - break; - default: - MSG("auth.c: garbage in Basic auth after '='\n"); - return 0; - break; - } - - /* parse the rest */ - value = *auth; - size = 0; - while (1) { +static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth, + Auth_parse_token_value_callback_t *callback) +{ + char keep_going, expect_quoted; + char *token, *beyond_token; + Dstr *value; + size_t *token_size; + + while (**auth) { + _MSG("Auth_parse_token_value: remaining: %s\n", *auth); + + /* parse a token */ + token = *auth; + + token_size = 0; + while (Auth_is_token_char(**auth)) { + (*auth)++; + token_size++; + } + if (token_size == 0) { + MSG("Auth_parse_token_value: missing auth token\n"); + return 0; + } + beyond_token = *auth; + /* skip linear whitespace characters */ + while (**auth == ' ' || **auth == '\t') + (*auth)++; + + /* parse the '=' */ switch (*(*auth)++) { - case '"': - if (set_realm) { - dFree((void *)auth_parse->realm); - auth_parse->realm = Auth_unquote_value(value, size); - auth_parse->ok = 1; - } - return 1; + case '=': + *beyond_token = '\0'; break; case '\0': - MSG("auth.c: auth string ended inside quoted string value\n"); + case ',': + MSG("Auth_parse_token_value: missing auth token value\n"); return 0; break; - case '\\': - /* end of string? */ - if (!*(*auth)++) { - MSG("auth.c: " - "auth string ended inside quoted string value " - "immediately after \\\n"); - return 0; + default: + MSG("Auth_parse_token_value: garbage after auth token\n"); + return 0; + break; + } + + value = Auth_unquote_value(auth); + expect_quoted = !(strcmp(token, "stale") == 0 || + strcmp(token, "algorithm") == 0); + + if (((*auth)[-1] == '"') != expect_quoted) + MSG_WARN("Auth_parse_token_value: " + "Values for key %s should%s be quoted.\n", + token, expect_quoted ? "" : " not"); + + keep_going = callback(auth_parse, token, value->str); + dStr_free(value, 1); + if (!keep_going) + break; + + /* skip ' ' and ',' */ + while ((**auth == ' ') || (**auth == ',')) + (*auth)++; + } + return 1; +} + +static int Auth_parse_basic_challenge_cb(AuthParse_t *auth_parse, char *token, + const char *value) +{ + if (dStrAsciiCasecmp("realm", token) == 0) { + if (!auth_parse->realm) + auth_parse->realm = strdup(value); + return 0; /* end parsing */ + } else + MSG("Auth_parse_basic_challenge_cb: Ignoring unknown parameter: %s = " + "'%s'\n", token, value); + return 1; +} + +static int Auth_parse_digest_challenge_cb(AuthParse_t *auth_parse, char *token, + const char *value) +{ + const char *const fn = "Auth_parse_digest_challenge_cb"; + + if (!dStrAsciiCasecmp("realm", token) && !auth_parse->realm) + auth_parse->realm = strdup(value); + else if (!strcmp("domain", token) && !auth_parse->domain) + auth_parse->domain = strdup(value); + else if (!strcmp("nonce", token) && !auth_parse->nonce) + auth_parse->nonce = strdup(value); + else if (!strcmp("opaque", token) && !auth_parse->opaque) + auth_parse->opaque = strdup(value); + else if (strcmp("stale", token) == 0) { + if (dStrAsciiCasecmp("true", value) == 0) + auth_parse->stale = 1; + else if (dStrAsciiCasecmp("false", value) == 0) + auth_parse->stale = 0; + else { + MSG("%s: Invalid stale value: %s\n", fn, value); + return 0; + } + } else if (strcmp("algorithm", token) == 0) { + if (strcmp("MD5", value) == 0) + auth_parse->algorithm = MD5; + else if (strcmp("MD5-sess", value) == 0) { + /* auth_parse->algorithm = MD5SESS; */ + MSG("%s: MD5-sess algorithm disabled (not tested because 'not " + "correctly implemented yet' in Apache 2.2)\n", fn); + return 0; + } else { + MSG("%s: Unknown algorithm: %s\n", fn, value); + return 0; + } + } else if (strcmp("qop", token) == 0) { + while (*value) { + int len = strcspn(value, ", \t"); + if (len == 4 && strncmp("auth", value, 4) == 0) { + auth_parse->qop = AUTH; + break; } - /* fall through to the next case */ - default: - size++; - break; - } - } -} - -/* - * Parse a token-value pair. - * Return: 1 if the parse succeeds, 0 otherwise. - */ -static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth) -{ - char *token; - int token_size, set_realm; - static const char realm_token[] = "realm"; - - /* parse a token */ - token = *auth; - token_size = 0; - while (Auth_is_token_char(**auth)) { - (*auth)++; - token_size++; - } - if (token_size == 0) { - MSG("auth.c: Auth_parse_token_value: " - "missing Basic auth token\n"); - return 0; - } - - /* skip space characters */ - while (**auth == ' ') - (*auth)++; - - /* parse the '=' */ - switch (*(*auth)++) { - case '=': - break; - case '\0': - case ',': - MSG("auth.c: Auth_parse_token_value: " - "missing Basic auth token value\n"); - return 0; - break; - default: - MSG("auth.c: Auth_parse_token_value: " - "garbage after Basic auth token\n"); - return 0; - break; - } - - /* skip space characters */ - while (**auth == ' ') - (*auth)++; - - /* is this value the realm? */ - set_realm = - auth_parse->realm == NULL && - dStrncasecmp(realm_token,token,token_size) == 0 && - strlen(realm_token) == (size_t)token_size; - - return Auth_parse_quoted_string(auth_parse, set_realm, auth); -} - -static void Auth_parse_auth_basic(AuthParse_t *auth_parse, char **auth) -{ - int token_value_pairs_found; - + if (len == 8 && strncmp("auth-int", value, 8) == 0) { + /* auth_parse->qop = AUTHINT; */ + /* Keep searching; maybe we'll find an "auth" yet. */ + MSG("%s: auth-int qop disabled (not tested because 'not " + "implemented yet' in Apache 2.2)\n", fn); + } else { + MSG("%s: Unknown qop value in %s\n", fn, value); + } + value += len; + while (*value == ' ' || *value == '\t') + value++; + if (*value == ',') + value++; + while (*value == ' ' || *value == '\t') + value++; + } + } else { + MSG("%s: Ignoring unknown parameter: %s = '%s'\n", fn, token, value); + } + return 1; +} + +static void Auth_parse_challenge_args(AuthParse_t *auth_parse, + char **challenge, + Auth_parse_token_value_callback_t *cb) +{ /* parse comma-separated token-value pairs */ - token_value_pairs_found = 0; while (1) { /* skip space and comma characters */ - while (**auth == ' ' || **auth == ',') - (*auth)++; + while (**challenge == ' ' || **challenge == ',') + (*challenge)++; /* end of string? */ - if (!**auth) + if (!**challenge) break; /* parse token-value pair */ - if (!Auth_parse_token_value(auth_parse, auth)) + if (!Auth_parse_token_value(auth_parse, challenge, cb)) break; - token_value_pairs_found = 1; - } - - if (!token_value_pairs_found) { - MSG("auth.c: Auth_parse_auth_basic: " - "missing Basic auth token-value pairs\n"); - return; - } - - if (!auth_parse->realm) { - MSG("auth.c: Auth_parse_auth_basic: " - "missing Basic auth realm\n"); - return; - } -} - -static void Auth_parse_auth(AuthParse_t *auth_parse, char *auth) -{ - _MSG("auth.c: Auth_parse_auth: auth = '%s'\n", auth); - if (dStrncasecmp(auth, "Basic ", 6) == 0) { - auth += 6; - Auth_parse_auth_basic(auth_parse, &auth); + } + + if (auth_parse->type == BASIC) { + if (auth_parse->realm) { + auth_parse->ok = 1; + } else { + MSG("Auth_parse_challenge_args: missing Basic auth realm\n"); + return; + } + } else if (auth_parse->type == DIGEST) { + if (auth_parse->realm && auth_parse->nonce) { + auth_parse->ok = 1; + } else { + MSG("Auth_parse_challenge_args: Digest challenge incomplete\n"); + return; + } + } +} + +static void Auth_parse_challenge(AuthParse_t *auth_parse, char *challenge) +{ + Auth_parse_token_value_callback_t *cb; + + MSG("auth.c: Auth_parse_challenge: challenge = '%s'\n", challenge); + if (auth_parse->type == DIGEST) { + challenge += 7; + cb = Auth_parse_digest_challenge_cb; } else { - MSG("auth.c: Auth_parse_auth: " - "unknown authorization scheme: auth = {%s}\n", - auth); - } + challenge += 6; + cb = Auth_parse_basic_challenge_cb; + } + Auth_parse_challenge_args(auth_parse, &challenge, cb); } /* @@ -288,8 +359,8 @@ int i; for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++) - if (((dStrcasecmp(URL_SCHEME(url), host->scheme) == 0) && - (dStrcasecmp(URL_AUTHORITY(url), host->authority) == 0))) + if (((dStrAsciiCasecmp(URL_SCHEME(url), host->scheme) == 0) && + (dStrAsciiCasecmp(URL_AUTHORITY(url), host->authority) == 0))) return host; return NULL; @@ -305,7 +376,7 @@ int i; for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) - if (strcmp(realm->name,name) == 0) + if (strcmp(realm->name, name) == 0) return realm; return NULL; @@ -319,9 +390,8 @@ { AuthRealm_t *realm_best, *realm; int i, j; - int match_length; - - match_length = 0; + int match_length = 0; + realm_best = NULL; for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) { char *realm_path; @@ -339,6 +409,24 @@ return realm_best; } +static void Auth_realm_delete(AuthRealm_t *realm) +{ + int i; + + MSG("Auth_realm_delete: \"%s\"\n", realm->name); + for (i = dList_length(realm->paths) - 1; i >= 0; i--) + dFree(dList_nth_data(realm->paths, i)); + dList_free(realm->paths); + dFree(realm->name); + dFree(realm->username); + dFree(realm->authorization); + dFree(realm->cnonce); + dFree(realm->nonce); + dFree(realm->opaque); + dFree(realm->domain); + dFree(realm); +} + static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path) { int i; @@ -377,22 +465,33 @@ /* * Return the authorization header for an HTTP query. - */ -const char *a_Auth_get_auth_str(const DilloUrl *url) -{ + * request_uri is a separate argument because we want it precisely as + * formatted in the request. + */ +char *a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri) +{ + char *ret = NULL; AuthHost_t *host; AuthRealm_t *realm; - return - ((host = Auth_host_by_url(url)) && - (realm = Auth_realm_by_path(host, URL_PATH(url)))) ? - realm->authorization : NULL; + if ((host = Auth_host_by_url(url)) && + (realm = Auth_realm_by_path(host, URL_PATH(url)))) { + if (realm->type == BASIC) + ret = dStrdup(realm->authorization); + else if (realm->type == DIGEST) + ret = a_Digest_authorization_hdr(realm, url, request_uri); + else + MSG("a_Auth_get_auth_str() got an unknown realm type: %i.\n", + realm->type); + } + return ret; } /* * Determine whether the user needs to authenticate. */ -static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url) +static int Auth_do_auth_required(const AuthParse_t *auth_parse, + const DilloUrl *url) { /* * TO DO: I dislike the way that this code must decide whether we @@ -420,12 +519,20 @@ * we will re-authenticate. */ if ((host = Auth_host_by_url(url)) && - (realm = Auth_realm_by_name(host, realm_name)) && - (!Auth_realm_includes_path(realm, URL_PATH(url)))) { - _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n", - realm_name, URL_STR(url)); - Auth_realm_add_path(realm, URL_PATH(url)); - return 0; + (realm = Auth_realm_by_name(host, auth_parse->realm))) { + if (!Auth_realm_includes_path(realm, URL_PATH(url))) { + _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n", + auth_parse->realm, URL_STR(url)); + Auth_realm_add_path(realm, URL_PATH(url)); + return 0; + } + + if (auth_parse->type == DIGEST && auth_parse->stale) { + /* we do have valid credentials but our nonce is old */ + dFree((void *)realm->nonce); + realm->nonce = dStrdup(auth_parse->nonce); + return 0; + } } /* @@ -441,7 +548,6 @@ AuthDialogData_t *data; AuthHost_t *host; AuthRealm_t *realm; - char *user_password, *response, *authorization, *authorization_old; data = (AuthDialogData_t *)vData; @@ -456,45 +562,78 @@ } /* find or create the realm */ - if (!(realm = Auth_realm_by_name(host, data->realm_name))) { - /* create a new realm */ - realm = dNew(AuthRealm_t, 1); - realm->name = dStrdup(data->realm_name); + if (!(realm = Auth_realm_by_name(host, data->auth_parse->realm))) { + realm = dNew0(AuthRealm_t, 1); + realm->name = dStrdup(data->auth_parse->realm); realm->paths = dList_new(1); - realm->authorization = NULL; dList_append(host->realms, realm); } + realm->type = data->auth_parse->type; + dFree(realm->authorization); + realm->authorization = NULL; Auth_realm_add_path(realm, URL_PATH(data->url)); - /* create and set the authorization */ - user_password = dStrconcat(user, ":", password, NULL); - response = a_Misc_encode_base64(user_password); - authorization = - dStrconcat("Authorization: Basic ", response, "\r\n", NULL); - authorization_old = realm->authorization; - realm->authorization = authorization; - dFree(authorization_old); - dFree(user_password); - dFree(response); -} - -static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url) + if (realm->type == BASIC) { + char *user_password = dStrconcat(user, ":", password, NULL); + char *response = a_Misc_encode_base64(user_password); + char *authorization = + dStrconcat("Authorization: Basic ", response, "\r\n", NULL); + dFree(realm->authorization); + realm->authorization = authorization; + dFree(response); + dStrshred(user_password); + dFree(user_password); + } else if (realm->type == DIGEST) { + dFree(realm->username); + realm->username = dStrdup(user); + realm->nonce_count = 0; + dFree(realm->nonce); + realm->nonce = dStrdup(data->auth_parse->nonce); + dFree(realm->opaque); + realm->opaque = dStrdup(data->auth_parse->opaque); + realm->algorithm = data->auth_parse->algorithm; + dFree(realm->domain); + realm->domain = dStrdup(data->auth_parse->domain); + realm->qop = data->auth_parse->qop; + dFree(realm->cnonce); + if (realm->qop != QOPNOTSET) + realm->cnonce = a_Digest_create_cnonce(); + if (!a_Digest_compute_digest(realm, user, password)) { + MSG("Auth_do_auth_dialog_cb: a_Digest_compute_digest failed.\n"); + dList_remove_fast(host->realms, realm); + Auth_realm_delete(realm); + } + } else { + MSG("Auth_do_auth_dialog_cb: Unknown auth type: %i\n", + realm->type); + } + dStrshred((char *)password); +} + +/* + * Return: Nonzero if we got new credentials from the user and everything + * seems fine. + */ +static int Auth_do_auth_dialog(const AuthParse_t *auth_parse, + const DilloUrl *url) { int ret; char *message; AuthDialogData_t *data; - - _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", realm); + const char *typestr = auth_parse->type == DIGEST ? "Digest" : "Basic"; + + _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", auth_parse->realm); + message = dStrconcat("The server at ", URL_HOST(url), " requires a username" - " and password for \"", realm, "\".", NULL); + " and password for \"", auth_parse->realm, "\".\n\n" + "Authentication scheme: ", typestr, NULL); data = dNew(AuthDialogData_t, 1); - data->realm_name = dStrdup(realm); + data->auth_parse = auth_parse; data->url = a_Url_dup(url); ret = a_Dialog_user_password(message, Auth_do_auth_dialog_cb, data); dFree(message); - dFree((void*)data->realm_name); - a_Url_free((void*)data->url); + a_Url_free((void *)data->url); dFree(data); return ret; } @@ -502,19 +641,20 @@ /* * Do authorization for an auth string. */ -static int Auth_do_auth(char *auth, const DilloUrl *url) -{ - int reload; +static int Auth_do_auth(char *challenge, enum AuthParseHTTPAuthType_t type, + const DilloUrl *url) +{ AuthParse_t *auth_parse; - - _MSG("auth.c: Auth_do_auth: auth={%s}\n", auth); - reload = 0; + int reload = 0; + + _MSG("auth.c: Auth_do_auth: challenge={%s}\n", challenge); auth_parse = Auth_parse_new(); - Auth_parse_auth(auth_parse, auth); + auth_parse->type = type; + Auth_parse_challenge(auth_parse, challenge); if (auth_parse->ok) reload = - Auth_do_auth_required(auth_parse->realm, url) ? - Auth_do_auth_dialog(auth_parse->realm, url) + Auth_do_auth_required(auth_parse, url) ? + Auth_do_auth_dialog(auth_parse, url) : 1; Auth_parse_free(auth_parse); @@ -522,18 +662,24 @@ } /* - * Do authorization for a set of auth strings. - */ -int a_Auth_do_auth(Dlist *auths, const DilloUrl *url) -{ - int reload, i; - char *auth; - - reload = 0; - for (i = 0; (auth = dList_nth_data(auths, i)); ++i) - if (Auth_do_auth(auth, url)) - reload = 1; - - return reload; -} - + * Given authentication challenge(s), prepare authorization. + * Return: 0 on failure + * nonzero on success. A new query will be sent to the server. + */ +int a_Auth_do_auth(Dlist *challenges, const DilloUrl *url) +{ + int i; + char *chal; + + for (i = 0; (chal = dList_nth_data(challenges, i)); ++i) + if (!dStrnAsciiCasecmp(chal, "Digest ", 7)) + if (Auth_do_auth(chal, DIGEST, url)) + return 1; + for (i = 0; (chal = dList_nth_data(challenges, i)); ++i) + if (!dStrnAsciiCasecmp(chal, "Basic ", 6)) + if (Auth_do_auth(chal, BASIC, url)) + return 1; + + return 0; +} + diff --git a/src/auth.h b/src/auth.h index 5f96f64..f78eb59 100644 --- a/src/auth.h +++ b/src/auth.h @@ -7,8 +7,29 @@ #include "url.h" +enum AuthParseHTTPAuthType_t { TYPENOTSET, BASIC, DIGEST }; +enum AuthParseDigestAlgorithm_t { ALGORITHMNOTSET, MD5, MD5SESS }; +enum AuthParseDigestQOP_t { QOPNOTSET, AUTH, AUTHINT }; -const char *a_Auth_get_auth_str(const DilloUrl *request_url); +typedef struct { + enum AuthParseHTTPAuthType_t type; + char *name; + Dlist *paths; /* stripped of any trailing '/', so the root path is "" */ + char *authorization; /* BASIC: the authorization request header */ + /* DIGEST: the hexdigest of A1 */ + /* digest state ahead */ + char *username; + char *cnonce; + unsigned int nonce_count; + char *nonce; + char *opaque; + enum AuthParseDigestAlgorithm_t algorithm; + char *domain; /* NOT USED */ + enum AuthParseDigestQOP_t qop; +} AuthRealm_t; + + +char *a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri); int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url); void a_Auth_init(void); diff --git a/src/binaryconst.h b/src/binaryconst.h index b5b9735..09d4e7f 100644 --- a/src/binaryconst.h +++ b/src/binaryconst.h @@ -1,5 +1,10 @@ #ifndef __BINARYCONST_H__ #define __BINARYCONST_H__ + +/* binaryconst.h was integrated into the Dillo project in April 2004, and + * presumably comes from the ancestor of the code found at + * http://cprog.tomsweb.net/binconst.txt + */ /* Macros for allowing binary constants in C * By Tom Torfs - donated to the public domain */ diff --git a/src/cache.c b/src/cache.c index 6094a8d..11c7faf 100644 --- a/src/cache.c +++ b/src/cache.c @@ -13,7 +13,6 @@ * Dillo's cache module */ -#include <ctype.h> /* for tolower */ #include <sys/types.h> #include <stdlib.h> @@ -521,7 +520,7 @@ /* META only gives charset; use detected MIME type too */ entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL); } else if (*from == 'm' && - !dStrncasecmp(ctype, "text/xhtml", 10)) { + !dStrnAsciiCasecmp(ctype, "text/xhtml", 10)) { /* WORKAROUND: doxygen uses "text/xhtml" in META */ entry->TypeNorm = dStrdup(entry->TypeDet); } @@ -584,7 +583,7 @@ for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) - if (tolower(fieldname[j]) != tolower(header[i + j])) + if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ @@ -620,7 +619,7 @@ for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) - if (tolower(fieldname[j]) != tolower(header[i + j])) + if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ @@ -675,21 +674,27 @@ entry->Header = dStr_new(""); return; } - if (header[9] == '3' && header[10] == '0') { + if (header[9] == '3' && header[10] == '0' && + (location_str = Cache_parse_field(header, "Location"))) { /* 30x: URL redirection */ - if ((location_str = Cache_parse_field(header, "Location"))) { - DilloUrl *location_url; - + DilloUrl *location_url = a_Url_new(location_str,URL_STR_(entry->Url)); + + if (prefs.filter_auto_requests == PREFS_FILTER_SAME_DOMAIN && + !a_Url_same_organization(entry->Url, location_url)) { + /* don't redirect; just show body like usual (if any) */ + MSG("Redirection not followed from %s to %s\n", + URL_HOST(entry->Url), URL_STR(location_url)); + a_Url_free(location_url); + } else { entry->Flags |= CA_Redirect; if (header[11] == '1') entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */ else if (header[11] == '2') entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */ - location_url = a_Url_new(location_str, URL_STR_(entry->Url)); if (URL_FLAGS(location_url) & (URL_Post + URL_Get) && - dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 && - dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) { + dStrAsciiCasecmp(URL_SCHEME(location_url), "dpi") == 0 && + dStrAsciiCasecmp(URL_SCHEME(entry->Url), "dpi") != 0) { /* Forbid dpi GET and POST from non dpi-generated urls */ MSG("Redirection Denied! '%s' -> '%s'\n", URL_STR(entry->Url), URL_STR(location_url)); @@ -697,8 +702,8 @@ } else { entry->Location = location_url; } - dFree(location_str); - } + } + dFree(location_str); } else if (strncmp(header + 9, "401", 3) == 0) { entry->Auth = Cache_parse_multiple_fields(header, "WWW-Authenticate"); @@ -728,7 +733,7 @@ * If Transfer-Encoding is present, Content-Length must be ignored. * If the Transfer-Encoding is non-identity, it is an error. */ - if (dStrcasecmp(encoding, "identity")) + if (dStrAsciiCasecmp(encoding, "identity")) MSG_HTTP("Content-Length and non-identity Transfer-Encoding " "headers both present.\n"); } else { @@ -1039,9 +1044,9 @@ */ int a_Cache_download_enabled(const DilloUrl *url) { - if (!dStrcasecmp(URL_SCHEME(url), "http") || - !dStrcasecmp(URL_SCHEME(url), "https") || - !dStrcasecmp(URL_SCHEME(url), "ftp")) + if (!dStrAsciiCasecmp(URL_SCHEME(url), "http") || + !dStrAsciiCasecmp(URL_SCHEME(url), "https") || + !dStrAsciiCasecmp(URL_SCHEME(url), "ftp")) return 1; return 0; } diff --git a/src/capi.c b/src/capi.c index bcec07b..6db7484 100644 --- a/src/capi.c +++ b/src/capi.c @@ -229,17 +229,17 @@ const DilloUrl *referer; int allow = FALSE; - if (dStrcasecmp(URL_SCHEME(url), "dpi") == 0) { + if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0) { if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) { allow = TRUE; } else if (!(URL_FLAGS(url) & URL_Post) && - strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) { + strncmp(URL_PATH(url), "/vsource/", 9) == 0) { allow = TRUE; } else { /* only allow GET&POST dpi-requests from dpi-generated urls */ if (a_Nav_stack_size(bw)) { referer = a_History_get_url(NAV_TOP_UIDX(bw)); - if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) { + if (dStrAsciiCasecmp(URL_SCHEME(referer), "dpi") == 0) { allow = TRUE; } } @@ -266,10 +266,10 @@ char *p, *server = NULL, *url_str = URL_STR(url); Dstr *tmp; - if ((dStrncasecmp(url_str, "http:", 5) == 0) || - (dStrncasecmp(url_str, "about:", 6) == 0)) { + if ((dStrnAsciiCasecmp(url_str, "http:", 5) == 0) || + (dStrnAsciiCasecmp(url_str, "about:", 6) == 0)) { /* URL doesn't use dpi (server = NULL) */ - } else if (dStrncasecmp(url_str, "dpi:/", 5) == 0) { + } else if (dStrnAsciiCasecmp(url_str, "dpi:/", 5) == 0) { /* dpi prefix, get this server's name */ if ((p = strchr(url_str + 5, '/')) != NULL) { server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5)); @@ -388,7 +388,8 @@ *want_host = URL_HOST(wanted); if (want_host[0] == '\0') { ret = (req_host[0] == '\0' || - !dStrcasecmp(URL_SCHEME(wanted), "data")) ? TRUE : FALSE; + !dStrAsciiCasecmp(URL_SCHEME(wanted), "data")) + ? TRUE : FALSE; } else { /* This will regard "www.dillo.org" and "www.dillo.org." as * different, but it doesn't seem worth caring about. @@ -454,9 +455,9 @@ } else if (Capi_url_uses_dpi(web->url, &server)) { /* dpi request */ if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) { - if (dStrcasecmp(scheme, "dpi") == 0) { + if (dStrAsciiCasecmp(scheme, "dpi") == 0) { if (strcmp(server, "vsource") == 0) { - /* don't reload the "view source" page */ + /* allow "view source" reload upon user request */ } else { /* make the other "dpi:/" prefixed urls always reload. */ a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery); @@ -478,7 +479,7 @@ } dFree(server); - } else if (!dStrcasecmp(scheme, "http")) { + } else if (!dStrAsciiCasecmp(scheme, "http")) { /* http request */ if (reload) { a_Capi_conn_abort_by_url(web->url); @@ -491,7 +492,7 @@ } use_cache = 1; - } else if (!dStrcasecmp(scheme, "about")) { + } else if (!dStrAsciiCasecmp(scheme, "about")) { /* internal request */ use_cache = 1; } diff --git a/src/colors.c b/src/colors.c index 5b647bb..5d929a8 100644 --- a/src/colors.c +++ b/src/colors.c @@ -262,7 +262,7 @@ high = NCOLORS - 1; while (low <= high) { mid = (low + high) / 2; - if ((ret = dStrcasecmp(cp, color_keyword[mid].key)) < 0) + if ((ret = dStrAsciiCasecmp(cp, color_keyword[mid].key)) < 0) high = mid - 1; else if (ret > 0) low = mid + 1; diff --git a/src/cookies.c b/src/cookies.c index eadd032..0a1e94f 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -284,11 +284,11 @@ rule[j++] = line[i++]; rule[j] = '\0'; - if (dStrcasecmp(rule, "ACCEPT") == 0) + if (dStrAsciiCasecmp(rule, "ACCEPT") == 0) cc.action = COOKIE_ACCEPT; - else if (dStrcasecmp(rule, "ACCEPT_SESSION") == 0) + else if (dStrAsciiCasecmp(rule, "ACCEPT_SESSION") == 0) cc.action = COOKIE_ACCEPT_SESSION; - else if (dStrcasecmp(rule, "DENY") == 0) + else if (dStrAsciiCasecmp(rule, "DENY") == 0) cc.action = COOKIE_DENY; else { MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n", @@ -297,7 +297,7 @@ } cc.domain = dStrdup(domain); - if (dStrcasecmp(cc.domain, "DEFAULT") == 0) { + if (dStrAsciiCasecmp(cc.domain, "DEFAULT") == 0) { /* Set the default action */ default_action = cc.action; dFree(cc.domain); @@ -338,13 +338,13 @@ if (ccontrol[i].domain[0] == '.') { diff = strlen(domain) - strlen(ccontrol[i].domain); if (diff >= 0) { - if (dStrcasecmp(domain + diff, ccontrol[i].domain) != 0) + if (dStrAsciiCasecmp(domain + diff, ccontrol[i].domain) != 0) continue; } else { continue; } } else { - if (dStrcasecmp(domain, ccontrol[i].domain) != 0) + if (dStrAsciiCasecmp(domain, ccontrol[i].domain) != 0) continue; } diff --git a/src/css.cc b/src/css.cc index a1d51da..1e703f2 100644 --- a/src/css.cc +++ b/src/css.cc @@ -265,16 +265,16 @@ if (element != ELEMENT_ANY && element != n->element) return false; if (pseudo != NULL && - (n->pseudo == NULL || dStrcasecmp (pseudo, n->pseudo) != 0)) + (n->pseudo == NULL || dStrAsciiCasecmp (pseudo, n->pseudo) != 0)) return false; - if (id != NULL && (n->id == NULL || dStrcasecmp (id, n->id) != 0)) + if (id != NULL && (n->id == NULL || dStrAsciiCasecmp (id, n->id) != 0)) return false; if (klass != NULL) { for (int i = 0; i < klass->size (); i++) { bool found = false; if (n->klass != NULL) { for (int j = 0; j < n->klass->size (); j++) { - if (dStrcasecmp (klass->get(i), n->klass->get(j)) == 0) { + if (dStrAsciiCasecmp (klass->get(i), n->klass->get(j)) == 0) { found = true; break; } @@ -610,7 +610,7 @@ "th {font-weight: bolder; text-align: center}" "code, tt, pre, samp, kbd {font-family: monospace}" /* WORKAROUND: Reset font properties in tables as some - * some pages rely on it (e.g. gmail). + * pages rely on it (e.g. gmail). * http://developer.mozilla.org/En/Fixing_Table_Inheritance_in_Quirks_Mode * has a detailed description of the issue. */ diff --git a/src/cssparser.cc b/src/cssparser.cc index 73b4331..6d5b376 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -119,6 +119,10 @@ static const char *const Css_text_decoration_enum_vals[] = { "underline", "overline", "line-through", "blink", NULL +}; + +static const char *const Css_text_transform_enum_vals[] = { + "none", "capitalize", "uppercase", "lowercase", NULL }; static const char *const Css_vertical_align_vals[] = { @@ -228,7 +232,8 @@ Css_text_decoration_enum_vals}, {"text-indent", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, {"text-shadow", {CSS_TYPE_UNUSED}, NULL}, - {"text-transform", {CSS_TYPE_UNUSED}, NULL}, + {"text-transform", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, + Css_text_transform_enum_vals}, {"top", {CSS_TYPE_UNUSED}, NULL}, {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL}, {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals}, @@ -669,7 +674,7 @@ case CSS_TYPE_ENUM: if (ttype == CSS_TK_SYMBOL) { for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) return true; } @@ -677,11 +682,11 @@ case CSS_TYPE_MULTI_ENUM: if (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "none") == 0) { + if (dStrAsciiCasecmp(tval, "none") == 0) { return true; } else { for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) { - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) return true; } @@ -698,14 +703,14 @@ case CSS_TYPE_SIGNED_LENGTH: if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT || - (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0)) + (ttype == CSS_TK_SYMBOL && dStrAsciiCasecmp(tval, "auto") == 0)) return true; break; case CSS_TYPE_COLOR: if ((ttype == CSS_TK_COLOR || ttype == CSS_TK_SYMBOL) && - (dStrcasecmp(tval, "rgb") == 0 || + (dStrAsciiCasecmp(tval, "rgb") == 0 || a_Color_parse(tval, -1, &err) != -1)) return true; break; @@ -833,7 +838,7 @@ case CSS_TYPE_ENUM: if (ttype == CSS_TK_SYMBOL) { for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) { val->intVal = i; ret = true; @@ -848,10 +853,10 @@ ret = true; while (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "none") != 0) { + if (dStrAsciiCasecmp(tval, "none") != 0) { for (i = 0, found = false; !found && Css_property_info[prop].enum_symbols[i]; i++) { - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) val->intVal |= (1 << i); } @@ -872,32 +877,32 @@ if (!spaceSeparated && ttype == CSS_TK_SYMBOL) { ret = true; - if (dStrcasecmp(tval, "px") == 0) { + if (dStrAsciiCasecmp(tval, "px") == 0) { lentype = CSS_LENGTH_TYPE_PX; nextToken(); - } else if (dStrcasecmp(tval, "mm") == 0) { + } else if (dStrAsciiCasecmp(tval, "mm") == 0) { lentype = CSS_LENGTH_TYPE_MM; nextToken(); - } else if (dStrcasecmp(tval, "cm") == 0) { + } else if (dStrAsciiCasecmp(tval, "cm") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= 10; nextToken(); - } else if (dStrcasecmp(tval, "in") == 0) { + } else if (dStrAsciiCasecmp(tval, "in") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= 25.4; nextToken(); - } else if (dStrcasecmp(tval, "pt") == 0) { + } else if (dStrAsciiCasecmp(tval, "pt") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= (25.4 / 72); nextToken(); - } else if (dStrcasecmp(tval, "pc") == 0) { + } else if (dStrAsciiCasecmp(tval, "pc") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= (25.4 / 6); nextToken(); - } else if (dStrcasecmp(tval, "em") == 0) { + } else if (dStrAsciiCasecmp(tval, "em") == 0) { lentype = CSS_LENGTH_TYPE_EM; nextToken(); - } else if (dStrcasecmp(tval, "ex") == 0) { + } else if (dStrAsciiCasecmp(tval, "ex") == 0) { lentype = CSS_LENGTH_TYPE_EX; nextToken(); } else { @@ -922,7 +927,7 @@ ret = true; val->intVal = CSS_CREATE_LENGTH(fval, lentype); - } else if (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0) { + } else if (ttype == CSS_TK_SYMBOL && !dStrAsciiCasecmp(tval, "auto")) { ret = true; val->intVal = CSS_LENGTH_TYPE_AUTO; nextToken(); @@ -938,7 +943,7 @@ ret = true; nextToken(); } else if (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "rgb") == 0) { + if (dStrAsciiCasecmp(tval, "rgb") == 0) { nextToken(); if (parseRgbColor(&val->intVal)) ret = true; @@ -1017,7 +1022,7 @@ if (ttype == CSS_TK_CHAR && tval[0] == '!') { nextToken(); if (ttype == CSS_TK_SYMBOL && - dStrcasecmp(tval, "important") == 0) { + dStrAsciiCasecmp(tval, "important") == 0) { nextToken(); return true; } @@ -1031,7 +1036,7 @@ */ static int Css_property_info_cmp(const void *a, const void *b) { - return dStrcasecmp(((CssPropertyInfo *) a)->symbol, + return dStrAsciiCasecmp(((CssPropertyInfo *) a)->symbol, ((CssPropertyInfo *) b)->symbol); } @@ -1041,7 +1046,7 @@ */ static int Css_shorthand_info_cmp(const void *a, const void *b) { - return dStrcasecmp(((CssShorthandInfo *) a)->symbol, + return dStrAsciiCasecmp(((CssShorthandInfo *) a)->symbol, ((CssShorthandInfo *) b)->symbol); } @@ -1386,7 +1391,7 @@ Dstr *urlStr = NULL; if (ttype != CSS_TK_SYMBOL || - dStrcasecmp(tval, "url") != 0) + dStrAsciiCasecmp(tval, "url") != 0) return NULL; nextToken(); @@ -1432,7 +1437,7 @@ nextToken(); if (ttype == CSS_TK_SYMBOL && - dStrcasecmp(tval, "url") == 0) + dStrAsciiCasecmp(tval, "url") == 0) urlStr = parseUrl(); else if (ttype == CSS_TK_STRING) urlStr = dStrdup (tval); @@ -1444,8 +1449,8 @@ mediaSyntaxIsOK = false; mediaIsSelected = false; while (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "all") == 0 || - dStrcasecmp(tval, "screen") == 0) + if (dStrAsciiCasecmp(tval, "all") == 0 || + dStrAsciiCasecmp(tval, "screen") == 0) mediaIsSelected = true; nextToken(); if (ttype == CSS_TK_CHAR && tval[0] == ',') { @@ -1486,8 +1491,8 @@ /* parse a comma-separated list of media */ while (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "all") == 0 || - dStrcasecmp(tval, "screen") == 0) + if (dStrAsciiCasecmp(tval, "all") == 0 || + dStrAsciiCasecmp(tval, "screen") == 0) mediaIsSelected = true; nextToken(); if (ttype == CSS_TK_CHAR && tval[0] == ',') { @@ -1573,11 +1578,11 @@ parser.tval[0] == '@') { parser.nextToken(); if (parser.ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(parser.tval, "import") == 0 && + if (dStrAsciiCasecmp(parser.tval, "import") == 0 && html != NULL && importsAreAllowed) { parser.parseImport(html, url); - } else if (dStrcasecmp(parser.tval, "media") == 0) { + } else if (dStrAsciiCasecmp(parser.tval, "media") == 0) { parser.parseMedia(); } else { parser.ignoreStatement(); diff --git a/src/decode.c b/src/decode.c index 2406731..7ea3dc2 100644 --- a/src/decode.c +++ b/src/decode.c @@ -190,7 +190,7 @@ { Decode *dc = NULL; - if (format && !dStrcasecmp(format, "chunked")) { + if (format && !dStrAsciiCasecmp(format, "chunked")) { int *chunk_remaining = dNew(int, 1); *chunk_remaining = 0; dc = dNew(Decode, 1); @@ -215,7 +215,8 @@ Decode *dc = NULL; if (format && *format) { - if (!dStrcasecmp(format, "gzip") || !dStrcasecmp(format, "x-gzip")) { + if (!dStrAsciiCasecmp(format, "gzip") || + !dStrAsciiCasecmp(format, "x-gzip")) { z_stream *zs; _MSG("gzipped data!\n"); @@ -245,17 +246,17 @@ */ static int Decode_is_ascii(const char *str) { - return (!(dStrcasecmp(str, "ASCII") && - dStrcasecmp(str, "US-ASCII") && - dStrcasecmp(str, "us") && - dStrcasecmp(str, "IBM367") && - dStrcasecmp(str, "cp367") && - dStrcasecmp(str, "csASCII") && - dStrcasecmp(str, "ANSI_X3.4-1968") && - dStrcasecmp(str, "iso-ir-6") && - dStrcasecmp(str, "ANSI_X3.4-1986") && - dStrcasecmp(str, "ISO_646.irv:1991") && - dStrcasecmp(str, "ISO646-US"))); + return (!(dStrAsciiCasecmp(str, "ASCII") && + dStrAsciiCasecmp(str, "US-ASCII") && + dStrAsciiCasecmp(str, "us") && + dStrAsciiCasecmp(str, "IBM367") && + dStrAsciiCasecmp(str, "cp367") && + dStrAsciiCasecmp(str, "csASCII") && + dStrAsciiCasecmp(str, "ANSI_X3.4-1968") && + dStrAsciiCasecmp(str, "iso-ir-6") && + dStrAsciiCasecmp(str, "ANSI_X3.4-1986") && + dStrAsciiCasecmp(str, "ISO_646.irv:1991") && + dStrAsciiCasecmp(str, "ISO646-US"))); } /* @@ -271,7 +272,7 @@ if (format && strlen(format) && - dStrcasecmp(format,"UTF-8") && + dStrAsciiCasecmp(format,"UTF-8") && !Decode_is_ascii(format)) { iconv_t ic = iconv_open("UTF-8", format); diff --git a/src/dialog.cc b/src/dialog.cc index 15e5d7a..f830f2d 100644 --- a/src/dialog.cc +++ b/src/dialog.cc @@ -94,6 +94,24 @@ return Fl_Choice::handle(e); }; }; + +class EnterButton : public Fl_Button { +public: + EnterButton (int x,int y,int w,int h, const char* label = 0) : + Fl_Button (x,y,w,h,label) {}; + int handle(int e); +}; + +int EnterButton::handle(int e) +{ + if (e == FL_KEYBOARD && Fl::focus() == this && Fl::event_key() == FL_Enter){ + set_changed(); + simulate_key_action(); + do_callback(); + return 1; + } + return Fl_Button::handle(e); +} //---------------------------------------------------------------------------- @@ -354,7 +372,7 @@ bw = (ww - gap)/nb - gap; xpos += gap; for (int i=1; i <= nb; ++i) { - b = new Fl_Button(xpos, wh-bh, bw, bh, txt[i]); + b = new EnterButton(xpos, wh-bh, bw, bh, txt[i]); b->align(FL_ALIGN_WRAP|FL_ALIGN_CLIP); b->box(FL_UP_BOX); b->callback(choice5_cb, INT2VOIDP(i)); @@ -426,14 +444,14 @@ /* "OK" button */ y += input_h + 20; - Fl_Button *ok_button = new Fl_Button(200, y, 50, button_h, "OK"); + Fl_Button *ok_button = new EnterButton(200, y, 50, button_h, "OK"); ok_button->labelsize(14); ok_button->callback(Dialog_user_password_cb); window->add(ok_button); /* "Cancel" button */ Fl_Button *cancel_button = - new Fl_Button(50, y, 100, button_h, "Cancel"); + new EnterButton(50, y, 100, button_h, "Cancel"); cancel_button->labelsize(14); cancel_button->callback(Dialog_user_password_cb); window->add(cancel_button); diff --git a/src/digest.c b/src/digest.c new file mode 100644 index 0000000..d546195 --- /dev/null +++ b/src/digest.c @@ -0,0 +1,204 @@ +/* + * File: digest.c + * + * Copyright 2009 Justus Winter <4winter@informatik.uni-hamburg.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include <stdlib.h> +#include "digest.h" +#include "md5.h" +#include "msg.h" +#include "../dlib/dlib.h" + +static const char *ALGORITHM2STR[] = { NULL, "MD5", "MD5-sess" }; +static const char *QOP2STR[] = { NULL, "auth", "auth-int" }; +static const char hexchars[] = "0123456789abcdef"; + +static Dstr *md5hexdigest(const Dstr *data) +{ + md5_state_t state; + md5_byte_t digest[16]; + Dstr *result = dStr_sized_new(33); + int i; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)data->str, data->len); + md5_finish(&state, digest); + + for (i = 0; i < 16; i++) + dStr_sprintfa(result, "%02x", digest[i]); + return result; +} + +/* + * Returns a pointer to a newly allocated string containing a cnonce + */ +char *a_Digest_create_cnonce(void) +{ + int i; + char *result = dNew(char, 33); + for (i = 0; i < 32; i++) + result[i] = hexchars[rand() % 16]; + result[32] = 0; + return result; +} + +/* + * This portion only has to be calculated once. + */ +int a_Digest_compute_digest(AuthRealm_t *realm, const char *username, + const char *passwd) +{ + Dstr *a1; + Dstr *digest; + + if (realm->algorithm == MD5 || realm->algorithm == ALGORITHMNOTSET) { + /* A1 = unq(username-value) ":" unq(realm-value) ":" passwd */ + a1 = dStr_new(NULL); + dStr_sprintf(a1, "%s:%s:%s", username, realm->name, passwd); + } else if (realm->algorithm == MD5SESS) { + /* A1 = H( unq(username-value) ":" unq(realm-value) + ** ":" passwd ) + ** ":" unq(nonce-value) ":" unq(cnonce-value) + */ + Dstr *a0 = dStr_new(NULL); + dStr_sprintf(a0, "%s:%s:%s", username, realm->name, passwd); + Dstr *ha0 = md5hexdigest(a0); + a1 = dStr_new(NULL); + dStr_sprintf(a1, "%s:%s:%s", ha0, realm->nonce, realm->cnonce); + dStr_free(a0, 1); + dStr_free(ha0, 1); + } else { + MSG("a_Digest_create_auth: Unknown algorithm.\n"); + return 0; + } + + digest = md5hexdigest(a1); + realm->authorization = digest->str; + dStr_shred(a1); + dStr_free(a1, 1); + dStr_free(digest, 0); + return 1; +} + +/* + * This portion is calculatd for each request. + */ +static Dstr *Digest_create_response(AuthRealm_t *realm, const char *method, + const char *digest_uri, + const Dstr *entity_body) +{ + Dstr *ha2; + Dstr *result; + + if (realm->qop == QOPNOTSET || realm->qop == AUTH) { + /* A2 = Method ":" digest-uri-value */ + Dstr *a2 = dStr_new(NULL); + dStr_sprintf(a2, "%s:%s", method, digest_uri); + ha2 = md5hexdigest(a2); + dStr_free(a2, 1); + } else if (realm->qop == AUTHINT) { + /* A2 = Method ":" digest-uri-value ":" H(entity-body) */ + Dstr *hentity = md5hexdigest(entity_body); + Dstr *a2 = dStr_new(NULL); + dStr_sprintf(a2, "%s:%s:%s", method, digest_uri, hentity->str); + ha2 = md5hexdigest(a2); + dStr_free(hentity, 1); + dStr_free(a2, 1); + } else { + MSG("a_Digest_create_auth: Unknown qop value.\n"); + return NULL; + } + result = dStr_new(NULL); + + if (realm->qop == AUTH || realm->qop == AUTHINT) { + dStr_sprintf(result, + "%s:%s:%08x:%s:%s:%s", + realm->authorization, + realm->nonce, + realm->nonce_count, + realm->cnonce, + QOP2STR[realm->qop], + ha2->str); + } else { + dStr_sprintf(result, + "%s:%s:%s", + realm->authorization, + realm->nonce, + ha2->str); + } + + Dstr *request_digest = md5hexdigest(result); + dStr_free(result, 1); + dStr_free(ha2, 1); + return request_digest; +} + +static void Digest_Dstr_append_token_value(Dstr *str, int delimiter, + const char *token, + const char *value, int quoted) +{ + char c; + dStr_sprintfa(str, "%s%s=", (delimiter ? ", " : ""), token); + if (quoted) { + dStr_append_c(str, '"'); + while ((c = *value++)) { + if (c == '"') + dStr_append_c(str, '\\'); + dStr_append_c(str, c); + } + dStr_append_c(str, '"'); + } else { + dStr_append(str, value); + } +} + +/* + * Construct Digest Authorization header. + * + * Field ordering: furaisanjin reports that his DVD recorder requires the + * order that IE happens to use: "username, realm, nonce, uri, cnonce, nc, + * algorithm, response, qop". It apparently doesn't use "opaque", so that's + * been left where it already was. + */ +char *a_Digest_authorization_hdr(AuthRealm_t *realm, const DilloUrl *url, + const char *digest_uri) +{ + char *ret; + Dstr *response, *result; + const char *method = URL_FLAGS(url) & URL_Post ? "POST" : "GET"; + + realm->nonce_count++; + response = Digest_create_response(realm, method, digest_uri, URL_DATA(url)); + if (!response) + return NULL; + result = dStr_new("Authorization: Digest "); + Digest_Dstr_append_token_value(result, 0, "username", realm->username, 1); + Digest_Dstr_append_token_value(result, 1, "realm", realm->name, 1); + Digest_Dstr_append_token_value(result, 1, "nonce", realm->nonce, 1); + Digest_Dstr_append_token_value(result, 1, "uri", digest_uri, 1); + if (realm->qop != QOPNOTSET) { + Digest_Dstr_append_token_value(result, 1, "cnonce", realm->cnonce, 1); + dStr_sprintfa(result, ", nc=%08x", realm->nonce_count); + } + if (realm->algorithm != ALGORITHMNOTSET) { + Digest_Dstr_append_token_value(result, 1, "algorithm", + ALGORITHM2STR[realm->algorithm], 0); + } + Digest_Dstr_append_token_value(result, 1, "response", response->str, 1); + if (realm->opaque) + Digest_Dstr_append_token_value(result, 1, "opaque", realm->opaque, 1); + if (realm->qop != QOPNOTSET) + Digest_Dstr_append_token_value(result, 1, "qop", QOP2STR[realm->qop], 1); + dStr_sprintfa(result, "\r\n"); + + dStr_free(response, 1); + ret = result->str; + dStr_free(result, 0); + return ret; +} diff --git a/src/digest.h b/src/digest.h new file mode 100644 index 0000000..2723c64 --- /dev/null +++ b/src/digest.h @@ -0,0 +1,16 @@ +#ifndef __DIGEST_H__ +#define __DIGEST_H__ + +#include "auth.h" +#include "../dlib/dlib.h" + + +char *a_Digest_create_cnonce(void); +int a_Digest_compute_digest(AuthRealm_t *realm, + const char *username, + const char *passwd); +char *a_Digest_authorization_hdr(AuthRealm_t *realm, + const DilloUrl *url, + const char *uri); + +#endif /* !__DIGEST_H__ */ diff --git a/src/dillo.cc b/src/dillo.cc index b05cb16..ff450fe 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -410,7 +410,8 @@ if (idx == argc) { /* No URLs/files on cmdline. Send startup screen */ - if (strcmp(URL_STR(prefs.start_page), "about:blank") == 0) + if (dStrAsciiCasecmp(URL_SCHEME(prefs.start_page), "about") == 0 && + strcmp(URL_PATH(prefs.start_page), "blank") == 0) a_UIcmd_open_url(bw, NULL); else a_UIcmd_open_url(bw, prefs.start_page); diff --git a/src/dns.c b/src/dns.c index 980586c..8471a52 100644 --- a/src/dns.c +++ b/src/dns.c @@ -113,7 +113,7 @@ int i; for (i = 0; i < dns_queue_size; i++) - if (!strcmp(hostname, dns_queue[i].hostname)) + if (!dStrAsciiCasecmp(hostname, dns_queue[i].hostname)) return i; return -1; @@ -377,7 +377,7 @@ /* check for cache hit. */ for (i = 0; i < dns_cache_size; i++) - if (!strcmp(hostname, dns_cache[i].hostname)) + if (!dStrAsciiCasecmp(hostname, dns_cache[i].hostname)) break; if (i < dns_cache_size) { @@ -445,8 +445,10 @@ * with the same hostname*/ for (j = i; j < dns_queue_size; j++) if (dns_queue[j].channel == -2 && - !strcmp(dns_queue[j].hostname, dns_queue[i].hostname)) + !dStrAsciiCasecmp(dns_queue[j].hostname, + dns_queue[i].hostname)) { dns_queue[j].channel = ch; + } Dns_server_req(ch, dns_queue[i].hostname); } else return; diff --git a/src/form.cc b/src/form.cc index f70bea8..8781d81 100644 --- a/src/form.cc +++ b/src/form.cc @@ -271,7 +271,7 @@ for (int idx = 0; idx < inputs->size(); idx++) { DilloHtmlInput *input = inputs->get(idx); if (input->type == DILLO_HTML_INPUT_RADIO && - input->name && !dStrcasecmp(input->name, name)) + input->name && !dStrAsciiCasecmp(input->name, name)) return input; } } @@ -318,9 +318,9 @@ method = DILLO_HTML_METHOD_GET; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "method"))) { - if (!dStrcasecmp(attrbuf, "post")) { + if (!dStrAsciiCasecmp(attrbuf, "post")) { method = DILLO_HTML_METHOD_POST; - } else if (dStrcasecmp(attrbuf, "get")) { + } else if (dStrAsciiCasecmp(attrbuf, "get")) { BUG_MSG("Unknown form submission method \"%s\"\n", attrbuf); } } @@ -333,7 +333,7 @@ content_type = DILLO_HTML_ENC_URLENCODED; if ((method == DILLO_HTML_METHOD_POST) && ((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) { - if (!dStrcasecmp(attrbuf, "multipart/form-data")) + if (!dStrAsciiCasecmp(attrbuf, "multipart/form-data")) content_type = DILLO_HTML_ENC_MULTIPART; } charset = NULL; @@ -343,9 +343,9 @@ char *ptr = first = dStrdup(attrbuf); while (ptr && !charset) { char *curr = dStrsep(&ptr, " ,"); - if (!dStrcasecmp(curr, "utf-8")) { + if (!dStrAsciiCasecmp(curr, "utf-8")) { charset = curr; - } else if (!dStrcasecmp(curr, "UNKNOWN")) { + } else if (!dStrAsciiCasecmp(curr, "UNKNOWN")) { /* defined to be whatever encoding the document is in */ charset = html->charset; } @@ -441,18 +441,18 @@ init_str = NULL; inp_type = DILLO_HTML_INPUT_UNKNOWN; - if (!dStrcasecmp(type, "password")) { + if (!dStrAsciiCasecmp(type, "password")) { inp_type = DILLO_HTML_INPUT_PASSWORD; attrbuf = a_Html_get_attr(html, tag, tagsize, "size"); int size = Html_input_get_size(html, attrbuf); resource = factory->createEntryResource (size, true, NULL); init_str = value; - } else if (!dStrcasecmp(type, "checkbox")) { + } else if (!dStrAsciiCasecmp(type, "checkbox")) { inp_type = DILLO_HTML_INPUT_CHECKBOX; resource = factory->createCheckButtonResource(false); init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL); init_str = (value) ? value : dStrdup("on"); - } else if (!dStrcasecmp(type, "radio")) { + } else if (!dStrAsciiCasecmp(type, "radio")) { inp_type = DILLO_HTML_INPUT_RADIO; RadioButtonResource *rb_r = NULL; DilloHtmlInput *input = Html_get_radio_input(html, name); @@ -461,22 +461,22 @@ resource = factory->createRadioButtonResource(rb_r, false); init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL); init_str = value; - } else if (!dStrcasecmp(type, "hidden")) { + } else if (!dStrAsciiCasecmp(type, "hidden")) { inp_type = DILLO_HTML_INPUT_HIDDEN; init_str = value; int size = Html_input_get_size(html, NULL); resource = factory->createEntryResource(size, false, name); - } else if (!dStrcasecmp(type, "submit")) { + } else if (!dStrAsciiCasecmp(type, "submit")) { inp_type = DILLO_HTML_INPUT_SUBMIT; init_str = (value) ? value : dStrdup("submit"); resource = factory->createLabelButtonResource(init_str); // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */ - } else if (!dStrcasecmp(type, "reset")) { + } else if (!dStrAsciiCasecmp(type, "reset")) { inp_type = DILLO_HTML_INPUT_RESET; init_str = (value) ? value : dStrdup("Reset"); resource = factory->createLabelButtonResource(init_str); // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */ - } else if (!dStrcasecmp(type, "image")) { + } else if (!dStrAsciiCasecmp(type, "image")) { if (URL_FLAGS(html->base_url) & URL_SpamSafe) { /* Don't request the image; make a text submit button instead */ inp_type = DILLO_HTML_INPUT_SUBMIT; @@ -491,7 +491,7 @@ embed = Html_input_image(html, tag, tagsize); init_str = value; } - } else if (!dStrcasecmp(type, "file")) { + } else if (!dStrAsciiCasecmp(type, "file")) { bool valid = true; if (html->InFlags & IN_FORM) { DilloHtmlForm *form = html->getCurrentForm(); @@ -512,24 +512,22 @@ init_str = dStrdup("File selector"); resource = factory->createLabelButtonResource(init_str); } - } else if (!dStrcasecmp(type, "button")) { + } else if (!dStrAsciiCasecmp(type, "button")) { inp_type = DILLO_HTML_INPUT_BUTTON; if (value) { init_str = value; resource = factory->createLabelButtonResource(init_str); } - } else if (!dStrcasecmp(type, "text") || !*type) { + } else { /* Text input, which also is the default */ inp_type = DILLO_HTML_INPUT_TEXT; + if (*type && dStrAsciiCasecmp(type, "text")) + BUG_MSG("Unknown input type: \"%s\"\n", type); attrbuf = a_Html_get_attr(html, tag, tagsize, "size"); int size = Html_input_get_size(html, attrbuf); resource = factory->createEntryResource(size, false, NULL); init_str = value; - } else { - /* Unknown input type */ - BUG_MSG("Unknown input type: \"%s\"\n", type); - } - + } if (resource) embed = new Embed (resource); @@ -833,11 +831,11 @@ type = a_Html_get_attr_wdef(html, tag, tagsize, "type", ""); - if (!dStrcasecmp(type, "button")) { + if (!dStrAsciiCasecmp(type, "button")) { inp_type = DILLO_HTML_INPUT_BUTTON; - } else if (!dStrcasecmp(type, "reset")) { + } else if (!dStrAsciiCasecmp(type, "reset")) { inp_type = DILLO_HTML_INPUT_BUTTON_RESET; - } else if (!dStrcasecmp(type, "submit") || !*type) { + } else if (!dStrAsciiCasecmp(type, "submit") || !*type) { /* submit button is the default */ inp_type = DILLO_HTML_INPUT_BUTTON_SUBMIT; } else { @@ -1036,7 +1034,7 @@ char *boundary = NULL; iconv_t char_encoder = (iconv_t) -1; - if (submit_charset && dStrcasecmp(submit_charset, "UTF-8")) { + if (submit_charset && dStrAsciiCasecmp(submit_charset, "UTF-8")) { char_encoder = iconv_open(submit_charset, "UTF-8"); if (char_encoder == (iconv_t) -1) { MSG_WARN("Cannot convert to character encoding '%s'\n", @@ -1310,8 +1308,8 @@ (void)a_Misc_get_content_type_from_data(file->str, file->len, &ctype); /* Heuristic: text/plain with ".htm[l]" extension -> text/html */ if ((ext = strrchr(filename, '.')) && - !dStrcasecmp(ctype, "text/plain") && - (!dStrcasecmp(ext, ".html") || !dStrcasecmp(ext, ".htm"))) { + !dStrAsciiCasecmp(ctype, "text/plain") && + (!dStrAsciiCasecmp(ext, ".html") || !dStrAsciiCasecmp(ext, ".htm"))){ ctype = "text/html"; } @@ -1476,7 +1474,7 @@ for (int idx = 0; idx < inputs->size(); idx++) { DilloHtmlInput *input = inputs->get(idx); if (input->type == DILLO_HTML_INPUT_RADIO && - input->name && !dStrcasecmp(input->name, name)) + input->name && !dStrAsciiCasecmp(input->name, name)) return input; } return NULL; diff --git a/src/html.cc b/src/html.cc index a50a183..f740a77 100644 --- a/src/html.cc +++ b/src/html.cc @@ -16,7 +16,7 @@ /*----------------------------------------------------------------------------- * Includes *---------------------------------------------------------------------------*/ -#include <ctype.h> /* for isspace and tolower */ +#include <ctype.h> /* for isspace */ #include <string.h> /* for memcpy and memmove */ #include <stdlib.h> #include <stdio.h> /* for sprintf */ @@ -311,16 +311,16 @@ if ((align = a_Html_get_attr(html, tag, tagsize, "align"))) { TextAlignType textAlignType = TEXT_ALIGN_LEFT; - if (dStrcasecmp (align, "left") == 0) + if (dStrAsciiCasecmp (align, "left") == 0) textAlignType = TEXT_ALIGN_LEFT; - else if (dStrcasecmp (align, "right") == 0) + else if (dStrAsciiCasecmp (align, "right") == 0) textAlignType = TEXT_ALIGN_RIGHT; - else if (dStrcasecmp (align, "center") == 0) + else if (dStrAsciiCasecmp (align, "center") == 0) textAlignType = TEXT_ALIGN_CENTER; - else if (dStrcasecmp (align, "justify") == 0) + else if (dStrAsciiCasecmp (align, "justify") == 0) textAlignType = TEXT_ALIGN_JUSTIFY; #if 0 - else if (dStrcasecmp (align, "char") == 0) { + else if (dStrAsciiCasecmp (align, "char") == 0) { /* TODO: Actually not supported for <p> etc. */ v.textAlign = TEXT_ALIGN_STRING; if ((charattr = a_Html_get_attr(html, tag, tagsize, "char"))) { @@ -352,11 +352,11 @@ VAlignType valign; if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) { - if (dStrcasecmp (attr, "top") == 0) + if (dStrAsciiCasecmp (attr, "top") == 0) valign = VALIGN_TOP; - else if (dStrcasecmp (attr, "bottom") == 0) + else if (dStrAsciiCasecmp (attr, "bottom") == 0) valign = VALIGN_BOTTOM; - else if (dStrcasecmp (attr, "baseline") == 0) + else if (dStrAsciiCasecmp (attr, "baseline") == 0) valign = VALIGN_BASELINE; else valign = VALIGN_MIDDLE; @@ -1107,6 +1107,8 @@ } if (spaceCnt) { + // add break possibility for the white-space:pre-wrap case + HT2TB(html)->addBreakOption (html->styleEngine->wordStyle ()); spc = dStrnfill(spaceCnt, ' '); HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ()); dFree(spc); @@ -1208,10 +1210,12 @@ Html_process_space(html, word2 + start, i - start); } else if (!strncmp(word2+i, utf8_zero_width_space, 3)) { i += 3; + HT2TB(html)->addBreakOption(html->styleEngine->wordStyle ()); } else if (a_Utf8_ideographic(word2+i, beyond_word2, &len)) { i += len; HT2TB(html)->addText(word2 + start, i - start, html->styleEngine->wordStyle ()); + HT2TB(html)->addBreakOption(html->styleEngine->wordStyle ()); } else { do { i += len; @@ -1236,7 +1240,7 @@ int i; for (i = 0; i < tagsize && tagstr[i] != '\0'; i++) { - if (tolower(tagstr[i]) != tolower(tag[i])) + if (D_ASCII_TOLOWER(tagstr[i]) != D_ASCII_TOLOWER(tag[i])) return false; } /* The test for '/' is for xml compatibility: "empty/>" will be matched. */ @@ -1458,10 +1462,10 @@ int i; for (i = 0; val[i]; ++i) - if (!(isalnum(val[i]) || strchr(":_.-", val[i]))) + if (!isascii(val[i]) || !(isalnum(val[i]) || strchr(":_.-", val[i]))) break; - if (val[i] || !isalpha(val[0])) + if (val[i] || !(isascii(val[0]) && isalpha(val[0]))) BUG_MSG("'%s' value is not of the form " "[A-Za-z][A-Za-z0-9:_.-]*\n", attrname); @@ -1526,18 +1530,18 @@ _MSG("New: {%s}\n", ntag); /* The default DT_NONE type is TagSoup */ - if (!dStrncasecmp(ntag, HTML_SGML_sig, strlen(HTML_SGML_sig))) { + if (!dStrnAsciiCasecmp(ntag, HTML_SGML_sig, strlen(HTML_SGML_sig))) { p = ntag + strlen(HTML_SGML_sig) + 1; if (!strncmp(p, HTML401, strlen(HTML401)) && - dStristr(p + strlen(HTML401), HTML401_url)) { + dStriAsciiStr(p + strlen(HTML401), HTML401_url)) { html->DocType = DT_HTML; html->DocTypeVersion = 4.01f; } else if (!strncmp(p, XHTML1, strlen(XHTML1)) && - dStristr(p + strlen(XHTML1), XHTML1_url)) { + dStriAsciiStr(p + strlen(XHTML1), XHTML1_url)) { html->DocType = DT_XHTML; html->DocTypeVersion = 1.0f; } else if (!strncmp(p, XHTML11, strlen(XHTML11)) && - dStristr(p + strlen(XHTML11), XHTML11_url)) { + dStriAsciiStr(p + strlen(XHTML11), XHTML11_url)) { html->DocType = DT_XHTML; html->DocTypeVersion = 1.1f; } else if (!strncmp(p, HTML40, strlen(HTML40))) { @@ -1550,7 +1554,7 @@ html->DocType = DT_HTML; html->DocTypeVersion = 2.0f; } - } else if (!dStrcasecmp(ntag, HTML5_sig)) { + } else if (!dStrAsciiCasecmp(ntag, HTML5_sig)) { BUG_MSG("Document follows HTML5 working draft; treating as HTML4.\n"); html->DocType = DT_HTML; html->DocTypeVersion = 5.0f; @@ -1683,11 +1687,11 @@ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { BUG_MSG("type attribute is required for <style>\n"); - } else if (dStrcasecmp(attrbuf, "text/css")) { + } else if (dStrAsciiCasecmp(attrbuf, "text/css")) { html->loadCssFromStash = false; } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) && - dStrcasecmp(attrbuf, "all") && !dStristr(attrbuf, "screen")) { + dStrAsciiCasecmp(attrbuf, "all") && !dStriAsciiStr(attrbuf, "screen")) { /* HTML 4.01 sec. 6.13 says that media descriptors are case-sensitive, * but sec. 14.2.3 says that the attribute is case-insensitive. * TODO can be a comma-separated list. @@ -1856,7 +1860,7 @@ textblock->addWidget(bullet, html->styleEngine->wordStyle ()); textblock->addSpace(html->styleEngine->wordStyle ()); - if (tolower(tag[1]) == 'i') { + if (D_ASCII_TOLOWER(tag[1]) == 'i') { /* IFRAME usually comes with very long advertising/spying URLS, * to not break rendering we will force name="IFRAME" */ textblock->addText ("IFRAME", html->styleEngine->wordStyle ()); @@ -2116,7 +2120,7 @@ Image->bg_color = HT2TB(html)->getBgColor()->getColor(); load_now = prefs.load_images || - !dStrcasecmp(URL_SCHEME(url), "data") || + !dStrAsciiCasecmp(URL_SCHEME(url), "data") || (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached); bool loading = false; if (load_now) @@ -2288,15 +2292,15 @@ } attrbuf = a_Html_get_attr(html, tag, tagsize, "shape"); - if (!attrbuf || !*attrbuf || !dStrcasecmp(attrbuf, "rect")) { + if (!attrbuf || !*attrbuf || !dStrAsciiCasecmp(attrbuf, "rect")) { /* the default shape is a rectangle */ type = RECTANGLE; - } else if (dStrcasecmp(attrbuf, "default") == 0) { + } else if (dStrAsciiCasecmp(attrbuf, "default") == 0) { /* "default" is the background */ type = BACKGROUND; - } else if (dStrcasecmp(attrbuf, "circle") == 0) { + } else if (dStrAsciiCasecmp(attrbuf, "circle") == 0) { type = CIRCLE; - } else if (dStrncasecmp(attrbuf, "poly", 4) == 0) { + } else if (dStrnAsciiCasecmp(attrbuf, "poly", 4) == 0) { type = POLYGON; } else { BUG_MSG("<area> unknown shape: \"%s\"\n", attrbuf); @@ -2390,7 +2394,7 @@ char ch, *p1, *p2; Dstr *Buf = html->attr_data; - if (dStrncasecmp("javascript", Buf->str, 10) == 0) { + if (dStrnAsciiCasecmp("javascript", Buf->str, 10) == 0) { i = strcspn(Buf->str, "'\""); ch = Buf->str[i]; if ((ch == '"' || ch == '\'') && @@ -2436,7 +2440,7 @@ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "href"))) { /* if it's a javascript link, extract the reference. */ - if (tolower(attrbuf[0]) == 'j') + if (D_ASCII_TOLOWER(attrbuf[0]) == 'j') attrbuf = Html_get_javascript_link(html); url = a_Html_url_new(html, attrbuf, NULL, 0); @@ -2544,11 +2548,11 @@ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { /* list_style_type explicitly defined */ - if (dStrcasecmp(attrbuf, "disc") == 0) + if (dStrAsciiCasecmp(attrbuf, "disc") == 0) list_style_type = LIST_STYLE_TYPE_DISC; - else if (dStrcasecmp(attrbuf, "circle") == 0) + else if (dStrAsciiCasecmp(attrbuf, "circle") == 0) list_style_type = LIST_STYLE_TYPE_CIRCLE; - else if (dStrcasecmp(attrbuf, "square") == 0) + else if (dStrAsciiCasecmp(attrbuf, "square") == 0) list_style_type = LIST_STYLE_TYPE_SQUARE; else /* invalid value */ @@ -2849,7 +2853,7 @@ } if ((equiv = a_Html_get_attr(html, tag, tagsize, "http-equiv"))) { - if (!dStrcasecmp(equiv, "refresh") && + if (!dStrAsciiCasecmp(equiv, "refresh") && (content = a_Html_get_attr(html, tag, tagsize, "content"))) { /* Get delay, if present, and make a message with it */ @@ -2901,7 +2905,7 @@ a_Url_free(new_url); dFree(mr_url); - } else if (!dStrcasecmp(equiv, "content-type") && + } else if (!dStrAsciiCasecmp(equiv, "content-type") && (content = a_Html_get_attr(html, tag, tagsize, "content"))) { _MSG("Html_tag_open_meta: content={%s}\n", content); /* Cannot ask cache whether the content type was changed, as @@ -3006,14 +3010,14 @@ dReturn_if_fail (prefs.load_stylesheets); /* CSS stylesheet link */ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "rel")) || - dStrcasecmp(attrbuf, "stylesheet")) + dStrAsciiCasecmp(attrbuf, "stylesheet")) return; /* IMPLIED attributes? */ if (((attrbuf = a_Html_get_attr(html, tag, tagsize, "type")) && - dStrcasecmp(attrbuf, "text/css")) || + dStrAsciiCasecmp(attrbuf, "text/css")) || ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) && - !dStristr(attrbuf, "screen") && dStrcasecmp(attrbuf, "all"))) + !dStriAsciiStr(attrbuf, "screen") && dStrAsciiCasecmp(attrbuf, "all"))) return; if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "href")) || @@ -3227,8 +3231,8 @@ static int Html_tag_compare(const char *p1, const char *p2) { while ( *p2 ) { - if (tolower(*p1) != *p2) - return(tolower(*p1) - *p2); + if (D_ASCII_TOLOWER(*p1) != *p2) + return(D_ASCII_TOLOWER(*p1) - *p2); ++p1; ++p2; } @@ -3470,7 +3474,7 @@ if (ni == -1) { /* TODO: doctype parsing is a bit fuzzy, but enough for the time being */ if (!(html->InFlags & IN_HTML)) { - if (tagsize > 9 && !dStrncasecmp(tag, "<!doctype", 9)) + if (tagsize > 9 && !dStrnAsciiCasecmp(tag, "<!doctype", 9)) Html_parse_doctype(html, tag, tagsize); } /* Ignore unknown tags */ @@ -3587,8 +3591,11 @@ (tag[i] == '=' || isspace(tag[i]) || tag[i] == '>')))) { state = SEEK_TOKEN_START; --i; - } else if (tolower(tag[i]) != tolower(attrname[attr_pos++])) - state = SEEK_ATTR_START; + } else { + if (D_ASCII_TOLOWER(tag[i]) != D_ASCII_TOLOWER(attrname[attr_pos])) + state = SEEK_ATTR_START; + attr_pos++; + } break; case SEEK_TOKEN_START: diff --git a/src/keys.cc b/src/keys.cc index bd78fd5..687d09f 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -103,7 +103,9 @@ { "new-tab" , KEYS_NEW_TAB , FL_CTRL , 't' }, { "left-tab" , KEYS_LEFT_TAB , FL_CTRL | FL_SHIFT , FL_Tab }, + { "left-tab" , KEYS_LEFT_TAB , FL_CTRL , FL_Page_Up }, { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Tab }, + { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Page_Down }, { "close-tab" , KEYS_CLOSE_TAB , FL_CTRL , 'w' }, { "find" , KEYS_FIND , FL_CTRL , 'f' }, { "websearch" , KEYS_WEBSEARCH , FL_CTRL , 's' }, @@ -243,7 +245,7 @@ { uint_t i; for (i = 0; i < sizeof(keyNames) / sizeof(Mapping_t); i++) { - if (!dStrcasecmp(keyNames[i].name, keyName)) { + if (!dStrAsciiCasecmp(keyNames[i].name, keyName)) { return keyNames[i].value; } } @@ -260,7 +262,7 @@ uint_t i; for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) { - if (!dStrcasecmp(default_keys[i].name, commandName)) + if (!dStrAsciiCasecmp(default_keys[i].name, commandName)) return default_keys[i].cmd; } return KEYS_INVALID; @@ -274,7 +276,7 @@ { uint_t i; for (i = 0; i < sizeof(modifierNames) / sizeof(Mapping_t); i++) { - if (!dStrcasecmp(modifierNames[i].name, modifierName)) { + if (!dStrAsciiCasecmp(modifierNames[i].name, modifierName)) { return modifierNames[i].value; } } diff --git a/src/keysrc b/src/keysrc index 706917d..e0c488f 100644 --- a/src/keysrc +++ b/src/keysrc @@ -40,7 +40,9 @@ # "left-tab" and "right-tab" switch to the left/right of the current tab. # <ctrl><shift>tab = left-tab +# <ctrl>PageUp = left-tab # <ctrl>tab = right-tab +# <ctrl>PageDown = right-tab # "back" and "forward" move back/forward through the browser history. #backspace = back diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..bd1ffb1 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,390 @@ +/* + * md5.c was taken from "RFC1321-based (RSA-free) MD5 library" by L. Peter + * Deutsch at http://sourceforge.net/projects/libmd5-rfc/ in October 2011. + * + * The code was not modified when integrated into the Dillo project, but you + * should check the source repository to be sure that there have not been + * modifications since this notice. + */ + +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..184a14b --- /dev/null +++ b/src/md5.h @@ -0,0 +1,100 @@ +/* + * md5.h was taken from "RFC1321-based (RSA-free) MD5 library" by L. Peter + * Deutsch at http://sourceforge.net/projects/libmd5-rfc/ in October 2011. + * + * The code was not modified when integrated into the Dillo project, but you + * should check the source repository to be sure that there have not been + * modifications since this notice. + */ + +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/menu.cc b/src/menu.cc index 658b89b..fe8ff13 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -207,8 +207,20 @@ static void Menu_stylesheet_cb(Fl_Widget*, void *vUrl) { + int mb = Fl::event_button(); const DilloUrl *url = (const DilloUrl *) vUrl; - a_UIcmd_open_url(popup_bw, url); + + if (mb == 1) { + a_UIcmd_open_url(popup_bw, url); + } else if (mb == 2) { + if (prefs.middle_click_opens_new_tab) { + int focus = prefs.focus_new_tab ? 1 : 0; + if (Fl::event_state(FL_SHIFT)) focus = !focus; + a_UIcmd_open_url_nt(popup_bw, url, focus); + } else { + a_UIcmd_open_url_nw(popup_bw, url); + } + } } /* @@ -315,7 +327,8 @@ has_bugs == TRUE ? pm[1].activate() : pm[1].deactivate(); - if (strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) + if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0 && + strncmp(URL_PATH(url), "/vsource/", 9) == 0) pm[0].deactivate(); else pm[0].activate(); diff --git a/src/misc.c b/src/misc.c index 2abfb98..0181a12 100644 --- a/src/misc.c +++ b/src/misc.c @@ -141,23 +141,23 @@ /* HTML try */ for (i = 0; i < Size && dIsspace(p[i]); ++i); - if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) || - (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) || - (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) || - (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) || + if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) || + (Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) || + (Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) || + (Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) || /* this line is workaround for FTP through the Squid proxy */ - (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) { + (Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) { Type = DT_TEXT_HTML; st = 0; /* Images */ - } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) { + } else if (Size >= 4 && !strncmp(p, "GIF8", 4)) { Type = DT_IMAGE_GIF; st = 0; - } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) { + } else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) { Type = DT_IMAGE_PNG; st = 0; - } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) { + } else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) { /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking * at the character representation should be machine independent. */ Type = DT_IMAGE_JPG; @@ -221,20 +221,20 @@ if (!(str = type)) return; - for (s = str; *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); - s++) ; + for (s = str; *s && isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) && + !strchr(tspecials_space, *s); s++) ; if (major) *major = dStrndup(str, s - str); if (*s == '/') { - for (str = ++s; - *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); s++) ; + for (str = ++s; *s && isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) && + !strchr(tspecials_space, *s); s++) ; if (minor) *minor = dStrndup(str, s - str); } if (charset && *s && - (dStrncasecmp(type, "text/", 5) == 0 || - dStrncasecmp(type, "application/xhtml+xml", 21) == 0)) { + (dStrnAsciiCasecmp(type, "text/", 5) == 0 || + dStrnAsciiCasecmp(type, "application/xhtml+xml", 21) == 0)) { /* "charset" parameter defined for text media type in RFC 2046, * application/xhtml+xml in RFC 3236. * @@ -246,7 +246,7 @@ const char terminators[] = " ;\t"; const char key[] = "charset"; - if ((s = dStristr(str, key)) && + if ((s = dStriAsciiStr(str, key)) && (s == str || strchr(terminators, s[-1]))) { s += sizeof(key) - 1; for ( ; *s == ' ' || *s == '\t'; ++s); @@ -283,12 +283,12 @@ a_Misc_parse_content_type(ct1, &major1, &minor1, &charset1); a_Misc_parse_content_type(ct2, &major2, &minor2, &charset2); - if (major1 && major2 && !dStrcasecmp(major1, major2) && - minor1 && minor2 && !dStrcasecmp(minor1, minor2) && + if (major1 && major2 && !dStrAsciiCasecmp(major1, major2) && + minor1 && minor2 && !dStrAsciiCasecmp(minor1, minor2) && ((!charset1 && !charset2) || - (charset1 && charset2 && !dStrcasecmp(charset1, charset2)) || - (!charset1 && charset2 && !dStrcasecmp(charset2, "UTF-8")) || - (charset1 && !charset2 && !dStrcasecmp(charset1, "UTF-8")))) { + (charset1 && charset2 && !dStrAsciiCasecmp(charset1, charset2)) || + (!charset1 && charset2 && !dStrAsciiCasecmp(charset2, "UTF-8")) || + (charset1 && !charset2 && !dStrAsciiCasecmp(charset1, "UTF-8")))) { ret = 0; } else { ret = 1; @@ -328,22 +328,23 @@ return 0; /* there's no mismatch without server type */ for (i = 1; MimeTypes[i].str; ++i) - if (dStrncasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) == 0) + if (dStrnAsciiCasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) ==0) break; if (!MimeTypes[i].str) { /* type not found, no mismatch */ st = 0; - } else if (dStrncasecmp(EntryType, "image/", 6) == 0 && - !dStrncasecmp(DetectedType,MimeTypes[i].str,MimeTypes[i].len)){ + } else if (dStrnAsciiCasecmp(EntryType, "image/", 6) == 0 && + !dStrnAsciiCasecmp(DetectedType, MimeTypes[i].str, + MimeTypes[i].len)){ /* An image, and there's an exact match */ st = 0; - } else if (dStrncasecmp(EntryType, "text/", 5) || - dStrncasecmp(DetectedType, "application/", 12)) { + } else if (dStrnAsciiCasecmp(EntryType, "text/", 5) || + dStrnAsciiCasecmp(DetectedType, "application/", 12)) { /* Not an application sent as text */ st = 0; - } else if (dStrncasecmp(EntryType, "application/xhtml+xml", 21) && - dStrncasecmp(DetectedType, "text/html", 9)) { + } else if (dStrnAsciiCasecmp(EntryType, "application/xhtml+xml", 21) && + dStrnAsciiCasecmp(DetectedType, "text/html", 9)) { /* XML version of HTML */ st = 0; } diff --git a/src/nav.c b/src/nav.c index bea90dd..7a56771 100644 --- a/src/nav.c +++ b/src/nav.c @@ -481,9 +481,10 @@ a_Nav_cancel_expect(bw); if (a_Nav_stack_size(bw)) { h_url = a_History_get_url(NAV_TOP_UIDX(bw)); - if (strncmp(URL_STR(h_url), "dpi:/vsource/", 13) == 0) { - /* disable reload for view source dpi */ - confirmed = 0; + if (dStrAsciiCasecmp(URL_SCHEME(h_url), "dpi") == 0 && + strncmp(URL_PATH(h_url), "/vsource/", 9) == 0) { + /* allow reload for view source dpi */ + confirmed = 1; } else if (URL_FLAGS(h_url) & URL_Post) { /* Attempt to repost data, let's confirm... */ choice = a_Dialog_choice5("Repost form data?", diff --git a/src/prefsparser.cc b/src/prefsparser.cc index efe64a0..7fd6cf4 100644 --- a/src/prefsparser.cc +++ b/src/prefsparser.cc @@ -127,8 +127,8 @@ switch (node->type) { case PREFS_BOOL: - *(bool_t *)node->pref = (!dStrcasecmp(value, "yes") || - !dStrcasecmp(value, "true")); + *(bool_t *)node->pref = (!dStrAsciiCasecmp(value, "yes") || + !dStrAsciiCasecmp(value, "true")); break; case PREFS_COLOR: *(int32_t *)node->pref = a_Color_parse(value, *(int32_t*)node->pref,&st); @@ -167,18 +167,18 @@ &prefs.width, &prefs.height); break; case PREFS_FILTER: - if (!dStrcasecmp(value, "same_domain")) + if (!dStrAsciiCasecmp(value, "same_domain")) prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN; else { - if (dStrcasecmp(value, "allow_all")) + if (dStrAsciiCasecmp(value, "allow_all")) MSG_WARN("prefs: unrecognized value for filter_auto_requests\n"); prefs.filter_auto_requests = PREFS_FILTER_ALLOW_ALL; } break; case PREFS_PANEL_SIZE: - if (!dStrcasecmp(value, "tiny")) + if (!dStrAsciiCasecmp(value, "tiny")) prefs.panel_size = P_tiny; - else if (!dStrcasecmp(value, "small")) + else if (!dStrAsciiCasecmp(value, "small")) prefs.panel_size = P_small; else /* default to "medium" */ prefs.panel_size = P_medium; diff --git a/src/styleengine.cc b/src/styleengine.cc index 6060836..3f674de 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -303,15 +303,15 @@ *c = '\0'; dStrstrip(p->value.strVal); - if (strcmp (p->value.strVal, "serif") == 0) + if (dStrAsciiCasecmp (p->value.strVal, "serif") == 0) fontName = prefs.font_serif; - else if (strcmp (p->value.strVal, "sans-serif") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "sans-serif") == 0) fontName = prefs.font_sans_serif; - else if (strcmp (p->value.strVal, "cursive") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "cursive") == 0) fontName = prefs.font_cursive; - else if (strcmp (p->value.strVal, "fantasy") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "fantasy") == 0) fontName = prefs.font_fantasy; - else if (strcmp (p->value.strVal, "monospace") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "monospace") == 0) fontName = prefs.font_monospace; else if (Font::exists(layout, p->value.strVal)) fontName = p->value.strVal; @@ -331,30 +331,33 @@ if (p->type == CSS_TYPE_ENUM) { switch (p->value.intVal) { case CSS_FONT_SIZE_XX_SMALL: - fontAttrs.size = roundInt(11.0 * prefs.font_factor); + fontAttrs.size = roundInt(8.1 * prefs.font_factor); break; case CSS_FONT_SIZE_X_SMALL: - fontAttrs.size = roundInt(12.0 * prefs.font_factor); + fontAttrs.size = roundInt(9.7 * prefs.font_factor); break; case CSS_FONT_SIZE_SMALL: - fontAttrs.size = roundInt(13.0 * prefs.font_factor); + fontAttrs.size = roundInt(11.7 * prefs.font_factor); break; case CSS_FONT_SIZE_MEDIUM: fontAttrs.size = roundInt(14.0 * prefs.font_factor); break; case CSS_FONT_SIZE_LARGE: + fontAttrs.size = roundInt(16.8 * prefs.font_factor); break; case CSS_FONT_SIZE_X_LARGE: - fontAttrs.size = roundInt(16.0 * prefs.font_factor); + fontAttrs.size = roundInt(20.2 * prefs.font_factor); break; case CSS_FONT_SIZE_XX_LARGE: - fontAttrs.size = roundInt(17.0 * prefs.font_factor); + fontAttrs.size = roundInt(24.2 * prefs.font_factor); break; case CSS_FONT_SIZE_SMALLER: - fontAttrs.size -= roundInt(1.0 * prefs.font_factor); + fontAttrs.size = roundInt(fontAttrs.size * 0.83 * + prefs.font_factor); break; case CSS_FONT_SIZE_LARGER: - fontAttrs.size += roundInt(1.0 * prefs.font_factor); + fontAttrs.size = roundInt(fontAttrs.size * 1.2 * + prefs.font_factor); break; default: assert(false); // invalid font-size enum @@ -561,6 +564,9 @@ break; case CSS_PROPERTY_TEXT_INDENT: computeLength (&attrs->textIndent, p->value.intVal, attrs->font); + break; + case CSS_PROPERTY_TEXT_TRANSFORM: + attrs->textTransform = (TextTransform) p->value.intVal; break; case CSS_PROPERTY_VERTICAL_ALIGN: attrs->valign = (VAlignType) p->value.intVal; diff --git a/src/table.cc b/src/table.cc index d48a0c4..622868c 100644 --- a/src/table.cc +++ b/src/table.cc @@ -81,13 +81,13 @@ a_Html_parse_length (html, attrbuf)); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) { - if (dStrcasecmp (attrbuf, "left") == 0) + if (dStrAsciiCasecmp (attrbuf, "left") == 0) html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_LEFT); - else if (dStrcasecmp (attrbuf, "right") == 0) + else if (dStrAsciiCasecmp (attrbuf, "right") == 0) html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_RIGHT); - else if (dStrcasecmp (attrbuf, "center") == 0) + else if (dStrAsciiCasecmp (attrbuf, "center") == 0) html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_CENTER); } @@ -347,9 +347,6 @@ if (a_Html_get_attr(html, tag, tagsize, "nowrap")) html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE, CSS_TYPE_ENUM, WHITE_SPACE_NOWRAP); - else - html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE, - CSS_TYPE_ENUM, WHITE_SPACE_NORMAL); a_Html_tag_set_align_attr (html, tag, tagsize); diff --git a/src/uicmd.cc b/src/uicmd.cc index 3822504..b02a574 100644 --- a/src/uicmd.cc +++ b/src/uicmd.cc @@ -597,7 +597,7 @@ /* Filter URL string */ new_urlstr = a_Url_string_strip_delimiters(urlstr); - if (!dStrncasecmp(new_urlstr, "file:", 5)) { + if (!dStrnAsciiCasecmp(new_urlstr, "file:", 5)) { /* file URI */ ch = new_urlstr[5]; if (!ch || ch == '.') { diff --git a/src/url.c b/src/url.c index 78f717c..4c7ea35 100644 --- a/src/url.c +++ b/src/url.c @@ -54,7 +54,7 @@ #define URL_STR_FIELD_CMP(s1,s2) \ (s1) && (s2) ? strcmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1 #define URL_STR_FIELD_I_CMP(s1,s2) \ - (s1) && (s2) ? dStrcasecmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1 + (s1) && (s2) ? dStrAsciiCasecmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1 /* * Return the url as a string. @@ -586,8 +586,7 @@ newstr = dNew(char, 6*strlen(str)+1); for (c = newstr; *str; str++) - if ((dIsalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str)) - /* we really need isalnum for the "C" locale */ + if ((dIsalnum(*str) && isascii(*str)) || strchr(verbatim, *str)) *c++ = *str; else if (*str == ' ') *c++ = '+'; @@ -652,10 +651,10 @@ _MSG("an IPv4 address\n"); return TRUE; } - if (*host == '[' && - (len == strspn(host, "0123456789abcdefABCDEF:.[]"))) { + if (strchr(host, ':') && + (len == strspn(host, "0123456789abcdefABCDEF:."))) { /* The precise format is shown in section 3.2.2 of rfc 3986 */ - _MSG("an IPv6 address\n"); + MSG("an IPv6 address\n"); return TRUE; } return FALSE; @@ -702,7 +701,7 @@ for (i = 0; i < tld_num; i++) { if (strlen(tlds[i]) == (uint_t) tld_len && - !dStrncasecmp(tlds[i], host + start, tld_len)) { + !dStrnAsciiCasecmp(tlds[i], host + start, tld_len)) { _MSG("TLD code matched %s\n", tlds[i]); ret++; break; @@ -759,6 +758,7 @@ if (!u1 || !u2) return FALSE; - return dStrcasecmp(Url_host_find_public_suffix(URL_HOST(u1)), - Url_host_find_public_suffix(URL_HOST(u2))) ? FALSE :TRUE; -} + return dStrAsciiCasecmp(Url_host_find_public_suffix(URL_HOST(u1)), + Url_host_find_public_suffix(URL_HOST(u2))) + ? FALSE : TRUE; +} diff --git a/src/web.cc b/src/web.cc index 74851f2..48c368b 100644 --- a/src/web.cc +++ b/src/web.cc @@ -95,7 +95,7 @@ } else { /* A non-RootUrl. At this moment we only handle image-children */ - if (!dStrncasecmp(Type, "image/", 6)) { + if (!dStrnAsciiCasecmp(Type, "image/", 6)) { dw = (Widget*) a_Mime_set_viewer(Type, Web, Call, Data); } else { MSG_HTTP("'%s' cannot be displayed as image; has media type '%s'\n", diff --git a/test/cookies.c b/test/cookies.c index af59cb4..1468c24 100644 --- a/test/cookies.c +++ b/test/cookies.c @@ -918,13 +918,13 @@ /* SOME IP ADDRS */ - a_Cookies_set("name=val", "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]", + a_Cookies_set("name=val", "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", "/", NULL); expect(__LINE__, "Cookie: name=val\r\n", "http", - "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]", "/"); - - a_Cookies_set("name=val", "[::FFFF:129.144.52.38]", "/", NULL); - expect(__LINE__, "Cookie: name=val\r\n", "http", "[::FFFF:129.144.52.38]", + "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", "/"); + + a_Cookies_set("name=val", "::FFFF:129.144.52.38", "/", NULL); + expect(__LINE__, "Cookie: name=val\r\n", "http", "::FFFF:129.144.52.38", "/"); a_Cookies_set("name=val", "127.0.0.1", "/", NULL); diff --git a/test/dw_anchors_test.cc b/test/dw_anchors_test.cc index 10525f1..cb839f4 100644 --- a/test/dw_anchors_test.cc +++ b/test/dw_anchors_test.cc @@ -69,14 +69,14 @@ char buf[16]; strcpy (buf, numbers[textblockNo]); - buf[0] = toupper (buf[0]); + buf[0] = lout::misc::AsciiToupper (buf[0]); topTextblock->addText (buf, headingStyle); topTextblock->addParbreak (5, headingStyle); for (int i = 0; i < 30; i++) { strcpy (buf, numbers[textblockNo]); if (i == 0) - buf[0] = toupper (buf[0]); + buf[0] = lout::misc::AsciiToupper (buf[0]); strcat (buf, i == 29 ? "." : ","); topTextblock->addText (buf, wordStyle); @@ -109,7 +109,7 @@ for (int i = 0; i < 10; i++) { char buf[16]; strcpy (buf, numbers[i]); - buf[0] = toupper (buf[0]); + buf[0] = lout::misc::AsciiToupper (buf[0]); buttonLabel[i] = strdup(buf); Fl_Button *button = new Fl_Button(0, 20 * i, 50, 20, buttonLabel[i]); button->callback (anchorCallback, (void*)(long)i);