//
//  main.m
//  mulle-plist-fix
//
//  Created by Nat! on 11.02.13.
//  Copyright (c) 2013 Nat!. All rights reserved.
//

#import <Foundation/Foundation.h>
#include <ctype.h>


/* this is really very, very old cold, that has been resurrected */

#define TRY_VALIANTLY_TO_CONVERT  0



static inline int   iswhite( int c)
{
   return( c == ' ' || c == '\n' || c == '\r' || c == '\t');
}


static id  _parse_object( char **s, NSUInteger *len);

static int   skip_white( char **s, NSUInteger *len)
{
   while( *len)
   {
      if( ! iswhite( **s))
         return( 0);
      ++*s;
      --*len;
   }
   return( -1);
}

/*
 static NSString  *slurp_identifier( char **s, unsigned int *len)
 {
 int    c;
 char   *memo;
 
 memo = *s;
 while( *len)
 {
 c = **s;
 if( ! (isalnum( c) || c == '_'))
 break;
 ++*s;
 --*len;
 }
 return( [NSString stringWithCString:memo
 length:*s - memo]);
 }
 */

static id   _parse_value( char **s, NSUInteger *len)
{
   int        c;
   char       *memo;
   int        quoted;
   NSString   *string;
   
   quoted = **s == '"';
   if( quoted)
   {
      ++*s;
      --*len;
   }
   
   memo = *s;
   while( *len)
   {
      c = **s;
      if( quoted)
      {
         if( c == '"')
            break;
      }
      else
      {
         if( iswhite( c))
            break;
      }
      ++*s;
      --*len;
   }
   
   
   string = [NSString stringWithCString:memo
                                 length:*s - memo];
   if( quoted)
   {
      ++*s;
      --*len;
   }
   
   // brute force, try NSCalendarDate first
   // then NSDecimalNumber Number
   // finally stay as NSString
#if TRY_VALIANTLY_TO_CONVERT
   if( isdigit( *memo))
   {
      if( value = [NSDecimalNumber decimalNumberWithString:string])
         return( value);
   }
   
   if( value = [NSCalendarDate dateWithString:string])
      return( value);
#endif
   
   return( string);
}


static int   _parse_key_value_into_dictionary( char **s, NSUInteger *len, NSMutableDictionary *dictionary)
{
   id   key;
   id   value;
   
   
   key = _parse_value( s, len);
   if( ! key)
      return( -1);
   
   if( skip_white( s, len))
   {
      NSLog( @"Malformed key value pair, only key %@ present", key);
      return( -2);
   }
   
   if( **s != '=')
   {
      NSLog( @"Malformed key value pair, = missing after key %@", key);
      return( -3);
   }
   
   ++*s;
   --*len;
   
   if( skip_white( s, len))
   {
      NSLog( @"Malformed key value pair, missing value after key %@", key);
      return( -4);
   }
   
   value = _parse_object( s, len);
   if( ! value)
   {
      NSLog( @"Malformed key value pair, missing value after key %@", key);
      return( -4);
   }
   [dictionary setObject:value
                  forKey:key];
   
   return( 0);
}


static id   _parse_array( char **s, NSUInteger *len)
{
   NSMutableArray   *array;
   id               parsed;
   
   array = [NSMutableArray array];
   while( *len)
   {
      if( skip_white( s, len))
         return( nil);
      
      if( **s == ',')
      {
         ++*s;
         --*len;
         continue;
      }
      
      if( **s == ')')
      {
         ++*s;
         --*len;
         return( array);
      }
      
      parsed = _parse_object( s, len);
      if( ! parsed)
         break;
      [array addObject:parsed];
   }
   NSLog( @"Array truncated");
   return( nil);
}


static id   _parse_dictionary( char **s, NSUInteger *len)
{
   NSMutableDictionary   *dictionary;
   
   dictionary = [NSMutableDictionary dictionary];
   while( *len)
   {
      if( skip_white( s, len))
         break;
      
      if( **s == ';')
      {
         ++*s;
         --*len;
         continue;
      }
      
      if( **s == '}')
      {
         ++*s;
         --*len;
         return( dictionary);
      }
      
      if( _parse_key_value_into_dictionary( s, len, dictionary) < 0)
         return( nil);
   }
   NSLog( @"Dictionary truncated");
   return( nil);
}


static id  _parse_object( char **s, NSUInteger *len)
{
   if( skip_white( s, len))
      return( nil);
   
   switch( **s)
   {
      case '{' :
         ++*s;
         --*len;
         return( _parse_dictionary( s, len));
         
      case '(' :
         ++*s;
         --*len;
         return( _parse_array( s, len));
   }
   return( _parse_value( s, len));
}


static id    parse_old_plist( NSData *data)
{
   char         *s;
   NSUInteger   len;
   
   s   = (char *) [data bytes];
   len = [data length];
   return( _parse_object( &s, &len));
}


static NSStringEncoding   encodings[] =
{
   NSUTF8StringEncoding,
   NSUTF16StringEncoding,
   NSUTF16BigEndianStringEncoding,
   NSUTF16LittleEndianStringEncoding,
   NSMacOSRomanStringEncoding,
   0
};


int main (int argc, const char * argv[])
{
   NSAutoreleasePool      *pool;
   NSData                 *data;
   NSPropertyListFormat   format;
   NSString               *error;
   NSString               *s;
   id                     list;
   NSStringEncoding       *p_encoding;
   NSFileHandle           *handle;
   
   pool   = [NSAutoreleasePool new];
   handle = [NSFileHandle fileHandleWithStandardInput];
   data   = [handle readDataToEndOfFile];
   
   if( ! [data length])
   {
      fprintf( stderr, "No data to convert\n");
      return( -1);
   }
   
   list = [NSPropertyListSerialization propertyListFromData:data
                                           mutabilityOption:NSPropertyListImmutable
                                                     format:&format
                                           errorDescription:&error];
   if( ! list)
   {
      for( p_encoding = encodings; *p_encoding; p_encoding++)
      {
         s = [[NSString alloc] initWithData:data
                                   encoding:*p_encoding];
         
         list = parse_old_plist( [s dataUsingEncoding:NSUTF8StringEncoding]);
         [s release];
         if( list)
            break;
      }
      
      if( ! list)
      {
         fprintf( stderr, "Conversion of property list failed: %s\n", [error cString]);
         return( -1);;
      }
   }

   printf( "%s", [[list description] cString]);
   return( 0);
}