xmlPrint.lib.c
/******************************************** -*-c-*- ***********************************************/
/*                                                                                                  */
/* xmlPrint.lib.c:                                                                                  */
/*                                                                                                  */
/****************************************************************************************************/

/****************************************************************************************************/
/*                                                                                                  */
/*     Copyright (C) 2004 Joerg Kunze                                                               */
/*                                                                                                  */
/*     This file is part of siliconBrainLib.                                                        */
/*                                                                                                  */
/*     siliconBrainLib is free software; you can redistribute it and/or modify                      */
/*     it under the terms of the GNU General Public License as published by                         */
/*     the Free Software Foundation; either version 2 of the License, or                            */
/*     (at your option) any later version.                                                          */
/*                                                                                                  */
/*     siliconBrainLib is distributed in the hope that it will be useful,                           */
/*     but WITHOUT ANY WARRANTY; without even the implied warranty of                               */
/*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                */
/*     GNU General Public License for more details.                                                 */
/*                                                                                                  */
/*     You should have received a copy of the GNU General Public License                            */
/*     along with this program; if not, write to the Free Software                                  */
/*     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                    */
/*                                                                                                  */
/****************************************************************************************************/

// char *siliconBrainRelease       = "$siliconBrainRelease: 0.0.4 $";
// char *siliconBrainRcsIdentifier = "$Id: xmlPrint.lib.c,v 1.25 2004/12/14 23:09:01 joerg Exp $";
// char *siliconBrainSaveStamp     = "$siliconBrainSaveStamp: 2004/12/14 22:31:26, Joerg Kunze$";

#include <stdarg.h>

#include "unistd.h"
#include "siliconBrainLib"

#define obstack_chunk_alloc malloc
#define obstack_chunk_free  free

static const char red    [] = "\e[0;31m";
static const char green  [] = "\e[0;32m";
static const char brown  [] = "\e[1;33m";
static const char blue   [] = "\e[0;34m";
static const char magenta[] = "\e[0;35m";
static const char cyan   [] = "\e[0;36m";
static const char normal [] = "\e[0m" ;

static const char *special  = brown  ;
static const char *keyWord  = cyan   ;
static const char *variable = green  ;
static const char *string   = magenta;
static const char *comment  = red    ;
static const char *quote    = blue   ;

/****************************************************************************************************/
/*                                                                                                  */
/* osprintf: obstack_printf and obstack_finish convenience wrapper                                  */
/*                                                                                                  */
/****************************************************************************************************/
extern int osprintf( struct obstack *obstack, char **target, const char *template, ... ) {
   va_list ap;

   int numberOfCharactersWritten;

   va_start( ap, template );
   numberOfCharactersWritten = obstack_vprintf( obstack, template, ap );
   va_end( ap );

   obstack_1grow( obstack, 0 );

   *target = obstack_finish( obstack );

   return numberOfCharactersWritten;
}

/****************************************************************************************************/
/*                                                                                                  */
/* StringBuffer:                                                                                    */
/*                                                                                                  */
/****************************************************************************************************/
void  stringBufferInit( StringBuffer *stringBuffer, Obstack *obstack ) {
   stringBuffer->obstack = obstack;
   stringBuffer->length  = 23;
   stringBuffer->value  = obstack_alloc( stringBuffer->obstack, stringBuffer->length );
   stringBuffer->value[ 0 ] = 0;
}

char *stringBufferAlloc( StringBuffer *stringBuffer, unsigned length ) {

   if( stringBuffer->length < length ) {
      stringBuffer->length = 2*length;
      stringBuffer->value  = obstack_alloc( stringBuffer->obstack, stringBuffer->length );
   }

   return stringBuffer->value;
}

/****************************************************************************************************/
/*                                                                                                  */
/* StringStack:                                                                                     */
/*                                                                                                  */
/****************************************************************************************************/
static void stringStackInit( StringStack *stack ) {
   stack->chunk   = g_string_chunk_new   ( 42 );
   stack->strings = g_ptr_array_sized_new( 42 );
}

static void stringStackDestroy( StringStack *stack ) {
   if( stack->chunk   ) g_string_chunk_free( stack->chunk         );
   if( stack->strings ) g_ptr_array_free   ( stack->strings, true );
}

static void stringStackPush( StringStack *stack, const char *newString ) {
   g_ptr_array_add( stack->strings, g_string_chunk_insert_const( stack->chunk, newString ) );
}

static char *stringStackGet( StringStack *stack ) {
    return g_ptr_array_index( stack->strings, stack->strings->len - 1 );
}

