]> git.sur5r.net Git - openldap/commitdiff
ITS#5810 Add ldif-filter for LDIF test output, extending scripts/acfilter.sh
authorHallvard Furuseth <hallvard@openldap.org>
Sun, 29 Nov 2009 00:03:06 +0000 (00:03 +0000)
committerHallvard Furuseth <hallvard@openldap.org>
Sun, 29 Nov 2009 00:03:06 +0000 (00:03 +0000)
tests/progs/Makefile.in
tests/progs/ldif-filter.c [new file with mode: 0644]

index a3cb136898e01d9908e422a4e2b6de147b35b133..6e2d113556c2b49bb0d860d77d273c6e6ea44069 100644 (file)
 ## <http://www.OpenLDAP.org/license.html>.
 
 PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel slapd-modrdn \
-               slapd-modify slapd-bind
+               slapd-modify slapd-bind ldif-filter
 
 SRCS     = slapd-common.c \
                slapd-tester.c slapd-search.c slapd-read.c slapd-addel.c \
-               slapd-modrdn.c slapd-modify.c slapd-bind.c
+               slapd-modrdn.c slapd-modify.c slapd-bind.c ldif-filter.c
 
 LDAP_INCDIR= ../../include
 LDAP_LIBDIR= ../../libraries
@@ -54,3 +54,6 @@ slapd-modify: slapd-modify.o $(OBJS) $(XLIBS)
 slapd-bind: slapd-bind.o $(OBJS) $(XLIBS)
        $(LTLINK) -o $@ slapd-bind.o $(OBJS) $(LIBS)
 
