Index: docs/MANUAL
===================================================================
RCS file: /cvsroot/curl/curl/docs/MANUAL,v
retrieving revision 1.40
diff -u -r1.40 MANUAL
--- docs/MANUAL	1 Aug 2008 18:41:14 -0000	1.40
+++ docs/MANUAL	15 Jan 2009 03:48:34 -0000
@@ -136,6 +136,11 @@
 
         curl -U user:passwd -x my-proxy:888 http://www.get.this/
 
+ A comma-separated list of hosts and domains which do not use the proxy can
+ be specified as:
+
+        curl --noproxy=localhost,get.this -x my-proxy:888 http://www.get.this/
+
  curl also supports SOCKS4 and SOCKS5 proxies with --socks4 and --socks5.
 
  See also the environment variables Curl support that offer further proxy
@@ -793,8 +798,9 @@
 
         NO_PROXY
 
-  If a tail substring of the domain-path for a host matches one of these
-  strings, transactions with that node will not be proxied.
+  If the host name matches one of these strings, or the host is within the
+  domain of one of these strings, transactions with that node will not be
+  proxied.
 
 
   The usage of the -x/--proxy flag overrides the environment variables.
Index: include/curl/curl.h
===================================================================
RCS file: /cvsroot/curl/curl/include/curl/curl.h,v
retrieving revision 1.371
diff -u -r1.371 curl.h
--- include/curl/curl.h	10 Dec 2008 23:13:31 -0000	1.371
+++ include/curl/curl.h	15 Jan 2009 03:48:35 -0000
@@ -1150,6 +1150,14 @@
   CINIT(PROXYUSERNAME, OBJECTPOINT, 175),
   CINIT(PROXYPASSWORD, OBJECTPOINT, 176),
 
+  /* Comma separated list of hostnames defining no-proxy zones. These should
+     match both hostnames directly, and hostnames within a domain. For example,
+     local.com will match local.com and www.local.com, but NOT notlocal.com or
+     www.notlocal.com. For compatibility with other implementations of this,
+     .local.com will be considered to be the same as local.com. A single * is
+     the only valid wildcard, and effectively disables the use of proxy. */
+  CINIT(NOPROXY, OBJECTPOINT, 177),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
Index: include/curl/typecheck-gcc.h
===================================================================
RCS file: /cvsroot/curl/curl/include/curl/typecheck-gcc.h,v
retrieving revision 1.8
diff -u -r1.8 typecheck-gcc.h
--- include/curl/typecheck-gcc.h	17 Oct 2008 03:59:02 -0000	1.8
+++ include/curl/typecheck-gcc.h	15 Jan 2009 03:48:35 -0000
@@ -201,6 +201,7 @@
    (option) == CURLOPT_PROXYUSERPWD ||                                        \
    (option) == CURLOPT_PROXYUSERNAME ||                                       \
    (option) == CURLOPT_PROXYPASSWORD ||                                       \
+   (option) == CURLOPT_NOPROXY ||                                             \
    (option) == CURLOPT_ENCODING ||                                            \
    (option) == CURLOPT_REFERER ||                                             \
    (option) == CURLOPT_USERAGENT ||                                           \
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.780
diff -u -r1.780 url.c
--- lib/url.c	8 Jan 2009 00:31:49 -0000	1.780
+++ lib/url.c	15 Jan 2009 03:48:37 -0000
@@ -1662,6 +1662,13 @@
     result = setstropt(&data->set.str[STRING_PROXYPASSWORD],
                        va_arg(param, char *));
     break;
+  case CURLOPT_NOPROXY:
+    /*
+     * proxy exception list
+     */
+    result = setstropt(&data->set.str[STRING_NOPROXY],
+                       va_arg(param, char *));
+    break;
 #endif
 
   case CURLOPT_RANGE:
