This patch adds several routines to the LDAP library that can be used
to produce HTML source text using the LDAP display templates:

    ldap_entry2html_search
    ldap_entry2html
    ldap_vals2html

These are needed for the LDAP URL support for LibWWW and X-Mosaic.  You
should apply these patches from the top of the LDAP 3.1 distribution
using this command:

    patch -p -s < ldap-html.patch


Please report any problems to ldap-support@umich.edu.

-Mark Smith <mcs@umich.edu>


*** include/disptmpl.h-	Fri Dec 16 10:33:32 1994
--- include/disptmpl.h	Mon Jan 30 15:21:55 1995
***************
*** 86,92 ****
--- 86,99 ----
   */
  #define LDAP_DISP_OPT_DOSEARCHACTIONS	0x00000002
  
+ /*
+  * include additional info. relevant to "non leaf" entries only
+  * used by ldap_entry2html and ldap_entry2html_search to include "Browse"
+  * and "Move Up" HREFs
+  */
+ #define LDAP_DISP_OPT_NONLEAF		0x00000004
  
+ 
  /*
   * display template item options (may not apply to all types)
   * if this bit is set in ti_options, it applies.
***************
*** 232,237 ****
--- 239,247 ----
  int ldap_entry2text_search();
  int ldap_entry2text();
  int ldap_vals2text();
+ int ldap_entry2html_search();
+ int ldap_entry2html();
+ int ldap_vals2html();
  
  #else /* !NEEDPROTOS */
  
***************
*** 294,299 ****
--- 304,325 ----
  	writeptype writeproc, void *writeparm, char *eol, int rdncount,
  	unsigned long opts );
  
+ LIBIMPORT int
+ ldap_entry2html( LDAP *ld, char *buf, LDAPMessage *entry,
+ 	struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals,
+ 	writeptype writeproc, void *writeparm, char *eol, int rdncount,
+ 	unsigned long opts, char *urlprefix, char *base );
+ 
+ LIBIMPORT int
+ ldap_vals2html( LDAP *ld, char *buf, char **vals, char *label, int labelwidth,
+ 	unsigned long syntaxid, writeptype writeproc, void *writeparm,
+ 	char *eol, int rdncount, char *urlprefix );
+ 
+ LIBIMPORT int
+ ldap_entry2html_search( LDAP *ld, char *dn, char *base, LDAPMessage *entry,
+ 	struct ldap_disptmpl *tmpllist, char **defattrs, char ***defvals,
+ 	writeptype writeproc, void *writeparm, char *eol, int rdncount,
+ 	unsigned long opts, char *urlprefix );
  #endif /* !NEEDPROTOS */


*** libraries/libldap/tmplout.c-	Mon Dec 19 11:36:14 1994
--- libraries/libldap/tmplout.c	Tue Feb 21 09:59:34 1995
***************
*** 33,54 ****
  static int do_entry2text( LDAP *ld, char *buf, char *base, LDAPMessage *entry,
  	struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals,
  	writeptype writeproc, void *writeparm, char *eol, int rdncount,
! 	unsigned long opts );
  static int max_label_len( struct ldap_disptmpl *tmpl );
  static int output_label( char *buf, char *label, int width,
! 	writeptype writeproc, void *writeparm, char *eol );
  static int output_dn( char *buf, char *dn, int width, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol );
  static char *time2text( char *ldtimestr, int dateonly );
  static long gtime( struct tm *tm );
  static int searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry,
  	char *dn, struct ldap_tmplitem *tip, int labelwidth, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol );
  #else /* NEEDPROTOS */
  static int do_entry2text();
  static int max_label_len();
  static int output_label();
  static int output_dn();
  static char *time2text();
  static long gtime();
  static int searchaction();
--- 33,65 ----
  static int do_entry2text( LDAP *ld, char *buf, char *base, LDAPMessage *entry,
  	struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals,
  	writeptype writeproc, void *writeparm, char *eol, int rdncount,
! 	unsigned long opts, char *urlprefix );
! static int do_entry2text_search( LDAP *ld, char *dn, char *base,
! 	LDAPMessage *entry, struct ldap_disptmpl *tmpllist, char **defattrs,
! 	char ***defvals, writeptype writeproc, void *writeparm, char *eol,
! 	int rdncount, unsigned long opts, char *urlprefix );
! static int do_vals2text( LDAP *ld, char *buf, char **vals, char *label,
! 	int labelwidth, unsigned long syntaxid, writeptype writeproc,
! 	void *writeparm, char *eol, int rdncount, char *urlprefix );
  static int max_label_len( struct ldap_disptmpl *tmpl );
  static int output_label( char *buf, char *label, int width,
! 	writeptype writeproc, void *writeparm, char *eol, int html );
  static int output_dn( char *buf, char *dn, int width, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol, char *urlprefix );
