- djm@cvs.openbsd.org 2011/05/06 21:31:38
     [readconf.c ssh_config.5]
     support negated Host matching, e.g.

     Host *.example.org !c.example.org
        User mekmitasdigoat

     Will match "a.example.org", "b.example.org", but not "c.example.org"
     ok markus@
diff --git a/ChangeLog b/ChangeLog
index 00f54f9..caec1dd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -23,6 +23,15 @@
      [ssh.c ssh_config.5]
      add a %L expansion (short-form of the local host name) for ControlPath;
      sync some more expansions with LocalCommand; ok markus@
+   - djm@cvs.openbsd.org 2011/05/06 21:31:38
+     [readconf.c ssh_config.5]
+     support negated Host matching, e.g.
+     
+     Host *.example.org !c.example.org
+        User mekmitasdigoat
+     
+     Will match "a.example.org", "b.example.org", but not "c.example.org"
+     ok markus@
 
 20110510
  - (dtucker) [openbsd-compat/openssl-compat.{c,h}] Bug #1882: fix
diff --git a/readconf.c b/readconf.c
index eb4a8b9..927e7fe 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.190 2010/11/13 23:27:50 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.191 2011/05/06 21:31:38 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -354,7 +354,7 @@
 		    int *activep)
 {
 	char *s, **charptr, *endofnumber, *keyword, *arg, *arg2, fwdarg[256];
-	int opcode, *intptr, value, value2, scale;
+	int negated, opcode, *intptr, value, value2, scale;
 	LogLevel *log_level_ptr;
 	long long orig, val64;
 	size_t len;
@@ -793,12 +793,28 @@
 
 	case oHost:
 		*activep = 0;
-		while ((arg = strdelim(&s)) != NULL && *arg != '\0')
+		arg2 = NULL;
+		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+			negated = *arg == '!';
+			if (negated)
+				arg++;
 			if (match_pattern(host, arg)) {
-				debug("Applying options for %.100s", arg);
+				if (negated) {
+					debug("%.200s line %d: Skipping Host "
+					    "block because of negated match "
+					    "for %.100s", filename, linenum,
+					    arg);
+					*activep = 0;
+					break;
+				}
+				if (!*activep)
+					arg2 = arg; /* logged below */
 				*activep = 1;
-				break;
 			}
+		}
+		if (*activep)
+			debug("%.200s line %d: Applying options for %.100s",
+			    filename, linenum, arg2);
 		/* Avoid garbage check below, as strdelim is done. */
 		return 0;
 
diff --git a/ssh_config.5 b/ssh_config.5
index a5bad8c..5bdc7fe 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,7 +33,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh_config.5,v 1.147 2011/05/06 21:18:02 djm Exp $
+.\" $OpenBSD: ssh_config.5,v 1.148 2011/05/06 21:31:38 djm Exp $
 .Dd $Mdocdate: May 6 2011 $
 .Dt SSH_CONFIG 5
 .Os
@@ -112,6 +112,15 @@
 argument given on the command line (i.e. the name is not converted to
 a canonicalized host name before matching).
 .Pp
+A pattern entry may be negated by prefixing it with an exclamation mark
+.Pq Sq !\& .
+If a negated entry is matched, then the
+.Cm Host
+entry is ignored, regardless of whether any other patterns on the line
+match.
+Negated matches are therefore useful to provide exceptions for wildcard
+matches.
+.Pp
 See
 .Sx PATTERNS
 for more information on patterns.