Jon Nelson <[EMAIL PROTECTED]> writes:

> I don't think I could have voiced it better myself. Mitch is absolutely
> right here when he says that empty ("") is *not* the same as
> non-existant.  It may not make perfect sense for SENDER, but for other
> variables it may -- indeed, the mere presence (regardless of content) of
> the variable is enough for some programs.
>
> Please, Sam, reconsider this code that seems to me to:
>
> a) /not/ operate on the principle of least surprise (thanks, "Courier
> User").
> b) /not/ follow common convention
> c) unintuitive
> d) it breaks a bunch of software
>
> Adding the 'unset' directive would allow those people that rely on the
> current (broken, IMO) behavior to continue "unsetting" variables.

Attached is a patch to maildrop (in the 0.44.2.20031219 release of
Courier) which implements the following features:

1.   VAR=""   

     No longer unsets the variable, but simply sets it to
     an empty string, as does every other variable-setting
     language that I know of.

2.   unset VAR

     A new directive that completely removes the variable.

I've done a small amount of testing, and it seems OK, but please do your
own testing, as well.

*** maildrop/lexer.C.save	Wed Jan 14 09:31:21 2004
--- maildrop/lexer.C	Wed Jan 14 09:32:29 2004
***************
*** 384,389 ****
--- 384,391 ----
  			t.Type(Token::importtoken);
  		else if (pattern == "-")		// Hack
  			t.Type(Token::minus);
+ 		else if (pattern == "unset")
+ 			t.Type(Token::unset);
  		else
  			t.Type(Token::qstring);
  		return;
*** maildrop/recipenode.C.save	Wed Jan 14 09:27:59 2004
--- maildrop/recipenode.C	Wed Jan 14 09:49:54 2004
***************
*** 1066,1071 ****
--- 1066,1089 ----
  			b.append( (unsigned long)t );
  		}
  		break;
+ 	case unset:
+ 		if (!firstChild)
+ 			throw "Internal error in unset statement.";
+ 		firstChild->Evaluate(r, b);
+ 		{
+ 		Buffer	s;
+ 
+ 			if (VerboseLevel() > 3)
+ 			{
+ 				s="unset ";
+ 				s += b;
+ 				s += '\0';
+ 				r.errmsg(*this, s);
+ 			}
+ 
+ 			UnsetVar(b);
+ 		}
+ 		break;
  	}
  }
  
*** maildrop/recipenode.h.save	Wed Jan 14 09:27:46 2004
--- maildrop/recipenode.h	Wed Jan 14 09:48:48 2004
***************
*** 116,122 ****
  		gdbmfetch,
  		gdbmstore,
  		timetoken,
! 		importtoken
  		} nodeType;
  
  	RecipeNode(RecipeNodeType);
--- 116,123 ----
  		gdbmfetch,
  		gdbmstore,
  		timetoken,
! 		importtoken,
! 		unset
  		} nodeType;
  
  	RecipeNode(RecipeNodeType);
*** maildrop/recipeparse.C.save	Wed Jan 14 09:28:08 2004
--- maildrop/recipeparse.C	Wed Jan 14 09:46:47 2004
***************
*** 235,240 ****
--- 235,248 ----
  			throw "Syntax error.";
  		lex->token(cur_tok);
  		return (n);
+ 	case Token::unset:
+ 		lex->token(cur_tok);
+ 		n=alloc(RecipeNode::unset);
+ 		n->AppendSibling( ParseExpr());
+ 		if (cur_tok.Type() != Token::semicolon)
+ 			throw "Syntax error.";
+ 		lex->token(cur_tok);
+ 		return (n);
  	default:
  		break;
  	}
*** maildrop/token.C.save	Wed Jan 14 09:32:48 2004
--- maildrop/token.C	Wed Jan 14 09:35:07 2004
***************
*** 67,73 ****
  		"gdbmfetch",
  		"gdbmstore",
  		"time",
! 		"import"
  		} ;
  
  static Buffer namebuf;
--- 67,74 ----
  		"gdbmfetch",
  		"gdbmstore",
  		"time",
! 		"import",
! 		"unset"
  		} ;
  
  static Buffer namebuf;
*** maildrop/token.h.save	Wed Jan 14 09:32:53 2004
--- maildrop/token.h	Wed Jan 14 09:46:33 2004
***************
*** 84,89 ****
--- 84,90 ----
  		gdbmstore,
  		timetoken,
  		importtoken,
+ 		unset,
  		};
  private:
  	tokentype type;
*** maildrop/varlist.C.save	Wed Jan 14 09:15:22 2004
--- maildrop/varlist.C	Wed Jan 14 09:22:41 2004
***************
*** 16,21 ****
--- 16,52 ----
  
  static Variable *varlist[101];
  
+ void UnsetVar(const Buffer &var)
+ {
+ int varlen=var.Length();
+ unsigned n=0;
+ int i;
+ const char *p=var;
+ 	for (i=varlen; i; --i)
+ 		n = (n << 1) ^ (unsigned char)*p++;
+ 
+ 	if (var.Length() == 7 &&
+ 		strncmp( (const char *)var, "VERBOSE", 7) == 0)
+ 	{
+ 		maildrop.verbose_level=0;
+ 	}
+ 
+ 	n %= sizeof(varlist)/sizeof(varlist[0]);
+ 
+ Variable **v;
+ 
+ 	for (v= &varlist[n]; *v; v= &(*v)->next)
+ 		if ( (*v)->name == var )
+ 		{
+ 		Variable *vv= (*v);
+ 
+ 			(*v)= vv->next;
+ 			delete vv;
+ 			break;
+ 		}
+ 	return;
+ }
+ 
  void SetVar(const Buffer &var, const Buffer &value)
  {
  int varlen=var.Length();
***************
*** 34,55 ****
  	}
  
  	n %= sizeof(varlist)/sizeof(varlist[0]);
- 
- 	if (value.Length() == 0)	// Delete variable
- 	{
- 	Variable **v;
- 
- 		for (v= &varlist[n]; *v; v= &(*v)->next)
- 			if ( (*v)->name == var )
- 			{
- 			Variable *vv= (*v);
- 
- 				(*v)= vv->next;
- 				delete vv;
- 				break;
- 			}
- 		return;
- 	}
  
  Variable *v;
  
--- 65,70 ----
*** maildrop/varlist.h.save	Wed Jan 14 09:23:42 2004
--- maildrop/varlist.h	Wed Jan 14 09:24:05 2004
***************
*** 9,14 ****
--- 9,15 ----
  
  class Buffer;
  
+ void UnsetVar(const Buffer &);
  void SetVar(const Buffer &, const Buffer &);
  const Buffer *GetVar(const Buffer &);
  const char *GetVarStr(const Buffer &);

-- 
 Courier User
 [EMAIL PROTECTED]

Reply via email to