! static int strcat_escaped( char *s1, char *s2 );
  static char *time2text( char *ldtimestr, int dateonly );
  static long gtime( struct tm *tm );
  static int searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry,
  	char *dn, struct ldap_tmplitem *tip, int labelwidth, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol, char *urlprefix );
  #else /* NEEDPROTOS */
  static int do_entry2text();
+ static int do_entry2text_search();
+ static int do_vals2text();
  static int max_label_len();
  static int output_label();
  static int output_dn();
+ static int strcat_escaped();
  static char *time2text();
  static long gtime();
  static int searchaction();
***************
*** 62,67 ****
--- 73,80 ----
  #define NONFATAL_LDAP_ERR( err )	( err == LDAP_SUCCESS || \
  	err == LDAP_TIMELIMIT_EXCEEDED || err == LDAP_SIZELIMIT_EXCEEDED )
  
+ #define DEF_LDAP_URL_PREFIX	"ldap:///"
+ 
   
  int
  ldap_entry2text(
***************
*** 78,97 ****
  	unsigned long		opts
  )
  {
-     int	err;
- 
      Debug( LDAP_DEBUG_TRACE, "ldap_entry2text\n", 0, 0, 0 );
  
!     if ( buf == NULL && ( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
! 	ld->ld_errno = LDAP_NO_MEMORY;
! 	return( -1 );
!     }
  
!     err = do_entry2text( ld, buf, NULL, entry, tmpl, defattrs, defvals,
! 		writeproc, writeparm, eol, rdncount, opts );
  
!     free( buf );
!     return( err );
  }
  
  
--- 91,130 ----
  	unsigned long		opts
  )
  {
      Debug( LDAP_DEBUG_TRACE, "ldap_entry2text\n", 0, 0, 0 );
  
!     return( do_entry2text( ld, buf, NULL, entry, tmpl, defattrs, defvals,
! 		writeproc, writeparm, eol, rdncount, opts, NULL ));
  
! }
  
! 
! 
! int
! ldap_entry2html(
! 	LDAP			*ld,
! 	char			*buf,		/* NULL for "use internal" */
! 	LDAPMessage		*entry,
! 	struct ldap_disptmpl	*tmpl,
! 	char			**defattrs,
! 	char			***defvals,
! 	writeptype		writeproc,
! 	void			*writeparm,
! 	char			*eol,
! 	int			rdncount,
! 	unsigned long		opts,
! 	char			*base,
! 	char			*urlprefix
! )
! {
!     Debug( LDAP_DEBUG_TRACE, "ldap_entry2html\n", 0, 0, 0 );
! 
!     if ( urlprefix == NULL ) {
! 	urlprefix = DEF_LDAP_URL_PREFIX;
!     }
! 
!     return( do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals,
! 		writeproc, writeparm, eol, rdncount, opts, urlprefix ));
  }
  
  
***************
*** 98,104 ****
  static int
  do_entry2text(
  	LDAP			*ld,
! 	char			*buf,
  	char			*base,		/* used for search actions */
  	LDAPMessage		*entry,
  	struct ldap_disptmpl	*tmpl,
--- 131,137 ----
  static int
  do_entry2text(
  	LDAP			*ld,
! 	char			*buf,		/* NULL for use-internal */
  	char			*base,		/* used for search actions */
  	LDAPMessage		*entry,
  	struct ldap_disptmpl	*tmpl,
***************
*** 108,117 ****
  	void			*writeparm,
  	char			*eol,
  	int			rdncount,
! 	unsigned long		opts
  )
  {
!     int				i, err, show, labelwidth;
      int				freebuf,  freevals;
      char			*dn, **vals;
      struct ldap_tmplitem	*rowp, *colp;
--- 141,151 ----
  	void			*writeparm,
  	char			*eol,
  	int			rdncount,
! 	unsigned long		opts,
! 	char			*urlprefix	/* if non-NULL, do HTML */
  )
  {
!     int				i, err, html, show, labelwidth;
      int				freebuf,  freevals;
      char			*dn, **vals;
      struct ldap_tmplitem	*rowp, *colp;
***************
*** 120,130 ****
  	return( -1 );
      }
  
-     (*writeproc)( writeparm, "\"", 1 );
-     output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "" );
-     sprintf( buf, "\"%s", eol );
-     (*writeproc)( writeparm, buf, strlen( buf ));
- 
      if ( buf == NULL ) {
  	if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
  	    ld->ld_errno = LDAP_NO_MEMORY;
--- 154,159 ----
***************
*** 136,141 ****
--- 165,233 ----
  	freebuf = 0;
      }
  
