#!/usr/bin/perl
##
## Sort attributes in each entires in LDIF data
## Copyright (c) 2012 SATOH Fumiyasu @ OSS Technology Corp., Japan
##               <http://www.OSSTech.co.jp/>
##
## License: GNU General Public License version 2 or later
## Date: 2012-11-07, since 2012-10-19
##

use strict;
use warnings;
use MIME::Base64 qw(decode_base64);
use Encode qw(encode_utf8 decode_utf8);
use Unicode::Normalize qw(NFKC);

$/ = '';

while (defined(my $entry = <>)) {
  if ($entry !~ /^dn:/i) {
    print $entry;
    next;
  }

  chomp($entry);
  $entry =~ s/\n[ \t]//g;
  my @attrs = split(/\n/, $entry);
  my $dn = shift(@attrs);

  print $dn, "\n";

  map {
    print $_->[0], "\n";
  }
  sort {
    ## Compare normalized "attr: value", original "value"
    $a->[1] cmp $b->[1] || $a->[2] cmp $b->[2];
  }
  map {
    my $normalized = $_;
    my $value_raw = '';
    if (my ($name, $b64, $value) = $_ =~ /^(\w+):(:)?\s*(.*)$/) {
      $normalized = lc($name) . ': ';
      $value_raw = defined($b64) ? MIME::Base64::decode_base64($value) : $value;
      my $value_normalized = eval {
        ## Use $tmp because Encode::FB_CROAK may change the input string.
        ## See the Encode::FB_QUIET description in `perldoc Encode` for details.
        encode_utf8(NFKC(decode_utf8(my $tmp=$value_raw, Encode::FB_CROAK)));
      };
      $normalized .= defined($value_normalized) ? $value_normalized : $value;
    }
    ## Original "attr: value", normalized "attr: value", original "value"
    [$_, $normalized, $value_raw];
  }
  @attrs;

  print "\n";
}

exit(0);

