Back to index

openldap  2.4.31
ldif-filter.c
Go to the documentation of this file.
00001 /* ldif-filter -- clean up LDIF testdata from stdin */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2009-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 #include <ac/ctype.h>
00021 #include <ac/stdlib.h>
00022 #include <ac/string.h>
00023 #include <ac/unistd.h>
00024 
00025 #define DEFAULT_SPECS "ndb=a,null=n"
00026 
00027 typedef struct { char   *val; size_t len, alloc; } String;
00028 typedef struct { String     *val; size_t len, alloc; } Strings;
00029 
00030 /* Flags and corresponding program options */
00031 enum { SORT_ATTRS = 1, SORT_ENTRIES = 2, NO_OUTPUT = 4, DUMMY_FLAG = 8 };
00032 static const char spec_options[] = "aen"; /* option index = log2(enum flag) */
00033 
00034 static const char *progname = "ldif-filter";
00035 static const String null_string = { NULL, 0, 0 };
00036 
00037 static void
00038 usage( void )
00039 {
00040        fprintf( stderr, "\
00041 Usage: %s [-b backend] [-s spec[,spec]...]\n\
00042 Filter standard input by first <spec> matching '[<backend>]=[a][e][n]':\n\
00043   - Remove LDIF comments.\n\
00044   - 'a': Sort attributes in entries.\n\
00045   - 'e': Sort any entries separated by just one empty line.\n\
00046   - 'n': Output nothing.\n\
00047 <backend> defaults to the $BACKEND environment variable.\n\
00048 Use specs '%s' if no spec on the command line applies.\n",
00049               progname, DEFAULT_SPECS );
00050        exit( EXIT_FAILURE );
00051 }
00052 
00053 /* Return flags from "backend=flags" in spec; nonzero if backend found */
00054 static unsigned
00055 get_flags( const char *backend, const char *spec )
00056 {
00057        size_t len = strlen( backend );
00058        unsigned flags = DUMMY_FLAG;
00059        const char *tmp;
00060 
00061        while ( '=' != *(spec += strncmp( spec, backend, len ) ? 0 : len) ) {
00062               if ( (spec = strchr( spec, ',' )) == NULL ) {
00063                      return 0;
00064               }
00065               ++spec;
00066        }
00067        while ( *++spec && *spec != ',' ) {
00068               if ( (tmp = strchr( spec_options, *spec )) == NULL ) {
00069                      usage();
00070               }
00071               flags |= 1U << (tmp - spec_options);
00072        }
00073        return flags;
00074 }
00075 
00076 #define APPEND(s /* String or Strings */, data, count, isString) do { \
00077        size_t slen = (s)->len, salloc = (s)->alloc, sz = sizeof *(s)->val; \
00078        if ( salloc <= slen + (count) ) { \
00079               (s)->alloc = salloc += salloc + ((count)|7) + 1; \
00080               (s)->val   = xrealloc( (s)->val, sz * salloc ); \
00081        } \
00082        memcpy( (s)->val + slen, data, sz * ((count) + !!(isString)) ); \
00083        (s)->len = slen + (count); \
00084 } while (0)
00085 
00086 static void *
00087 xrealloc( void *ptr, size_t len )
00088 {
00089        if ( (ptr = realloc( ptr, len )) == NULL ) {
00090               perror( progname );
00091               exit( EXIT_FAILURE );
00092        }
00093        return ptr;
00094 }
00095 
00096 static int
00097 cmp( const void *s, const void *t )
00098 {
00099        return strcmp( ((const String *) s)->val, ((const String *) t)->val );
00100 }
00101 
00102 static void
00103 sort_strings( Strings *ss, size_t offset )
00104 {
00105        qsort( ss->val + offset, ss->len - offset, sizeof(*ss->val), cmp );
00106 }
00107 
00108 /* Build entry ss[n] from attrs ss[n...], and free the attrs */
00109 static void
00110 build_entry( Strings *ss, size_t n, unsigned flags, size_t new_len )
00111 {
00112        String *vals = ss->val, *e = &vals[n];
00113        size_t end = ss->len;
00114        char *ptr;
00115 
00116        if ( flags & SORT_ATTRS ) {
00117               sort_strings( ss, n + 1 );
00118        }
00119        e->val = xrealloc( e->val, e->alloc = new_len + 1 );
00120        ptr = e->val + e->len;
00121        e->len = new_len;
00122        ss->len = ++n;
00123        for ( ; n < end; free( vals[n++].val )) {
00124               ptr = strcpy( ptr, vals[n].val ) + vals[n].len;
00125        }
00126        assert( ptr == e->val + new_len );
00127 }
00128 
00129 /* Flush entries to stdout and free them */
00130 static void
00131 flush_entries( Strings *ss, const char *sep, unsigned flags )
00132 {
00133        size_t i, end = ss->len;
00134        const char *prefix = "";
00135 
00136        if ( flags & SORT_ENTRIES ) {
00137               sort_strings( ss, 0 );
00138        }
00139        for ( i = 0; i < end; i++, prefix = sep ) {
00140               if ( printf( "%s%s", prefix, ss->val[i].val ) < 0 ) {
00141                      perror( progname );
00142                      exit( EXIT_FAILURE );
00143               }
00144               free( ss->val[i].val );
00145        }
00146        ss->len = 0;
00147 }
00148 
00149 static void
00150 filter_stdin( unsigned flags )
00151 {
00152        char line[256];
00153        Strings ss = { NULL, 0, 0 };       /* entries + attrs of partial entry */
00154        size_t entries = 0, attrs_totlen = 0, line_len;
00155        const char *entry_sep = "\n", *sep = "";
00156        int comment = 0, eof = 0, eol, prev_eol = 1;     /* flags */
00157        String *s;
00158 
00159        /* LDIF = Entries ss[..entries-1] + sep + attrs ss[entries..] + line */
00160        for ( ; !eof || ss.len || *sep; prev_eol = eol ) {
00161               if ( eof || (eof = !fgets( line, sizeof(line), stdin ))) {
00162                      strcpy( line, prev_eol ? "" : *sep ? sep : "\n" );
00163               }
00164               line_len = strlen( line );
00165               eol = (line_len == 0 || line[line_len - 1] == '\n');
00166 
00167               if ( *line == ' ' ) {              /* continuation line? */
00168                      prev_eol = 0;
00169               } else if ( prev_eol ) {    /* start of logical line? */
00170                      comment = (*line == '#');
00171               }
00172               if ( comment || (flags & NO_OUTPUT) ) {
00173                      continue;
00174               }
00175 
00176               /* Collect attrs for partial entry in ss[entries...] */
00177               if ( !prev_eol && attrs_totlen != 0 ) {
00178                      goto grow_attr;
00179               } else if ( line_len > (*line == '\r' ? 2 : 1) ) {
00180                      APPEND( &ss, &null_string, 1, 0 ); /* new attr */
00181               grow_attr:
00182                      s = &ss.val[ss.len - 1];
00183                      APPEND( s, line, line_len, 1 ); /* strcat to attr */
00184                      attrs_totlen += line_len;
00185                      continue;
00186               }
00187 
00188               /* Empty line - consume sep+attrs or entries+sep */
00189               if ( attrs_totlen != 0 ) {
00190                      entry_sep = sep;
00191                      if ( entries == 0 )
00192                             fputs( sep, stdout );
00193                      build_entry( &ss, entries++, flags, attrs_totlen );
00194                      attrs_totlen = 0;
00195               } else {
00196                      flush_entries( &ss, entry_sep, flags );
00197                      fputs( sep, stdout );
00198                      entries = 0;
00199               }
00200               sep = "\r\n" + 2 - line_len;       /* sep = copy(line) */
00201        }
00202 
00203        free( ss.val );
00204 }
00205 
00206 int
00207 main( int argc, char **argv )
00208 {
00209        const char *backend = getenv( "BACKEND" ), *specs = "", *tmp;
00210        unsigned flags;
00211        int i;
00212 
00213        if ( argc > 0 ) {
00214               progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0];
00215        }
00216 
00217        while ( (i = getopt( argc, argv, "b:s:" )) != EOF ) {
00218               switch ( i ) {
00219               case 'b':
00220                      backend = optarg;
00221                      break;
00222               case 's':
00223                      specs = optarg;
00224                      break;
00225               default:
00226                      usage();
00227               }
00228        }
00229        if ( optind < argc ) {
00230               usage();
00231        }
00232        if ( backend == NULL ) {
00233               backend = "";
00234        }
00235 
00236        flags = get_flags( backend, specs );
00237        filter_stdin( flags ? flags : get_flags( backend, DEFAULT_SPECS ));
00238        if ( fclose( stdout ) == EOF ) {
00239               perror( progname );
00240               return EXIT_FAILURE;
00241        }
00242 
00243        return EXIT_SUCCESS;
00244 }