@@ -3354,6 +3361,66 @@
 
 #ifndef CURL_DISABLE_PROXY
 /****************************************************************
+* Checks if the host is in the noproxy list. returns true if it matches
+* and therefore the proxy should NOT be used.
+****************************************************************/
+static bool check_noproxy(const char* name, const char* no_proxy)
+{
+  /* no_proxy=domain1.dom,host.domain2.dom
+   *   (a comma-separated list of hosts which should
+   *   not be proxied, or an asterisk to override
+   *   all proxy variables)
+   */
+  char *tmp_no_proxy = NULL;
+
+  if(no_proxy && no_proxy[0]) {
+    if(Curl_raw_equal("*", no_proxy)) {
+      return TRUE;
+    }
+    /* NO_PROXY was specified and it wasn't just an asterisk */
+    tmp_no_proxy = strdup(no_proxy);
+  }
+  if (tmp_no_proxy) {
+    char *nope;
+    char *no_proxy_tok_buf;
+
+    nope=strtok_r(tmp_no_proxy, ", ", &no_proxy_tok_buf);
+    while(nope) {
+      size_t namelen;
+      char *endptr = strchr(name, ':');
+      if(endptr)
+        namelen=endptr-name;
+      else
+        namelen=strlen(name);
+
+      /* To match previous behaviour, where it was necessary to specify
+       * .local.com to prevent matching notlocal.com, we will leave the . off
+       */
+      if (nope[0] == '.')
+        ++nope;
+      if(strlen(nope) <= namelen) {
+        const char *checkn=
+          name + namelen - strlen(nope);
+        if(checkprefix(nope, checkn)) {
+            if(strlen(nope) == namelen || *(checkn - 1) == '.') {
+              /* We either have an exact match, or the previous character is a .
+               * so it is within the same domain, so no proxy for this host.
+               */
+              break;
+          }
+        }
+      }
+      nope=strtok_r(NULL, ", ", &no_proxy_tok_buf);
+    }
+    free(tmp_no_proxy);
+    if (nope)
+        return TRUE;
+  } /* NO_PROXY was specified and it wasn't just an asterisk, or strdup failed */
+
+  return FALSE;
+}
+
+/****************************************************************
 * Detect what (if any) proxy to use. Remember that this selects a host
 * name and is not limited to HTTP proxies only.
 * The returned pointer must be freed by the caller (unless NULL)
@@ -3381,91 +3448,56 @@
    * checked if the lowercase versions don't exist.
    */
   char *no_proxy=NULL;
-  char *no_proxy_tok_buf;
   char proxy_env[128];
 
   no_proxy=curl_getenv("no_proxy");
   if(!no_proxy)
     no_proxy=curl_getenv("NO_PROXY");
 