+     html = ( urlprefix != NULL );
+ 
+     if ( html ) {
+ 	/*
+ 	 * add HTML intro. and title
+ 	 */
+ 	sprintf( buf, "<HTML>%s<HEAD>%s<TITLE>%s%s - ", eol, eol, eol,
+ 		( tmpl == NULL ) ? "Entry" : tmpl->dt_name );
+ 	(*writeproc)( writeparm, buf, strlen( buf ));
+ 	output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL );
+ 	sprintf( buf, "%s</TITLE>%s</HEAD>%s<BODY>%s<H3>%s - ", eol, eol,
+ 		eol, eol, ( tmpl == NULL ) ? "Entry" : tmpl->dt_name );
+ 	(*writeproc)( writeparm, buf, strlen( buf ));
+ 	output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL );
+ 	sprintf( buf, "</H3>%s", eol );
+ 	(*writeproc)( writeparm, buf, strlen( buf ));
+ 
+ 	if (( opts & LDAP_DISP_OPT_NONLEAF ) != 0 &&
+ 		( vals = ldap_explode_dn( dn, 0 )) != NULL ) {
+ 	    char	*untagged;
+ 
+ 	    /*
+ 	     * add "Move Up" link
+ 	     */
+ 	    sprintf( buf, "<A HREF=\"%s", urlprefix );
+ 	    for ( i = 1; vals[ i ] != NULL; ++i ) {
+ 		if ( i > 1 ) {
+ 		     strcat_escaped( buf, ", " );
+ 		}
+ 		strcat_escaped( buf, vals[ i ] );
+ 	    }
+ 	    if ( vals[ 1 ] != NULL ) {
+ 		untagged = strchr( vals[ 1 ], '=' );
+ 	    } else {
+ 		untagged = "=The World";
+ 	    }
+ 	    sprintf( buf + strlen( buf ),
+ 		    "%s\">Move Up To <EM>%s</EM></A>%s<BR>",
+ 		    ( vals[ 1 ] == NULL ) ? "??one" : "",
+ 		    ( untagged != NULL ) ? untagged + 1 : vals[ 1 ], eol, eol );
+ 	    (*writeproc)( writeparm, buf, strlen( buf ));
+ 
+ 	    /*
+ 	     * add "Browse" link
+ 	     */
+ 	    untagged = strchr( vals[ 0 ], '=' );
+ 	    sprintf( buf, "<A HREF=\"%s", urlprefix );
+ 	    strcat_escaped( buf, dn );
+ 	    sprintf( buf + strlen( buf ), "??one?(!(objectClass=dsa))\">Browse Below <EM>%s</EM></A>%s%s",
+ 		    ( untagged != NULL ) ? untagged + 1 : vals[ 0 ], eol, eol );
+ 	    (*writeproc)( writeparm, buf, strlen( buf ));
+ 
+ 	    ldap_value_free( vals );
+ 	}
+ 
+ 	(*writeproc)( writeparm, "<HR>", 4 );	/* horizontal rule */
+     } else {
+ 	(*writeproc)( writeparm, "\"", 1 );
+ 	output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL );
+ 	sprintf( buf, "\"%s", eol );
+ 	(*writeproc)( writeparm, buf, strlen( buf ));
+     }
+ 
      if ( tmpl != NULL && ( opts & LDAP_DISP_OPT_AUTOLABELWIDTH ) != 0 ) {
  	labelwidth = max_label_len( tmpl ) + 3;
      } else {
***************
*** 168,176 ****
  		freevals = 1;
  	    }
  
! 	    err = ldap_vals2text( ld, buf, vals, attr, labelwidth,
  		    LDAP_SYN_CASEIGNORESTR, writeproc, writeparm, eol, 
! 		    rdncount );
  	    if ( freevals ) {
  		ldap_value_free( vals );
  	    }
--- 260,272 ----
  		freevals = 1;
  	    }
  
! 	    if ( islower( *attr )) {	/* cosmetic -- upcase attr. name */
! 		*attr = toupper( *attr );
! 	    }
! 
! 	    err = do_vals2text( ld, buf, vals, attr, labelwidth,
  		    LDAP_SYN_CASEIGNORESTR, writeproc, writeparm, eol, 
! 		    rdncount, urlprefix );
  	    if ( freevals ) {
  		ldap_value_free( vals );
  	    }
***************
*** 211,217 ****
  		}
  
  		/*
! 		 * don't bother even calling ldap_vals2text() if no values
  		 * or boolean with value false and "hide if false" option set
  		 */
  		show = ( vals != NULL && vals[ 0 ] != NULL );
--- 307,313 ----
  		}
  
  		/*
! 		 * don't bother even calling do_vals2text() if no values
  		 * or boolean with value false and "hide if false" option set
  		 */
  		show = ( vals != NULL && vals[ 0 ] != NULL );
