1 /* $NetBSD: ldif-filter.c,v 1.1.1.1 2010/12/12 15:24:16 adam Exp $ */ 2 3 /* ldif-filter -- clean up LDIF testdata from stdin */ 4 /* OpenLDAP: pkg/ldap/tests/progs/ldif-filter.c,v 1.3.2.2 2010/04/19 19:14:31 quanah Exp */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2009-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19 #include "portable.h" 20 21 #include <stdio.h> 22 #include <ac/ctype.h> 23 #include <ac/stdlib.h> 24 #include <ac/string.h> 25 #include <ac/unistd.h> 26 27 #define DEFAULT_SPECS "ndb=a,null=n" 28 29 typedef struct { char *val; size_t len, alloc; } String; 30 typedef struct { String *val; size_t len, alloc; } Strings; 31 32 /* Flags and corresponding program options */ 33 enum { SORT_ATTRS = 1, SORT_ENTRIES = 2, NO_OUTPUT = 4, DUMMY_FLAG = 8 }; 34 static const char spec_options[] = "aen"; /* option index = log2(enum flag) */ 35 36 static const char *progname = "ldif-filter"; 37 static const String null_string = { NULL, 0, 0 }; 38 39 static void 40 usage( void ) 41 { 42 fprintf( stderr, "\ 43 Usage: %s [-b backend] [-s spec[,spec]...]\n\ 44 Filter standard input by first <spec> matching '[<backend>]=[a][e][n]':\n\ 45 - Remove LDIF comments.\n\ 46 - 'a': Sort attributes in entries.\n\ 47 - 'e': Sort any entries separated by just one empty line.\n\ 48 - 'n': Output nothing.\n\ 49 <backend> defaults to the $BACKEND environment variable.\n\ 50 Use specs '%s' if no spec on the command line applies.\n", 51 progname, DEFAULT_SPECS ); 52 exit( EXIT_FAILURE ); 53 } 54 55 /* Return flags from "backend=flags" in spec; nonzero if backend found */ 56 static unsigned 57 get_flags( const char *backend, const char *spec ) 58 { 59 size_t len = strlen( backend ); 60 unsigned flags = DUMMY_FLAG; 61 const char *tmp; 62 63 while ( '=' != *(spec += strncmp( spec, backend, len ) ? 0 : len) ) { 64 if ( (spec = strchr( spec, ',' )) == NULL ) { 65 return 0; 66 } 67 ++spec; 68 } 69 while ( *++spec && *spec != ',' ) { 70 if ( (tmp = strchr( spec_options, *spec )) == NULL ) { 71 usage(); 72 } 73 flags |= 1U << (tmp - spec_options); 74 } 75 return flags; 76 } 77 78 #define APPEND(s /* String or Strings */, data, count, isString) do { \ 79 size_t slen = (s)->len, salloc = (s)->alloc, sz = sizeof *(s)->val; \ 80 if ( salloc <= slen + (count) ) { \ 81 (s)->alloc = salloc += salloc + ((count)|7) + 1; \ 82 (s)->val = xrealloc( (s)->val, sz * salloc ); \ 83 } \ 84 memcpy( (s)->val + slen, data, sz * ((count) + !!(isString)) ); \ 85 (s)->len = slen + (count); \ 86 } while (0) 87 88 static void * 89 xrealloc( void *ptr, size_t len ) 90 { 91 if ( (ptr = realloc( ptr, len )) == NULL ) { 92 perror( progname ); 93 exit( EXIT_FAILURE ); 94 } 95 return ptr; 96 } 97 98 static int 99 cmp( const void *s, const void *t ) 100 { 101 return strcmp( ((const String *) s)->val, ((const String *) t)->val ); 102 } 103 104 static void 105 sort_strings( Strings *ss, size_t offset ) 106 { 107 qsort( ss->val + offset, ss->len - offset, sizeof(*ss->val), cmp ); 108 } 109 110 /* Build entry ss[n] from attrs ss[n...], and free the attrs */ 111 static void 112 build_entry( Strings *ss, size_t n, unsigned flags, size_t new_len ) 113 { 114 String *vals = ss->val, *e = &vals[n]; 115 size_t end = ss->len; 116 char *ptr; 117 118 if ( flags & SORT_ATTRS ) { 119 sort_strings( ss, n + 1 ); 120 } 121 e->val = xrealloc( e->val, e->alloc = new_len + 1 ); 122 ptr = e->val + e->len; 123 e->len = new_len; 124 ss->len = ++n; 125 for ( ; n < end; free( vals[n++].val )) { 126 ptr = strcpy( ptr, vals[n].val ) + vals[n].len; 127 } 128 assert( ptr == e->val + new_len ); 129 } 130 131 /* Flush entries to stdout and free them */ 132 static void 133 flush_entries( Strings *ss, const char *sep, unsigned flags ) 134 { 135 size_t i, end = ss->len; 136 const char *prefix = ""; 137 138 if ( flags & SORT_ENTRIES ) { 139 sort_strings( ss, 0 ); 140 } 141 for ( i = 0; i < end; i++, prefix = sep ) { 142 if ( printf( "%s%s", prefix, ss->val[i].val ) < 0 ) { 143 perror( progname ); 144 exit( EXIT_FAILURE ); 145 } 146 free( ss->val[i].val ); 147 } 148 ss->len = 0; 149 } 150 151 static void 152 filter_stdin( unsigned flags ) 153 { 154 char line[256]; 155 Strings ss = { NULL, 0, 0 }; /* entries + attrs of partial entry */ 156 size_t entries = 0, attrs_totlen = 0, line_len; 157 const char *entry_sep = "\n", *sep = ""; 158 int comment = 0, eof = 0, eol, prev_eol = 1; /* flags */ 159 String *s; 160 161 /* LDIF = Entries ss[..entries-1] + sep + attrs ss[entries..] + line */ 162 for ( ; !eof || ss.len || *sep; prev_eol = eol ) { 163 if ( eof || (eof = !fgets( line, sizeof(line), stdin ))) { 164 strcpy( line, prev_eol ? "" : *sep ? sep : "\n" ); 165 } 166 line_len = strlen( line ); 167 eol = (line_len == 0 || line[line_len - 1] == '\n'); 168 169 if ( *line == ' ' ) { /* continuation line? */ 170 prev_eol = 0; 171 } else if ( prev_eol ) { /* start of logical line? */ 172 comment = (*line == '#'); 173 } 174 if ( comment || (flags & NO_OUTPUT) ) { 175 continue; 176 } 177 178 /* Collect attrs for partial entry in ss[entries...] */ 179 if ( !prev_eol && attrs_totlen != 0 ) { 180 goto grow_attr; 181 } else if ( line_len > (*line == '\r' ? 2 : 1) ) { 182 APPEND( &ss, &null_string, 1, 0 ); /* new attr */ 183 grow_attr: 184 s = &ss.val[ss.len - 1]; 185 APPEND( s, line, line_len, 1 ); /* strcat to attr */ 186 attrs_totlen += line_len; 187 continue; 188 } 189 190 /* Empty line - consume sep+attrs or entries+sep */ 191 if ( attrs_totlen != 0 ) { 192 entry_sep = sep; 193 if ( entries == 0 ) 194 fputs( sep, stdout ); 195 build_entry( &ss, entries++, flags, attrs_totlen ); 196 attrs_totlen = 0; 197 } else { 198 flush_entries( &ss, entry_sep, flags ); 199 fputs( sep, stdout ); 200 entries = 0; 201 } 202 sep = "\r\n" + 2 - line_len; /* sep = copy(line) */ 203 } 204 205 free( ss.val ); 206 } 207 208 int 209 main( int argc, char **argv ) 210 { 211 const char *backend = getenv( "BACKEND" ), *specs = "", *tmp; 212 unsigned flags; 213 int i; 214 215 if ( argc > 0 ) { 216 progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0]; 217 } 218 219 while ( (i = getopt( argc, argv, "b:s:" )) != EOF ) { 220 switch ( i ) { 221 case 'b': 222 backend = optarg; 223 break; 224 case 's': 225 specs = optarg; 226 break; 227 default: 228 usage(); 229 } 230 } 231 if ( optind < argc ) { 232 usage(); 233 } 234 if ( backend == NULL ) { 235 backend = ""; 236 } 237 238 flags = get_flags( backend, specs ); 239 filter_stdin( flags ? flags : get_flags( backend, DEFAULT_SPECS )); 240 if ( fclose( stdout ) == EOF ) { 241 perror( progname ); 242 return EXIT_FAILURE; 243 } 244 245 return EXIT_SUCCESS; 246 } 247