-  if(!no_proxy || !Curl_raw_equal("*", no_proxy)) {
-    /* NO_PROXY wasn't specified or it wasn't just an asterisk */
-    char *nope;
-
-    nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL;
-    while(nope) {
-      size_t namelen;
-      char *endptr = strchr(conn->host.name, ':');
-      if(endptr)
-        namelen=endptr-conn->host.name;
-      else
-        namelen=strlen(conn->host.name);
-
-      if(strlen(nope) <= namelen) {
-        char *checkn=
-          conn->host.name + namelen - strlen(nope);
-        if(checkprefix(nope, checkn)) {
-          /* no proxy for this host! */
-          break;
-        }
-      }
-      nope=strtok_r(NULL, ", ", &no_proxy_tok_buf);
-    }
-    if(!nope) {
-      /* It was not listed as without proxy */
-      char *protop = conn->protostr;
-      char *envp = proxy_env;
-      char *prox;
-
-      /* Now, build <protocol>_proxy and check for such a one to use */
-      while(*protop)
-        *envp++ = (char)tolower((int)*protop++);
-
-      /* append _proxy */
-      strcpy(envp, "_proxy");
-
-      /* read the protocol proxy: */
+  if (!check_noproxy(conn->host.name, no_proxy)) {
+    /* It was not listed as without proxy */
+    char *protop = conn->protostr;
+    char *envp = proxy_env;
+    char *prox;
+
+    /* Now, build <protocol>_proxy and check for such a one to use */
+    while(*protop)
+      *envp++ = (char)tolower((int)*protop++);
+
+    /* append _proxy */
+    strcpy(envp, "_proxy");
+
+    /* read the protocol proxy: */
+    prox=curl_getenv(proxy_env);
+
+    /*
+     * We don't try the uppercase version of HTTP_PROXY because of
+     * security reasons:
+     *
+     * When curl is used in a webserver application
+     * environment (cgi or php), this environment variable can
+     * be controlled by the web server user by setting the
+     * http header 'Proxy:' to some value.
+     *
+     * This can cause 'internal' http/ftp requests to be
+     * arbitrarily redirected by any external attacker.
+     */
+    if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) {
+      /* There was no lowercase variable, try the uppercase version: */
+      for(envp = proxy_env; *envp; envp++)
+        *envp = (char)toupper((int)*envp);
       prox=curl_getenv(proxy_env);
+    }
 
-      /*
-       * We don't try the uppercase version of HTTP_PROXY because of
-       * security reasons:
-       *
-       * When curl is used in a webserver application
-       * environment (cgi or php), this environment variable can
-       * be controlled by the web server user by setting the
-       * http header 'Proxy:' to some value.
-       *
-       * This can cause 'internal' http/ftp requests to be
-       * arbitrarily redirected by any external attacker.
-       */
-      if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) {
-        /* There was no lowercase variable, try the uppercase version: */
-        for(envp = proxy_env; *envp; envp++)
-          *envp = (char)toupper((int)*envp);
-        prox=curl_getenv(proxy_env);
-      }
-
-      if(prox && *prox) { /* don't count "" strings */
-        proxy = prox; /* use this */
-      }
-      else {
-        proxy = curl_getenv("all_proxy"); /* default proxy to use */
-        if(!proxy)
-          proxy=curl_getenv("ALL_PROXY");
-      }
-
-      if(proxy && *proxy) {
-        long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING);
-
-        if(conn->proxytype == CURLPROXY_HTTP) {
-          /* force this connection's protocol to become HTTP */
-          conn->protocol = PROT_HTTP | bits;
-          conn->bits.proxy = conn->bits.httpproxy = TRUE;
-        }
-      }
-    } /* if(!nope) - it wasn't specified non-proxy */
-  } /* NO_PROXY wasn't specified or '*' */
+    if(prox && *prox) { /* don't count "" strings */
+      proxy = prox; /* use this */
+    }
+    else {
+      proxy = curl_getenv("all_proxy"); /* default proxy to use */
+      if(!proxy)
+        proxy=curl_getenv("ALL_PROXY");
+    }
+  } /* if (!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified non-proxy */
   if(no_proxy)
     free(no_proxy);
 
@@ -4103,15 +4135,26 @@
                         and the SessionHandle */
 
   conn->proxytype = data->set.proxytype; /* type */
+
+#ifdef CURL_DISABLE_PROXY
+
+  conn->bits.proxy = FALSE;
+  conn->bits.httpproxy = FALSE;
+  conn->bits.proxy_user_passwd = FALSE;
+  conn->bits.tunnel_proxy = FALSE;
+
+#else /* CURL_DISABLE_PROXY */
+
   conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] &&
                             *data->set.str[STRING_PROXY]);
   conn->bits.httpproxy = (bool)(conn->bits.proxy
                                 && (conn->proxytype == CURLPROXY_HTTP));
+  conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERNAME]);
+  conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
 
+#endif /* CURL_DISABLE_PROXY */
 
   conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERNAME]);
-  conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERNAME]);
-  conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
   conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
   conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
 
@@ -4191,11 +4234,34 @@
 
   if(!proxy)
     proxy = detect_proxy(conn);