***************
*** 228,234 ****
  				toupper( vals[ 0 ][ 0 ] ) == 'T' )) {
  			    err = searchaction( ld, buf, base, entry, dn, colp,
  				    labelwidth, rdncount, writeproc,
! 				    writeparm, eol );
  			}
  		    }
  		    show = 0;
--- 324,330 ----
  				toupper( vals[ 0 ][ 0 ] ) == 'T' )) {
  			    err = searchaction( ld, buf, base, entry, dn, colp,
  				    labelwidth, rdncount, writeproc,
! 				    writeparm, eol, urlprefix );
  			}
  		    }
  		    show = 0;
***************
*** 235,243 ****
  		}
  
  		if ( show ) {
! 		    err = ldap_vals2text( ld, buf, vals, colp->ti_label,
  			labelwidth, colp->ti_syntaxid, writeproc, writeparm,
! 			eol, rdncount );
  		}
  
  		if ( freevals ) {
--- 331,339 ----
  		}
  
  		if ( show ) {
! 		    err = do_vals2text( ld, buf, vals, colp->ti_label,
  			labelwidth, colp->ti_syntaxid, writeproc, writeparm,
! 			eol, rdncount, urlprefix );
  		}
  
  		if ( freevals ) {
***************
*** 247,252 ****
--- 343,353 ----
  	}
      }
  