+ldif-filter: ldif-filter.o $(XLIBS)
+       $(LTLINK) -o $@ ldif-filter.o $(LIBS)
+
diff --git a/tests/progs/ldif-filter.c b/tests/progs/ldif-filter.c
new file mode 100644 (file)
index 0000000..932b46e
--- /dev/null
@@ -0,0 +1,229 @@
+/* ldif-filter -- clean up LDIF testdata from stdin */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/ctype.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#define DEFAULT_SPECS "ndb=a,null=n"
+
+typedef struct { char   *val; size_t len, alloc; } String;
+typedef struct { String        *val; size_t len, alloc; } Strings;
+
+/* Flags and corresponding program options */
+enum { SORT_ATTRS = 1, SORT_ENTRIES = 2, NO_OUTPUT = 4, DUMMY_FLAG = 8 };
+static const char spec_options[] = "aen"; /* option index = log2(enum flag) */
+
+static const char *progname = "ldif-filter";
+static const String null_string = { NULL, 0, 0 };
+
+static void
+usage( void )
+{
+       fprintf( stderr, "\
+Usage: %s [backend] [spec[,spec]...]\n\
+Filter standard input by first <spec> matching '[<backend>]=[a][e][n]':\n\
+  - Remove LDIF comments.\n\
+  - 'a': Sort attributes in entries.\n\
+  - 'e': Sort any entries separated by just one empty line.\n\
+  - 'n': Output nothing.\n\
+<backend> defaults to the $BACKEND environment variable.\n\
+Use specs '%s' if no spec on the command line applies.\n",
+               progname, DEFAULT_SPECS );
+       exit( EXIT_FAILURE );
+}
+
+/* Return flags from "backend=flags" in spec; nonzero if backend found */
+static unsigned
+get_flags( const char *backend, const char *spec )
+{
+       size_t len = strlen( backend );
+       unsigned flags = DUMMY_FLAG;
+       const char *tmp;
+
+       while ( '=' != *(spec += strncmp( spec, backend, len ) ? 0 : len) ) {
+               if ( (spec = strchr( spec, ',' )) == NULL ) {
+                       return 0;
+               }
+               ++spec;
+       }
+       while ( *++spec && *spec != ',' ) {
+               if ( (tmp = strchr( spec_options, *spec )) == NULL ) {
+                       usage();
+               }
+               flags |= 1U << (tmp - spec_options);
+       }
+       return flags;
+}
+
+#define APPEND(s /* String or Strings */, data, count, isString) do { \
+       size_t slen = (s)->len, salloc = (s)->alloc, sz = sizeof *(s)->val; \
+       if ( salloc <= slen + (count) ) { \
+               (s)->alloc = salloc += salloc + ((count)|7) + 1; \
+               (s)->val   = xrealloc( (s)->val, sz * salloc ); \
+       } \
+       memcpy( (s)->val + slen, data, sz * ((count) + !!(isString)) ); \
+       (s)->len = slen + (count); \
+} while (0)
+
+static void *
+xrealloc( void *ptr, size_t len )
+{
+       if ( (ptr = realloc( ptr, len )) == NULL ) {
+               perror( progname );
+               exit( EXIT_FAILURE );
+       }
+       return ptr;
+}
+
+static int
+cmp( const void *s, const void *t )
+{
+       return strcmp( ((const String *) s)->val, ((const String *) t)->val );
+}
+
+static void
+sort_strings( Strings *ss, size_t offset )
+{
+       qsort( ss->val + offset, ss->len - offset, sizeof(*ss->val), cmp );
+}
+
+/* Build entry ss[n] from attrs ss[n...], and free the attrs */
+static void
+build_entry( Strings *ss, size_t n, unsigned flags, size_t new_len )
+{
+       String *vals = ss->val, *e = &vals[n];
+       size_t end = ss->len;
+       char *ptr;
+
+       if ( flags & SORT_ATTRS ) {
+               sort_strings( ss, n + 1 );
+       }
+       e->val = xrealloc( e->val, e->alloc = new_len + 1 );
+       ptr = e->val + e->len;
+       e->len = new_len;
+       ss->len = ++n;
+       for ( ; n < end; free( vals[n++].val )) {
+               ptr = strcpy( ptr, vals[n].val ) + vals[n].len;
+       }
+       assert( ptr == e->val + new_len );
+}
+
+/* Flush entries to stdout and free them */
+static void
+flush_entries( Strings *ss, const char *sep, unsigned flags )
+{
+       size_t i, end = ss->len;
+       const char *prefix = "";
+
+       if ( flags & SORT_ENTRIES ) {
+               sort_strings( ss, 0 );
+       }
+       for ( i = 0; i < end; i++, prefix = sep ) {
+               if ( printf( "%s%s", prefix, ss->val[i].val ) < 0 ) {
+                       perror( progname );
+                       exit( EXIT_FAILURE );
+               }
+               free( ss->val[i].val );
+       }
+       ss->len = 0;
+}
+
+static void
+filter_stdin( unsigned flags )
+{
+       char line[256];
+       Strings ss = { NULL, 0, 0 };    /* entries + attrs of partial entry */
+       size_t entries = 0, attrs_totlen = 0, line_len;
+       const char *entry_sep = "\n", *sep = "";
+       int comment = 0, eof = 0, eol, prev_eol = 1;    /* flags */
+       String *s;
+
+       /* LDIF = Entries ss[..entries-1] + sep + attrs ss[entries..] + line */
+       for ( ; !eof || ss.len || *sep; prev_eol = eol ) {
+               if ( eof || (eof = !fgets( line, sizeof(line), stdin ))) {
+                       strcpy( line, prev_eol ? "" : *sep ? sep : "\n" );
+               }
+               line_len = strlen( line );
+               eol = (line_len == 0 || line[line_len - 1] == '\n');
+
+               if ( *line == ' ' ) {           /* continuation line? */
+                       prev_eol = 0;
+               } else if ( prev_eol ) {        /* start of logical line? */
+                       comment = (*line == '#');
+               }
+               if ( comment || (flags & NO_OUTPUT) ) {
+                       continue;
+               }
+
+               /* Collect attrs for partial entry in ss[entries...] */
+               if ( !prev_eol && attrs_totlen != 0 ) {
+                       goto grow_attr;
+               } else if ( line_len > (*line == '\r' ? 2 : 1) ) {
+                       APPEND( &ss, &null_string, 1, 0 ); /* new attr */
+               grow_attr:
+                       s = &ss.val[ss.len - 1];
+                       APPEND( s, line, line_len, 1 ); /* strcat to attr */
+                       attrs_totlen += line_len;
+                       continue;
+               }
+
+               /* Empty line - consume sep+attrs or entries+sep */
+               if ( attrs_totlen != 0 ) {
+                       entry_sep = sep;
+                       if ( entries == 0 )
+                               fputs( sep, stdout );
+                       build_entry( &ss, entries++, flags, attrs_totlen );
+                       attrs_totlen = 0;
+               } else {
+                       flush_entries( &ss, entry_sep, flags );
+                       fputs( sep, stdout );
+                       entries = 0;
+               }
+               sep = "\r\n" + 2 - line_len;    /* sep = copy(line) */
+       }
+
+       free( ss.val );
+}
+
+int
+main( int argc, char **argv )
+{
+       const char *backend, *specs, *tmp;
+       unsigned flags;
+
+       if ( argc > 0 ) {
+               progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0];
+       }
+       specs = (argc > 1 && strchr( argv[argc-1], '=' )) ? argv[--argc] : "";
+       backend = (argc > 1 && isalnum( (unsigned char) *argv[argc-1] ))
+               ? argv[--argc] : (tmp = getenv( "BACKEND" )) ? tmp : "";
+       if ( argc > 1 ) {
+               usage();
+       }
+
+       flags = get_flags( backend, specs );
+       filter_stdin( flags ? flags : get_flags( backend, DEFAULT_SPECS ));
+       if ( fclose( stdout ) == EOF ) {
+               perror( progname );
+               return EXIT_FAILURE;
+       }
+
+       return EXIT_SUCCESS;
+}