+  else if(data->set.str[STRING_NOPROXY]) {
+    if (check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
+      free(proxy);  /* proxy is in exception list */
+      proxy = NULL;
+    }
+  }
   if(proxy && !*proxy) {
     free(proxy);  /* Don't bother with an empty proxy string */
     proxy = NULL;
   }
   /* proxy must be freed later unless NULL */
+  if(proxy && *proxy) {
+    long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING);
+
+    if(conn->proxytype == CURLPROXY_HTTP) {
+      /* force this connection's protocol to become HTTP */
+      conn->protocol = PROT_HTTP | bits;
+      conn->bits.httpproxy = TRUE;
+    }
+    conn->bits.proxy = TRUE;
+  }
+  else {
+      /* we aren't using the proxy after all... */
+      conn->bits.proxy = FALSE;
+      conn->bits.httpproxy = FALSE;
+      conn->bits.proxy_user_passwd = FALSE;
+      conn->bits.tunnel_proxy = FALSE;
+  }
 #endif /* CURL_DISABLE_PROXY */
 
   /*************************************************************
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.401
diff -u -r1.401 urldata.h
--- lib/urldata.h	13 Jan 2009 06:44:03 -0000	1.401
+++ lib/urldata.h	15 Jan 2009 03:48:38 -0000
@@ -1363,6 +1363,7 @@
   STRING_PASSWORD,        /* <password>, if used */
   STRING_PROXYUSERNAME,   /* Proxy <username>, if used */
   STRING_PROXYPASSWORD,   /* Proxy <password>, if used */
+  STRING_NOPROXY,         /* List of urls which should not use the proxy, if used */
 
   /* -- end of strings -- */
   STRING_LAST /* not used, just an end-of-list marker */
Index: src/main.c
===================================================================
RCS file: /cvsroot/curl/curl/src/main.c,v
retrieving revision 1.502
diff -u -r1.502 main.c
--- src/main.c	7 Jan 2009 19:39:35 -0000	1.502
+++ src/main.c	15 Jan 2009 03:48:40 -0000
@@ -448,6 +448,7 @@
   char *userpwd;
   char *proxyuserpwd;
   char *proxy;
+  char *noproxy;
   bool proxytunnel;
   bool ftp_append;         /* APPE on ftp */
   bool mute;               /* shutup */
@@ -772,6 +773,7 @@
     " -N/--no-buffer     Disable buffering of the output stream",
     "    --no-keepalive  Disable keepalive use on the connection",
     "    --no-sessionid  Disable SSL session-ID reusing (SSL)",
+    "    --noproxy       Comma-separated list of hosts which do not use proxy",
     "    --ntlm          Use HTTP NTLM authentication (H)",
     " -o/--output <file> Write output to <file> instead of stdout",
     "    --pass  <pass>  Pass phrase for the private key (SSL/SSH)",
@@ -1666,6 +1668,7 @@
     {"$2", "socks5-hostname", TRUE},
     {"$3", "keepalive-time",  TRUE},
     {"$4", "post302",    FALSE},
+    {"$5", "noproxy",    TRUE},
 
     {"0", "http1.0",     FALSE},
     {"1", "tlsv1",       FALSE},
@@ -2175,6 +2178,10 @@
       case '4': /* --post302 */
         config->post302 = toggle;
         break;
+      case '5': /* --noproxy */
+        /* This specifies the noproxy list */
+        GetStr(&config->noproxy, nextarg);
+        break;
       }
       break;
     case '#': /* --progress-bar */
@@ -3641,6 +3648,8 @@
     free(config->proxy);
   if(config->proxyuserpwd)
     free(config->proxyuserpwd);
+  if(config->noproxy)
+    free(config->noproxy);
   if(config->cookie)
     free(config->cookie);
   if(config->cookiefile)
@@ -4559,6 +4568,7 @@
         my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii);
         my_setopt(curl, CURLOPT_USERPWD, config->userpwd);
         my_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
+        my_setopt(curl, CURLOPT_NOPROXY, config->noproxy);
         my_setopt(curl, CURLOPT_RANGE, config->range);
         my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
         my_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