+     if ( html ) {
+ 	sprintf( buf, "</BODY>%s</HTML>%s", eol, eol );
+ 	(*writeproc)( writeparm, buf, strlen( buf ));
+     }
+ 
      free( dn );
      if ( freebuf ) {
  	free( buf );
***************
*** 272,290 ****
  	unsigned long		opts
  )
  {
!     int				err, freedn, freetmpls;
      char			*buf, **fetchattrs, **vals;
      LDAPMessage			*ldmp;
      struct ldap_disptmpl	*tmpl;
      struct timeval		timeout;
  
-     Debug( LDAP_DEBUG_TRACE, "ldap_entry2text_search\n", 0, 0, 0 );
- 
      if ( dn == NULL && entry == NULLMSG ) {
  	ld->ld_errno = LDAP_PARAM_ERROR;
  	return( -1 );
      }
  
      timeout.tv_sec = SEARCH_TIMEOUT_SECS;
      timeout.tv_usec = 0;
  
--- 373,440 ----
  	unsigned long		opts
  )
  {
!     Debug( LDAP_DEBUG_TRACE, "ldap_entry2text_search\n", 0, 0, 0 );
! 
!     return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs,
! 	    defvals, writeproc, writeparm, eol, rdncount, opts, NULL ));
! }
! 
! 
! 
! int
! ldap_entry2html_search(
! 	LDAP			*ld,
! 	char			*dn,		/* if NULL, use entry */
! 	char			*base,		/* if NULL, no search actions */
! 	LDAPMessage		*entry, 	/* if NULL, use dn */
! 	struct ldap_disptmpl*	tmpllist,	/* if NULL, load default file */
! 	char			**defattrs,
! 	char			***defvals,
! 	writeptype		writeproc,
! 	void			*writeparm,
! 	char			*eol,
! 	int			rdncount,	/* if 0, display full DN */
! 	unsigned long		opts,
! 	char			*urlprefix
! )
! {
!     Debug( LDAP_DEBUG_TRACE, "ldap_entry2html_search\n", 0, 0, 0 );
! 
!     return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs,
! 	    defvals, writeproc, writeparm, eol, rdncount, opts, urlprefix ));
! }
! 
! 
! static int
! do_entry2text_search(
! 	LDAP			*ld,
! 	char			*dn,		/* if NULL, use entry */
! 	char			*base,		/* if NULL, no search actions */
! 	LDAPMessage		*entry, 	/* if NULL, use dn */
! 	struct ldap_disptmpl*	tmpllist,	/* if NULL, load default file */
! 	char			**defattrs,
! 	char			***defvals,
! 	writeptype		writeproc,
! 	void			*writeparm,
! 	char			*eol,
! 	int			rdncount,	/* if 0, display full DN */
! 	unsigned long		opts,
! 	char			*urlprefix
! )
! {
!     int				err, freedn, freetmpls, html;
      char			*buf, **fetchattrs, **vals;
      LDAPMessage			*ldmp;
      struct ldap_disptmpl	*tmpl;
      struct timeval		timeout;
  
      if ( dn == NULL && entry == NULLMSG ) {
  	ld->ld_errno = LDAP_PARAM_ERROR;
  	return( -1 );
      }
  
+     html = ( urlprefix != NULL );
+ 
      timeout.tv_sec = SEARCH_TIMEOUT_SECS;
      timeout.tv_usec = 0;
  
***************
*** 298,305 ****
  
      if ( tmpllist == NULL ) {
  	if (( err = ldap_init_templates( TEMPLATEFILE, &tmpllist )) != 0 ) {
! 	    sprintf( buf, "Unable to read template file %s (error %d)%s",
! 		    TEMPLATEFILE, err, eol );
  	    (*writeproc)( writeparm, buf, strlen( buf ));
  	}
  	freetmpls = 1;
--- 448,456 ----
  
      if ( tmpllist == NULL ) {
  	if (( err = ldap_init_templates( TEMPLATEFILE, &tmpllist )) != 0 ) {
! 	    sprintf( buf, "%sUnable to read template file %s (error %d)%s%s",
! 		    html ? "<!-- " : "", TEMPLATEFILE, err,
! 		    html ? "-->" : "", eol );
  	    (*writeproc)( writeparm, buf, strlen( buf ));
  	}
  	freetmpls = 1;
***************
*** 385,391 ****
      }
  
      err = do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals,
! 	    writeproc, writeparm, eol, rdncount, opts );
  
      free( buf );
      if ( freetmpls ) {
--- 536,542 ----
      }
  
      err = do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals,
! 	    writeproc, writeparm, eol, rdncount, opts, urlprefix );
  
      free( buf );
      if ( freetmpls ) {
***************
*** 410,437 ****
  	int			rdncount
  )
  {
!     int		i, freebuf;
      char	*p, *s, *outval;
  
-     Debug( LDAP_DEBUG_TRACE, "ldap_vals2text\n", 0, 0, 0 );
  
      if ( vals == NULL ) {
  	return( 0 );
      }
  
      switch( LDAP_GET_SYN_TYPE( syntaxid )) {
      case LDAP_SYN_TYPE_TEXT:
      case LDAP_SYN_TYPE_BOOLEAN:
! 	break;		/* we only bother with these two types */
      default:
  	return( 0 );
      }
  
!     if ( labelwidth == 0 ) {
  	labelwidth = DEF_LABEL_WIDTH;
      }
  
- 
      if ( buf == NULL ) {
  	if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
  	    ld->ld_errno = LDAP_NO_MEMORY;
--- 561,636 ----
  	int			rdncount
  )
  {
!     Debug( LDAP_DEBUG_TRACE, "ldap_vals2text\n", 0, 0, 0 );
! 
!     return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid,
! 		writeproc, writeparm, eol, rdncount, NULL ));
! }
! 
! 
! int
! ldap_vals2html(
! 	LDAP			*ld,
! 	char			*buf,		/* NULL for "use internal" */
! 	char			**vals,
! 	char			*label,
! 	int			labelwidth,	/* 0 means use default */
! 	unsigned long		syntaxid,
! 	writeptype		writeproc,
! 	void			*writeparm,
! 	char			*eol,
! 	int			rdncount,
! 	char			*urlprefix
! )
! {
!     Debug( LDAP_DEBUG_TRACE, "ldap_vals2html\n", 0, 0, 0 );
! 
!     if ( urlprefix == NULL ) {
! 	urlprefix = DEF_LDAP_URL_PREFIX;
!     }
! 
!     return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid,
! 		writeproc, writeparm, eol, rdncount, urlprefix ));
! }
! 
! 
! static int
! do_vals2text(
! 	LDAP			*ld,
! 	char			*buf,		/* NULL for "use internal" */
! 	char			**vals,
! 	char			*label,
! 	int			labelwidth,	/* 0 means use default */
! 	unsigned long		syntaxid,
! 	writeptype		writeproc,
! 	void			*writeparm,
! 	char			*eol,
! 	int			rdncount,
! 	char			*urlprefix
! )
! {
!     int		i, html, writeoutval, freebuf, notascii;
      char	*p, *s, *outval;
  
  
      if ( vals == NULL ) {
  	return( 0 );
      }
  
+     html = ( urlprefix != NULL );
+ 
      switch( LDAP_GET_SYN_TYPE( syntaxid )) {
      case LDAP_SYN_TYPE_TEXT:
      case LDAP_SYN_TYPE_BOOLEAN:
! 	break;		/* we only bother with these two types... */
      default:
  	return( 0 );
      }
  
!     if ( labelwidth == 0 || labelwidth < 0 ) {
  	labelwidth = DEF_LABEL_WIDTH;
      }
  
      if ( buf == NULL ) {
  	if (( buf = malloc( LDAP_DTMPL_BUFSIZ )) == NULL ) {
  	    ld->ld_errno = LDAP_NO_MEMORY;
***************
*** 442,448 ****
  	freebuf = 0;
      }
  
!     output_label( buf, label, labelwidth, writeproc, writeparm, eol );
  
      for ( i = 0; vals[ i ] != NULL; ++i ) {
  	for ( p = vals[ i ]; *p != '\0'; ++p ) {
--- 641,647 ----
  	freebuf = 0;
      }
  
!     output_label( buf, label, labelwidth, writeproc, writeparm, eol, html );
  
      for ( i = 0; vals[ i ] != NULL; ++i ) {
  	for ( p = vals[ i ]; *p != '\0'; ++p ) {
***************
*** 450,473 ****
  		break;
  	    }
  	}
! 	outval = ( *p == '\0' ) ? vals[ i ] : "(not ASCII)";
  
  	switch( syntaxid ) {
  	case LDAP_SYN_CASEIGNORESTR:
  	case LDAP_SYN_RFC822ADDR:
! 	    sprintf( buf, "%-*s%s%s", labelwidth, " ", outval, eol );
! 	    (*writeproc)( writeparm, buf, strlen( buf ));
  	    break;
  
  	case LDAP_SYN_DN:	/* for now */
  	    output_dn( buf, outval, labelwidth, rdncount, writeproc,
! 		    writeparm, eol );
  	    break;
  
  	case LDAP_SYN_MULTILINESTR:
! 	    if ( i > 0 ) {
  		output_label( buf, label, labelwidth, writeproc,
! 			writeparm, eol );
  	    }
  
  	    p = s = outval;
--- 649,687 ----
  		break;
  	    }
  	}
! 	notascii = ( *p != '\0' );
! 	outval = notascii ? "(unable to display non-ASCII text value)"
! 		: vals[ i ];
  
+ 	writeoutval = 0;	/* if non-zero, write outval after switch */
+ 
  	switch( syntaxid ) {
  	case LDAP_SYN_CASEIGNORESTR:
+ 	    ++writeoutval;
+ 	    break;
+ 
  	case LDAP_SYN_RFC822ADDR:
! 	    if ( html ) {
! 		char	*esc_begin, *esc_end;
! 
! 		strcpy( buf, "<DD><A HREF=\"mailto:" );
! 		strcat_escaped( buf, outval );
! 		sprintf( buf + strlen( buf ), "\">%s</A><BR>%s", outval, eol );
! 		(*writeproc)( writeparm, buf, strlen( buf ));
! 	    } else {
! 		++writeoutval;
! 	    }
  	    break;
  
  	case LDAP_SYN_DN:	/* for now */
  	    output_dn( buf, outval, labelwidth, rdncount, writeproc,
! 		    writeparm, eol, urlprefix );
  	    break;
  
  	case LDAP_SYN_MULTILINESTR:
! 	    if ( i > 0 && !html ) {
  		output_label( buf, label, labelwidth, writeproc,
! 			writeparm, eol, html );
  	    }
  
  	    p = s = outval;
***************
*** 476,511 ****
  		while ( isspace( *s )) {
  		    ++s;
  		}
! 		sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol );
  		(*writeproc)( writeparm, buf, strlen( buf ));
  		p = s;
  	    }
! 	    sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol );
! 	    (*writeproc)( writeparm, buf, strlen( buf ));
  	    break;
  
  	case LDAP_SYN_BOOLEAN:
