non-ASCII output for ldapsearch [PATCH]
Emmanuel Dreyfus <[email protected]> Fri, 27 Feb 2026 15:59:55 +0000
| Newsgroups | gmane.network.openldap.devel |
|---|---|
| Message-ID | <[email protected]> |
--2tNte84QOFG9kpvm Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello It would be nice if ldapsearch could display non-ASCII strings in a configurable charset, instead of always encoding in base64. I understand the current behavior is a requirement for LDIF compliance, but I believe we can add a non-LDIF compliant output, if this is what the user requested. Attached is a quick patch that will let ldapsearch output in the charset specified by LDAP_CHARSET environment variable (inspiration from LESS_CHARSET for less(1)). That makes life much more confortable when handling non-english data. It uses iconv() if <iconv.h> is detected. This function has been in POSIX for a while: https://pubs.opengroup.org/onlinepubs/007908775/xsh/iconv.html Opinions? Thoughts? -- Emmanuel Dreyfus [email protected] --2tNte84QOFG9kpvm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename=ldapsearch_charset.patch diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c index 7ab29777e8..6ab5aa5db8 100644 --- a/clients/tools/ldapsearch.c +++ b/clients/tools/ldapsearch.c @@ -60,6 +60,9 @@ #ifdef HAVE_IO_H #include <io.h> #endif +#ifdef HAVE_ICONV_H +#include <iconv.h> +#endif #include <ldap.h> @@ -89,6 +92,8 @@ ldap_sort_entries LDAP_P(( LDAP *ld, LDAP_SORT_AD_CMP_PROC *cmp )); #endif +typedef int ( *tool_write_ldif_t ) ( int type, char *name, char *value, ber_len_t vallen ); + static int scope = LDAP_SCOPE_SUBTREE; static int deref = -1; static int attrsonly; @@ -1929,6 +1934,49 @@ done: return( rc2 ); } +static int +ldapsearch_write( int type, char *name, char *value, ber_len_t vallen ) +{ + static iconv_t cd = (iconv_t)-1; + char *dst_charset; + char dstbuf[4096]; + char *dst = dstbuf; + size_t dstleft = sizeof(dstbuf); + char *src = value; + size_t srcleft = vallen; + size_t inval; + + if ( type != LDIF_PUT_VALUE ) + return 0; + + dst_charset = getenv("LDAP_CHARSET"); + if (dst_charset == NULL) + goto fallback; + + if (strstr(name, ";binary") != NULL) + goto fallback; + + if (cd == (iconv_t)-1) { + cd = iconv_open(dst_charset, "utf8"); + if (cd == (iconv_t)-1) + goto fallback; + } + + if (iconv(cd, &src, &srcleft, &dst, &dstleft) == 0) { + dstbuf[sizeof(dstbuf) - dstleft] = '\0'; + printf("%s: %s\n", name, dstbuf); + } else if (errno == E2BIG) { + dstbuf[sizeof(dstbuf) - dstleft] = '\0'; + printf("%s: %s (...)\n", name, dstbuf); + } else { + goto fallback; + } + + return 0; +fallback: + return tool_write_ldif(type, name, value, vallen); +} + /* This is the proposed new way of doing things. * It is more efficient, but the API is non-standard. */ @@ -1946,14 +1994,18 @@ print_entry( struct berval bv, *bvals, **bvp = &bvals; LDAPControl **ctrls = NULL; FILE *tmpfp; + tool_write_ldif_t write_ldif = tool_write_ldif;; + + if (getenv("LDAP_CHARSET")) + write_ldif = ldapsearch_write; rc = ldap_get_dn_ber( ld, entry, &ber, &bv ); if ( ldif < 2 ) { ufn = ldap_dn2ufn( bv.bv_val ); - tool_write_ldif( LDIF_PUT_COMMENT, NULL, ufn, ufn ? strlen( ufn ) : 0 ); + write_ldif( LDIF_PUT_COMMENT, NULL, ufn, ufn ? strlen( ufn ) : 0 ); } - tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len ); + write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len ); rc = ldap_get_entry_controls( ld, entry, &ctrls ); if( rc != LDAP_SUCCESS ) { @@ -1971,7 +2023,7 @@ print_entry( if( ufn == NULL ) { ufn = ldap_dn2ufn( bv.bv_val ); } - tool_write_ldif( LDIF_PUT_VALUE, "ufn", ufn, ufn ? strlen( ufn ) : 0 ); + write_ldif( LDIF_PUT_VALUE, "ufn", ufn, ufn ? strlen( ufn ) : 0 ); } if( ufn != NULL ) ldap_memfree( ufn ); @@ -1985,7 +2037,7 @@ print_entry( if (bv.bv_val == NULL) break; if ( attrsonly ) { - tool_write_ldif( LDIF_PUT_NOVALUE, bv.bv_val, NULL, 0 ); + write_ldif( LDIF_PUT_NOVALUE, bv.bv_val, NULL, 0 ); } else if ( bvals ) { for ( i = 0; bvals[i].bv_val != NULL; i++ ) { @@ -2025,10 +2077,10 @@ print_entry( &tmpfname[strlen(tmpdir) + sizeof(LDAP_DIRSEP) - 1] ); urlize( url ); - tool_write_ldif( LDIF_PUT_URL, bv.bv_val, url, strlen( url )); + write_ldif( LDIF_PUT_URL, bv.bv_val, url, strlen( url )); } else { - tool_write_ldif( LDIF_PUT_VALUE, bv.bv_val, + write_ldif( LDIF_PUT_VALUE, bv.bv_val, bvals[ i ].bv_val, bvals[ i ].bv_len ); } } diff --git a/configure b/configure index 5dc5bf1dd5..035cfeafee 100755 --- a/configure +++ b/configure @@ -16320,6 +16320,12 @@ if test "x$ac_cv_header_io_h" = xyes then : printf "%s\n" "#define HAVE_IO_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "iconv.h" "ac_cv_header_iconv_h" "$ac_includes_default" +if test "x$ac_cv_header_iconv_h" = xyes +then : + printf "%s\n" "#define HAVE_ICONV_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "libutil.h" "ac_cv_header_libutil_h" "$ac_includes_default" if test "x$ac_cv_header_libutil_h" = xyes diff --git a/configure.ac b/configure.ac index bead4d2b9c..6830bd46a1 100644 --- a/configure.ac +++ b/configure.ac @@ -815,6 +815,7 @@ AC_CHECK_HEADERS( \ getopt.h \ grp.h \ io.h \ + iconv.h \ libutil.h \ limits.h \ locale.h \ diff --git a/include/portable.hin b/include/portable.hin index 45e5b6ee13..e4d649fd32 100644 --- a/include/portable.hin +++ b/include/portable.hin @@ -313,6 +313,9 @@ /* define if you have libsodium */ #undef HAVE_LIBSODIUM +/* Define to 1 if you have the <iconv.h> header file. */ +#undef HAVE_ICONV_H + /* Define to 1 if you have the <libutil.h> header file. */ #undef HAVE_LIBUTIL_H --2tNte84QOFG9kpvm--