static void stringStackPop( StringStack *stack ) {
   g_ptr_array_remove_index_fast( stack->strings, stack->strings->len - 1 );
}

/****************************************************************************************************/
/*                                                                                                  */
/* indentationFilter:                                                                               */
/*                                                                                                  */
/****************************************************************************************************/
static void printCharacterStandard ( SiliconBrainPrinter *printer, char characterToPrint ) {
   putchar( characterToPrint );
}

static void printStringStandard ( SiliconBrainPrinter *printer, const char *stringToPrint ) {
   printu( stringToPrint );
}

static void printCharacterIndent ( SiliconBrainPrinter *printer, char characterToPrint ) {
   int spaceCounter;

   if( printer->deferredIndentation ) {
      printer->deferredIndentation = false;

      if( characterToPrint != '\n' ) {
         for( spaceCounter = printer->indent; spaceCounter; --spaceCounter )
            putchar( ' ' );
         if( printer->insideComment )
            printu( printer->commentIndent );
      }
   }

   putchar( characterToPrint );

   if( characterToPrint == '\n' ) {
      printer->deferredIndentation = true;
   }
}

static void printStringIndent ( SiliconBrainPrinter *printer, const char *stringToPrint ) {

   for( ; *stringToPrint; ++stringToPrint ) {
      printCharacterIndent( printer, *stringToPrint );
   }
}

/****************************************************************************************************/
/*                                                                                                  */
/* quote:                                                                                           */
/*                                                                                                  */
/****************************************************************************************************/
static void normalQuote( SiliconBrainPrinter *printer, const char *source ) {
   printer->printString( printer, source );
}

static void coloredQuote( SiliconBrainPrinter *printer, const char *source ) {
   printer->printString( printer, quote                );
   printer->printString( printer, source               );
   printer->printString( printer, printer->stringColor );
}

/****************************************************************************************************/
/*                                                                                                  */
/* Xml:                                                                                             */
/*                                                                                                  */
/****************************************************************************************************/
/*--------------------------------------------------------------------------------------------------*/
/* escape:                                                                                          */
/*--------------------------------------------------------------------------------------------------*/
static void printEscapedCharXml( struct SiliconBrainPrinter *printer, char characterToPrint ) {
   switch( characterToPrint ) {
      case '&' : printer->quoter( printer, "&amp;" ); break;
      case '<' : printer->quoter( printer, "&lt;"  ); break;
      case '>' : printer->quoter( printer, "&gt;"  ); break;
      case '\'': printer->quoter( printer, "&apos;"); break;
      case '"' : printer->quoter( printer, "&quot;"); break;
      default  : printer->printCharacter( printer, characterToPrint );
   }
}

/****************************************************************************************************/
/*                                                                                                  */
/* Tty:                                                                                             */
/*                                                                                                  */
/****************************************************************************************************/

/*--------------------------------------------------------------------------------------------------*/
/* escape:                                                                                          */
/*--------------------------------------------------------------------------------------------------*/
static void printEscapedCharTty( struct SiliconBrainPrinter *printer, char characterToPrint ) {
   printer->printCharacter( printer, characterToPrint );
}

/****************************************************************************************************/
/*                                                                                                  */
/* Bash:                                                                                            */
/*                                                                                                  */
/****************************************************************************************************/

/*--------------------------------------------------------------------------------------------------*/
/* escape:                                                                                          */
/*--------------------------------------------------------------------------------------------------*/
static void printEscapedCharBash( struct SiliconBrainPrinter *printer, char characterToPrint ) {
   switch( characterToPrint ) {
      case '\'' : printer->quoter( printer, "'\"'\"'"  ); break;
      default  : printer->printCharacter( printer, characterToPrint );
   }
}

/*--------------------------------------------------------------------------------------------------*/
/* comment:                                                                                         */
/*--------------------------------------------------------------------------------------------------*/
static void printCommentCharBash( struct SiliconBrainPrinter *printer, char characterToPrint ) {
   switch( characterToPrint ) {
      case '\n' : printer->printString( printer, "\n# "  ); break;
      default  : printer->printCharacter( printer, characterToPrint );
   }
}

/****************************************************************************************************/
/*                                                                                                  */
/* BashEnvironment:                                                                                 */
/*                                                                                                  */
/****************************************************************************************************/

/*--------------------------------------------------------------------------------------------------*/
/* onRootTag:                                                                                       */
/*--------------------------------------------------------------------------------------------------*/
static void onRootTagBashEnvironment( struct SiliconBrainPrinter *printer, StartEnd mode ) {

   if( mode == start ) {
      stringBufferAlloc( &printer->namePrefix, strlen( printer->nameOfRootTag ) + 2 );
      stpcpy( stpcpy( printer->namePrefix.value, printer->nameOfRootTag ), "_" );
   }
   else if( mode == end ) {
      printer->namePrefix.value[ 0 ] = 0;
   }
}