! 	    sprintf( buf, "%-*s%s%s", labelwidth, " ",
! 		    toupper( outval[ 0 ] ) == 'T' ? "TRUE"
! 		    : "FALSE", eol );
! 	    (*writeproc)( writeparm, buf, strlen( buf ));
  	    break;
  
  	case LDAP_SYN_TIME:
  	case LDAP_SYN_DATE:
! 	    sprintf( buf, "%-*s%s%s", labelwidth, " ",
! 		    time2text( outval, syntaxid == LDAP_SYN_DATE ), eol);
! 	    (*writeproc)( writeparm, buf, strlen( buf ));
  	    break;
  
  	case LDAP_SYN_LABELEDURL:
! 	    if (( p = strchr( outval, '$' )) != NULL ) {
  		*p++ = '\0';
  		while ( isspace( *p )) {
  		    ++p;
  		}
  		s = outval;
! 	    } else if (( s = strchr( outval, ' ' )) != NULL ) {
  		*s++ = '\0';
  		while ( isspace( *s )) {
  		    ++s;
--- 690,726 ----
  		while ( isspace( *s )) {
  		    ++s;
  		}
! 		if ( html ) {
! 		    sprintf( buf, "<DD>%s<BR>%s", p, eol );
! 		} else {
! 		    sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol );
! 		}
  		(*writeproc)( writeparm, buf, strlen( buf ));
  		p = s;
  	    }
! 	    outval = p;
! 	    ++writeoutval;
  	    break;
  
  	case LDAP_SYN_BOOLEAN:
