/*
 *  Program for export kernel symbols
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>
 *
 *   This program 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.
 *
 *   This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <linux/version.h>

#define LinuxVersionCode( v, p, s ) (((v)<<16)|((p)<<8)|(s))

static int nover = 0;

static void header( FILE *out, const char *filename )
{
  fprintf( out, "\
/*\n\
 *  File generated by program export-symbols from file %s...\n\
 *  Don't edit!!!\n\
 */\n\
\n%s\
#define EXPORT_SYMTAB\n\
#include \"driver.h\"\n\
\n", filename, nover ? "#define SND_NO_MODVERS\n" : "" );
}

static void begin( FILE *out, const char *label )
{
  fprintf( out, "\n" );
#if LinuxVersionCode( 2, 1, 0 ) > LINUX_VERSION_CODE
  fprintf( out, "\
struct symbol_table snd_symbol_table_%s = {\n\
#include <linux/symtab_begin.h>\n", label );
#endif
}

static void footer( FILE *out )
{
#if LinuxVersionCode( 2, 1, 0 ) > LINUX_VERSION_CODE
  fprintf( out, "\
#include <linux/symtab_end.h>\n\
};\n" );
#endif
}

static void symbol( FILE *out, const char *symbol )
{
#if LinuxVersionCode( 2, 1, 0 ) <= LINUX_VERSION_CODE
  fprintf( out, "  EXPORT_SYMBOL%s( %s );\n", nover ? "_NOVERS" : "", symbol );
#else
  fprintf( out, "  X( %s ),\n", symbol );
#endif
}

static int process( const char *filename )
{
  FILE *in, *out;
  char base[ 512 ];
  char outfilename[512];
  char line[ 512 ];
  char *str;
  int stage = 0;
  
  if ( strstr( filename, "/" ) ) {
    fprintf( stderr, "export-symbols: error: filename '%s' contains path\n", filename );
    return 1;
  }
  strcpy( str = base, filename );
  while ( *str && *str != '.' ) str++;
  if ( *str ) *str = '\0';
  strcpy( outfilename, base );
  strcat( outfilename, ".c" );
  if ( ( in = fopen( filename, "r" ) ) == NULL ) {
    fprintf( stderr, "export-symbols: open error: %s\n", strerror( errno ) );
    return 1;
  }
  if ( ( out = fopen( outfilename, "w+" ) ) == NULL ) {
    fprintf( stderr, "export-symbols: create error: %s\n", strerror( errno ) );
    fclose( in );
    return 1;
  }
  printf( "generating %s from %s... ", outfilename, filename ); fflush( stdout );
  header( out, filename );
  line[ sizeof( line ) - 1 ] = '\0';
  while ( !feof( in ) ) {
    if ( fgets( line, sizeof( line ) - 1, in ) == NULL ) continue;
    if ( line[ 0 ] == '\0' ) continue;
    if ( line[ strlen( line ) - 1 ] == '\n' )
      line[ strlen( line ) - 1 ] = '\0';
    while ( line[ strlen( line ) - 1 ] == ' ' ||
            line[ strlen( line ) - 1 ] == '\n' )
      line[ strlen( line ) - 1 ] = '\0';
    switch ( line[ 0 ] ) {
      case '+':		/* copy without change */
        fprintf( out, "%s\n", line + 1 + (line[ 1 ] == ' ' || line[ 1 ] == '\t') );
        break;
      case 'a'...'z':
      case 'A'...'Z':
      case '_':
        if ( !stage ) {
          begin( out, base );
          stage = 1;
        }
        symbol( out, line );
        break;
      case '#':
      case '!':
      case ' ':
        if ( !stage ) {
          begin( out, base );
          stage = 1;
        }
        fprintf( out, "  /* %s */\n", line + 1 + (line[ 1 ] == ' ' || line[ 1 ] == '\t') );
        break;
      case '<':
        fprintf( out, "#if %s\n", line + 1 + (line[ 1 ] == ' ' || line[ 1 ] == '\t') );
        break;
      case '>':
        fprintf( out, "#endif\n" );
        break;
      default:
        fprintf( stderr, "export-symbols: skipping line beginning with char '%c'\n", line[ 0 ] );
        fclose( out );
        fclose( in );
        return 1;
    }
  }
  footer( out );
  printf( "done\n" );
  fclose( out );
  fclose( in );
  return 0;
}

int main( int argc, char *argv[] )
{
  int idx, err;

  if ( argc < 2 ) {
    __usage:
    fprintf( stderr, "Usage: export-symbols [-n] file.sym [[file1.sym] ...]\n" );
    return 1;
  }
  idx = 1;
  if ( !strcmp( argv[ idx ], "-n" ) ) {
    if ( argc < 3 ) goto __usage;
    nover = 1;
    idx++;
  }
  for ( ; idx < argc; idx++ ) {
    if ( (err = process( argv[ idx ] )) < 0 )
      return err;
  }
  return 0;
}
 