diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
commit | 5ea77a75dd2d2158401331879f3c8f47940a732c (patch) | |
tree | d89dc06e9f4850a900f161e25f84e922c4f86cc8 /tests/progs/ldif-filter.c | |
parent | Initial commit. (diff) | |
download | openldap-5ea77a75dd2d2158401331879f3c8f47940a732c.tar.xz openldap-5ea77a75dd2d2158401331879f3c8f47940a732c.zip |
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | tests/progs/ldif-filter.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/tests/progs/ldif-filter.c b/tests/progs/ldif-filter.c new file mode 100644 index 0000000..355b716 --- /dev/null +++ b/tests/progs/ldif-filter.c @@ -0,0 +1,256 @@ +/* ldif-filter -- clean up LDIF testdata from stdin */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2009-2022 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> +#include <ac/unistd.h> +#ifdef _WIN32 +#include <fcntl.h> +#endif + +#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 [-b backend] [-s 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 *end, *tmp; + + for ( ;; spec = end + ( *end != '\0' )) { + if ( !*spec ) + return 0; + end = spec + strcspn( spec, "," ); + if ( !(tmp = memchr( spec, '=', end-spec ))) + break; + if ( tmp-spec == len && !memcmp( spec, backend, len )) { + spec = tmp+1; + break; + } + } + + for ( ; spec < end; 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 = getenv( "BACKEND" ), *specs = "", *tmp; + unsigned flags; + int i; + + if ( argc > 0 ) { + progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0]; + } + + while ( (i = getopt( argc, argv, "b:s:" )) != EOF ) { + switch ( i ) { + case 'b': + backend = optarg; + break; + case 's': + specs = optarg; + break; + default: + usage(); + } + } + if ( optind < argc ) { + usage(); + } + if ( backend == NULL ) { + backend = ""; + } + +#ifdef _WIN32 + _setmode(1, _O_BINARY); /* don't convert \n to \r\n on stdout */ +#endif + 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; +} |