! 	    outval = toupper( outval[ 0 ] ) == 'T' ? "TRUE" : "FALSE";
! 	    ++writeoutval;
  	    break;
  
  	case LDAP_SYN_TIME:
  	case LDAP_SYN_DATE:
! 	    outval = time2text( outval, syntaxid == LDAP_SYN_DATE );
! 	    ++writeoutval;
  	    break;
  
  	case LDAP_SYN_LABELEDURL:
! 	    if ( !notascii && ( p = strchr( outval, '$' )) != NULL ) {
  		*p++ = '\0';
  		while ( isspace( *p )) {
  		    ++p;
  		}
  		s = outval;
! 	    } else if ( !notascii && ( s = strchr( outval, ' ' )) != NULL ) {
  		*s++ = '\0';
  		while ( isspace( *s )) {
  		    ++s;
***************
*** 516,522 ****
  		p = outval;
  	    }
  
! 	    if ( s != NULL ) {	/* has label */
  		sprintf( buf, "%-*s%s%s%-*s%s%s", labelwidth, " ",
  		    s, eol, labelwidth + 2, " ",p , eol );
  	    }
--- 731,742 ----
  		p = outval;
  	    }
  
! 	    /*
! 	     * at this point `s' points to the label & `p' to the URL
! 	     */
! 	    if ( html ) {
! 		sprintf( buf, "<DD><A HREF=\"%s\">%s</A><BR>%s", p, s, eol );
! 	    } else {
  		sprintf( buf, "%-*s%s%s%-*s%s%s", labelwidth, " ",
  		    s, eol, labelwidth + 2, " ",p , eol );
  	    }
***************
*** 528,533 ****
--- 748,762 ----
  		    syntaxid, eol );
  	    (*writeproc)( writeparm, buf, strlen( buf ));
  	}
+ 
+ 	if ( writeoutval ) {
+ 	    if ( html ) {
+ 		sprintf( buf, "<DD>%s<BR>%s", outval, eol );
+ 	    } else {
+ 		sprintf( buf, "%-*s%s%s", labelwidth, " ", outval, eol );
+ 	    }
+ 	    (*writeproc)( writeparm, buf, strlen( buf ));
+ 	}
      }
  
      if ( freebuf ) {
***************
*** 562,580 ****
  
  static int
  output_label( char *buf, char *label, int width, writeptype writeproc,
! 	void *writeparm, char *eol )
  {
      char	*p;
  
!     sprintf( buf, " %s:", label );
!     p = buf + strlen( buf );
  
!     while ( p - buf < width ) {
! 	*p++ = ' ';
      }
  
-     *p = '\0';
-     strcat( buf, eol );
      return ((*writeproc)( writeparm, buf, strlen( buf )));
  }
  
--- 791,814 ----
  
  static int
  output_label( char *buf, char *label, int width, writeptype writeproc,
! 	void *writeparm, char *eol, int html )
  {
      char	*p;
  
!     if ( html ) {
! 	sprintf( buf, "<DT><B>%s</B>", label );
!     } else {
! 	sprintf( buf, " %s:", label );
! 	p = buf + strlen( buf );
  
! 	while ( p - buf < width ) {
! 	    *p++ = ' ';
! 	}
! 
! 	*p = '\0';
! 	strcat( buf, eol );
      }
  
      return ((*writeproc)( writeparm, buf, strlen( buf )));
  }
  
***************
*** 581,587 ****
  
  static int
  output_dn( char *buf, char *dn, int width, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol )
  {
      char	**dnrdns;
      int		i;
--- 815,821 ----
  
  static int
  output_dn( char *buf, char *dn, int width, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
  {
      char	**dnrdns;
      int		i;
***************
*** 590,596 ****
  	return( -1 );
      }
  
!     if ( width > 0 ) {
  	sprintf( buf, "%-*s", width, " " );
      } else {
  	*buf = '\0';
--- 824,834 ----
  	return( -1 );
      }
  
!     if ( urlprefix != NULL ) {
! 	sprintf( buf, "<DD><A HREF=\"%s", urlprefix );
! 	strcat_escaped( buf, dn );
! 	strcat( buf, "\">" );
!     } else if ( width > 0 ) {
  	sprintf( buf, "%-*s", width, " " );
      } else {
  	*buf = '\0';
***************
*** 603,608 ****
--- 841,850 ----
  	strcat( buf, dnrdns[ i ] );
      }
  
+     if ( urlprefix != NULL ) {
+ 	strcat( buf, "</A><BR>" );
+     }
+ 
      ldap_value_free( dnrdns );
  
      strcat( buf, eol );
***************
*** 611,616 ****
--- 853,885 ----
  }
  
  
+ 
+ #define HREF_CHAR_ACCEPTABLE( c )	(( c >= '-' && c <= '9' ) ||	\
+ 					 ( c >= '@' && c <= 'Z' ) ||	\
+ 					 ( c == '_' ) ||		\
+ 					 ( c >= 'a' && c <= 'z' ))
+ 
+ static int
+ strcat_escaped( char *s1, char *s2 )
+ {
+     char	*p, *q;
+     char	hexdig[] = "0123456789ABCDEF";
+ 
+     p = s1 + strlen( s1 );
+     for ( q = s2; *q != '\0'; ++q ) {
+ 	if ( HREF_CHAR_ACCEPTABLE( *q )) {
+ 	    *p++ = *q;
+ 	} else {
+ 	    *p++ = '%';
+ 	    *p++ = hexdig[ *q >> 4 ];
+ 	    *p++ = hexdig[ *q & 0x0F ];
+ 	}
+     }
+ 
+     *p = '\0';
+ }
+ 
+ 
  #define GET2BYTENUM( p )	(( *p - '0' ) * 10 + ( *(p+1) - '0' ))
  
  static char *
***************
*** 718,732 ****
  static int
  searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn,
  	struct ldap_tmplitem *tip, int labelwidth, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol )
  {
!     int			err, lderr, i, count;
      char		**vals, **members;
      char		*value, *filtpattern, *attr, *selectattr, *selectname;
!     char		*retattrs[2];
      LDAPMessage		*ldmp;
      struct timeval	timeout;
  
      for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) {
  	;
      }