/****************************************************************************************************/
/*                                                                                                  */
/* Perl:                                                                                            */
/*                                                                                                  */
/****************************************************************************************************/

/*--------------------------------------------------------------------------------------------------*/
/* escape:                                                                                          */
/*--------------------------------------------------------------------------------------------------*/
static void printEscapedCharPerl( struct SiliconBrainPrinter *printer, char characterToPrint ) {
   switch( characterToPrint ) {
      case '\'' : printer->quoter( printer, "\\'"  ); break;
      default  : printer->printCharacter( printer, characterToPrint );
   }
}

/****************************************************************************************************/
/*                                                                                                  */
/* Generic:                                                                                         */
/*                                                                                                  */
/****************************************************************************************************/

/*--------------------------------------------------------------------------------------------------*/
/* tag:                                                                                             */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterTag( SiliconBrainPrinter *printer, const char *name ) {

   if( printer->insideTag ) {
      printer->insideTag = false;
      printer->printString( printer, printer->tagEndTagBeforeNewTag );
   }

   if( printer->insideText ) {
      printer->insideText = false;
      printer->printString( printer, printer->textValueEnd );
   }

   printer->printString( printer, printer->tagStartTag      );
   printer->printString( printer, printer->namePrefix.value );
   printer->printString( printer, name                      );

   printer->insideTag  = true;

   stringStackPush( &printer->tagNameStack, name );

   if( !printer->nestingDepth ) {
      printer->nameOfRootTag = name;

      if( printer->onRootTag )
         printer->onRootTag( printer, start );
   }

   printer->indent += printer->indentation;
   ++printer->nestingDepth;

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* text:                                                                                            */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterText( SiliconBrainPrinter *printer, const char *value ) {

   if( !value ) return 0;

   printer->insideText = true;

   if( printer->insideTag ) {
      printer->insideTag       = false;
      printer->printString( printer, printer->textEndTagBeforeText  );
   }

   printer->stringColor = normal;

   for(; *value; ++value ) {
      printer->printEscapedChar( printer, *value );
   }

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* comment:                                                                                         */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterComment( SiliconBrainPrinter *printer, const char *value ) {

   if( !printer->doIndent ) return 0;

   if( !value ) return 0;

   if( printer->insideTag ) {
      printer->insideTag       = false;
      printer->printString( printer, printer->tagEndTagBeforeNewTag );
   }

   printer->printString( printer, printer->commentStart );

   printer->insideComment = true;

   if( printer->printCommentChar ) {
      for(; *value; ++value ) {
         printer->printCommentChar( printer, *value );
      }
   } else {
      printer->printString( printer, value );
   }

   printer->printString( printer, printer->commentEnd   );

   printer->insideComment = false;

   printer->printCharacter( printer, '\n' );

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* atttribute:                                                                                      */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterAttribute( SiliconBrainPrinter *printer, const char *name, const char *value ) {

   if( !value ) return 0;

   if( printer->attributeIsOutsideTag ) {
      if( printer->insideTag ) {
         printer->insideTag = false;
         printer->printString( printer, printer->attributeEndTagBeforeAttribute );
      }
   }

   printer->printString( printer, printer->attributeNameStart );
   printer->printString( printer, printer->namePrefix.value   );
   printer->printString( printer, name                        );
   printer->printString( printer, printer->attributeNameEnd   );

   printer->stringColor = string;

   for(; *value; ++value ) {
      printer->printEscapedChar( printer, *value );
   }

   printer->printString( printer, printer->attributeValueEnd );

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* key:                                                                                             */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterKey( SiliconBrainPrinter *printer, const char *key, const char *value ) {

   if( !value ) return 0;

   siliconBrainPrinterTag ( printer, key   );
   siliconBrainPrinterText( printer, value );
   siliconBrainPrinterEnd ( printer        );
   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* keyBool:                                                                                         */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterKeyBool( SiliconBrainPrinter *printer, const char *key, bool value ) {

   siliconBrainPrinterTag ( printer, key   );

   if( printer->boolTrue && value ) {
      printer->insideTag = false;
      printer->printString( printer, printer->boolTrue );
   }
   else if( printer->boolFalse && !value ) {
      printer->insideTag = false;
      printer->printString( printer, printer->boolFalse );
   }

   siliconBrainPrinterEnd ( printer );

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* end:                                                                                             */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterEnd( SiliconBrainPrinter *printer ) {

   --printer->nestingDepth;
   printer->indent -= printer->indentation;

   if( !printer->nestingDepth ) {
      printer->nameOfRootTag = 0;

      if( printer->onRootTag )
         printer->onRootTag( printer, end );
   }

   if( printer->insideText ) {
      printer->insideText = false;
      printer->printString( printer, printer->textValueEnd );
   }

   if( printer->insideTag ) {
      printer->insideTag = false;
      printer->printString( printer, printer->endInsideTag );
   } else {
      if( printer->endTagStart ) {
         printer->printString( printer, printer->endTagStart );
         printer->printString( printer, stringStackGet( &printer->tagNameStack )  );
      }
      printer->printString( printer, printer->endTagEnd );
   }

   stringStackPop( &printer->tagNameStack );

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* destroy:                                                                                         */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterDestroy( SiliconBrainPrinter *printer ) {

   stringStackDestroy( &printer->tagNameStack );

   obstack_free( &printer->obstack, 0 );

   return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/* create:                                                                                          */
/*--------------------------------------------------------------------------------------------------*/
int siliconBrainPrinterInit( SiliconBrainPrinter *printer, int argc, const char *argv[] ) {

   const char *mode = 0;

   obstack_init( &printer->obstack );

   printer->tagNameStack.chunk   = 0;
   printer->tagNameStack.strings = 0;
   printer->insideTag            = false;
   printer->insideText           = false;
   printer->insideComment        = false;
   printer->indent               = 0;
   printer->nestingDepth         = 0;
   printer->quoter               = &normalQuote;
   printer->nameOfRootTag        = 0;
   printer->onRootTag            = 0;

   stringBufferInit( &printer->namePrefix, &printer->obstack );
   stringStackInit ( &printer->tagNameStack                  );

   /*-------------------------------------------------*/
   /* some defaults to things, which are configurable */
   /*-------------------------------------------------*/
   printer->indentation      = 3;
   printer->usageDescription = 0;
   printer->printCommentChar = 0;
   printer->printCharacter   = &printCharacterStandard;
   printer->printString      = &printStringStandard;

   printer->doIndent         = false;

   /*-----------------------------------------------------------------*/
   /* tty:                                                            */
   /*-----------------------------------------------------------------*/
   if( isatty( 1 ) ) {
      printer->doIndent         = true;

      if( argc == 0 || argv[ 1 ] == 0 ) mode = "--tty";
   } else {
      if( argc == 0 || argv[ 1 ] == 0 ) mode = "--xml";
   }

   mode = mode ?: argv[ 1 ];

   if( getenv( "siliconBrainLibPrintIndent" ) ) {
      printer->doIndent         = true;
   }

   if( printer->doIndent ) {
      printer->printCharacter   = &printCharacterIndent;
      printer->printString      = &printStringIndent;
   }

   if( strequ( mode, "--xml" ) ) {
      printer->tagEndTagBeforeNewTag          = ">\n" ;
      printer->tagStartTag                    = "<";
      printer->attributeEndTagBeforeAttribute = "";
      printer->attributeIsOutsideTag          = false;
      printer->attributeNameStart             = " ";
      printer->attributeNameEnd               = "=\"";
      printer->attributeValueEnd              = "\"";
      printer->endInsideTag                   = "/>\n";
      printer->endTagStart                    = "</";
      printer->endTagEnd                      = ">\n";
      printer->textEndTagBeforeText           = "> ";
      printer->textValueEnd                   = " ";
      printer->boolTrue                       = 0;
      printer->boolFalse                      = "> false ";
      printer->commentStart                   = "\n<!-- ";
      printer->commentEnd                     = " -->";
      printer->commentIndent                  = "   ";
      printer->printEscapedChar               = printEscapedCharXml;
   }
   else if( strequ( mode, "--tty" ) ) {
      printer->tagEndTagBeforeNewTag          = ":\n";
      printer->tagStartTag                    = "";
      printer->attributeEndTagBeforeAttribute = ":\n";
      printer->attributeIsOutsideTag          = true;
      printer->attributeNameStart             = "";
      printer->attributeNameEnd               = " = ";
      printer->attributeValueEnd              = "\n";
      printer->endInsideTag                   = ":\n";
      printer->endTagStart                    = 0;
      printer->endTagEnd                      = "\n";
      printer->textEndTagBeforeText           = " = ";
      printer->textValueEnd                   = "";
      printer->boolTrue                       = ": yes";
      printer->boolFalse                      = ": no";
      printer->commentStart                   = "\n# ";
      printer->commentEnd                     = "";
      printer->commentIndent                  = "";
      printer->printEscapedChar               = printEscapedCharTty;
      printer->printCommentChar               = printCommentCharBash;
   }
   else if(
      strequ( mode, "--bash"            ) ||
      strequ( mode, "--bashEnvironment" )
   ) {
      printer->tagEndTagBeforeNewTag          = "='<configured>'\n";
      printer->tagStartTag                    = "";
      printer->attributeEndTagBeforeAttribute = "='<configured>'\n";
      printer->attributeIsOutsideTag          = true;
      printer->attributeNameStart             = "";
      printer->attributeNameEnd               = "='";
      printer->attributeValueEnd              = "'\n";
      printer->endInsideTag                   = "='<configured>'\n";
      printer->endTagStart                    = 0;
      printer->endTagEnd                      = "\n";
      printer->textEndTagBeforeText           = "='";
      printer->textValueEnd                   = "'";
      printer->boolTrue                       = "=";
      printer->boolFalse                      = "='false'";
      printer->commentStart                   = "\n# ";
      printer->commentEnd                     = "";
      printer->commentIndent                  = "";
      printer->printEscapedChar               = printEscapedCharBash;
      printer->printCommentChar               = printCommentCharBash;
      printer->usageDescription               = "Usage: eval \"$(<thisCommand> --bash)\"";
      if( strequ( mode, "--bashEnvironment" ) ) {
         printer->tagStartTag                    = "export ";
         printer->attributeNameStart             = "export ";
         printer->onRootTag                      = onRootTagBashEnvironment;
         printer->usageDescription               = "Usage: eval \"$(<thisCommand> --bashEnvironment)\"";
      }
   }
   else if( strequ( mode, "--perl" ) ) {
      printer->tagEndTagBeforeNewTag          = "='<configured>';\n";
      printer->tagStartTag                    = "$";
      printer->attributeEndTagBeforeAttribute = "='<configured>';\n";
      printer->attributeIsOutsideTag          = true;
      printer->attributeNameStart             = "$";
      printer->attributeNameEnd               = "='";
      printer->attributeValueEnd              = "';\n";
      printer->endInsideTag                   = "='<configured>';\n";
      printer->endTagStart                    = 0;
      printer->endTagEnd                      = "\n";
      printer->textEndTagBeforeText           = "='";
      printer->textValueEnd                   = "';";
      printer->boolTrue                       = "=1;";
      printer->boolFalse                      = "=0;";
      printer->commentStart                   = "\n# ";
      printer->commentEnd                     = "";
      printer->commentIndent                  = "";
      printer->printEscapedChar               = printEscapedCharPerl;
      printer->printCommentChar               = printCommentCharBash;
      printer->usageDescription               = "Usage: eval `<thisCommand> --perl`;";
   }
   else {
      fprintf( stderr, "siliconBrainPrinterCreate: invalid target format \"%s\"\n", argv[ 1 ] );
      exit( 42 );
   }

   if( isatty( 1 ) ) {

      struct obstack *obstack = &printer->obstack;

      #define colorize( field, start, end ) \
         if( printer->field )  \
            osprintf( obstack, &printer->field, "%s%s%s", start, printer->field, end );

      colorize( tagEndTagBeforeNewTag          , special, ""       );
      colorize( tagStartTag                    , special, keyWord  );
      colorize( attributeEndTagBeforeAttribute , special, ""       );
      colorize( attributeNameStart             , special, variable );
      colorize( attributeNameEnd               , special, string   );
      colorize( attributeValueEnd              , special, ""       );
      colorize( endInsideTag                   , special, ""       );
      colorize( endTagStart                    , special, keyWord  );
      colorize( endTagEnd                      , special, ""       );
      colorize( textEndTagBeforeText           , special, normal   );
      colorize( textValueEnd                   , special, keyWord  );
      colorize( boolTrue                       , normal , normal   );
      colorize( boolFalse                      , normal , normal   );
      colorize( commentStart                   , comment, ""       );
      colorize( commentEnd                     , ""     , ""       );
      colorize( commentIndent                  , ""     , ""       );

      printer->quoter = &coloredQuote;
   }

   if( printer->usageDescription ) siliconBrainPrinterComment( printer, printer->usageDescription );

   return 0;
}

/*
$Log: xmlPrint.lib.c,v $
Revision 1.25  2004/12/14 23:09:01  joerg
published for new release 0.0.4

Revision 1.24  2004/12/14 23:02:56  joerg
published for new release 0.0.3

Revision 1.23  2004/12/14 22:44:36  joerg
allFiles: all sources have a Log CVS keyword at the end now.

*/