--- 987,1003 ----
  static int
  searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn,
  	struct ldap_tmplitem *tip, int labelwidth, int rdncount,
! 	writeptype writeproc, void *writeparm, char *eol, char *urlprefix )
  {
!     int			err, lderr, i, count, html;
      char		**vals, **members;
      char		*value, *filtpattern, *attr, *selectattr, *selectname;
!     char		*retattrs[2], filter[ 256 ];
      LDAPMessage		*ldmp;
      struct timeval	timeout;
  
+     html = ( urlprefix != NULL );
+ 
      for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) {
  	;
      }
***************
*** 750,769 ****
  	value = NULL;
      }
  
!     ldap_build_filter( buf, LDAP_DTMPL_BUFSIZ, filtpattern, NULL, NULL, NULL,
  	    value, NULL );
  
      timeout.tv_sec = SEARCH_TIMEOUT_SECS;
      timeout.tv_usec = 0;
  
- 
  #ifdef CLDAP
      if ( LDAP_IS_CLDAP( ld ))
! 	lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, buf, retattrs,
  		0, &ldmp, NULL );
      else
  #endif /* CLDAP */
! 	lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, buf, retattrs,
  		0, &timeout, &ldmp );
  
      if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) {
--- 1021,1056 ----
  	value = NULL;
      }
  
!     ldap_build_filter( filter, sizeof( filter ), filtpattern, NULL, NULL, NULL,
  	    value, NULL );
  
+     if ( html ) {
+ 	/*
+ 	 * if we are generating HTML, we add an HREF link that embodies this
+ 	 * search action as an LDAP URL, instead of actually doing the search
+ 	 * now.
+ 	 */
+ 	sprintf( buf, "<DT><A HREF=\"%s", urlprefix );
+ 	if ( base != NULL ) {
+ 	    strcat_escaped( buf, base );
+ 	}
+ 	strcat( buf, "??sub?" );
+ 	strcat_escaped( buf, filter );
+ 	sprintf( buf + strlen( buf ), "\"><B>%s</B></A><DD><BR>%s",
+ 		tip->ti_label, eol );
+ 	return ((*writeproc)( writeparm, buf, strlen( buf )));
+     }
+ 
      timeout.tv_sec = SEARCH_TIMEOUT_SECS;
      timeout.tv_usec = 0;
  
  #ifdef CLDAP
      if ( LDAP_IS_CLDAP( ld ))
! 	lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
  		0, &ldmp, NULL );
      else
  #endif /* CLDAP */
! 	lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs,
  		0, &timeout, &ldmp );
  
      if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) {
***************
*** 781,788 ****
  
  		ldap_sort_values( ld, members, ldap_sort_strcasecmp );
  
! 		err = ldap_vals2text( ld, NULL, members, tip->ti_label, 0,
! 			LDAP_SYN_DN, writeproc, writeparm, eol, rdncount );
  
  		ldap_value_free( members );
  	    }
--- 1068,1076 ----
  
  		ldap_sort_values( ld, members, ldap_sort_strcasecmp );
  
! 		err = do_vals2text( ld, NULL, members, tip->ti_label,
! 			html ? -1 : 0, LDAP_SYN_DN, writeproc, writeparm,
! 			eol, rdncount, urlprefix );
  
  		ldap_value_free( members );
  	    }
