/* * splitindex.c * Copyright (c) Markus Kohm, 2002 * * $Id: splitindex.c 4 2016-02-18 10:13:32Z mjk $ * * This file is part of the SplitIndex bundle. * * This work may be distributed and/or modified under the conditions of * the LaTeX Project Public License, version 1.3c of the license. * The latest version of this license is in * http://www.latex-project.org/lppl.txt * and version 1.3c or later is part of all distributions of LaTeX * version 2005/12/01 or later and of this work. * * This work has the LPPL maintenance status "author-maintained". * * The Current Maintainer and author of this work is Markus Kohm. * * The list of all files belongig to the SplitIndex bundle is given in * in the file `manifest.txt'. Files generated by means of unpacking the * distribution (using, for example, the docstrip program) or by means * of compiling them from a source file, for example, from splitindex.c * or splitindex.java may be distributed at the distributor's discretion. * However if they are distributed then a copy of the SplitIndex bundle * must be distributed together with them. * * The list of derived (unpacked or compiled) files belongig to the * distribution and covered by LPPL is defined by the unpacking scripts * (with extension .ins) and the installation script (with name * install.sh) which are part of the distribution. * * Two often ignorred clauses from LPPL 1.3c you should not ignore: * ---------------------------------------------------------------- * 2. You may distribute a complete, unmodified copy of the Work as you * received it. Distribution of only part of the Work is considered * modification of the Work, and no right to distribute such a Derived * Work may be assumed under the terms of this clause. * 3. You may distribute a Compiled Work that has been generated from a * complete, unmodified copy of the Work as distributed under Clause 2 * above, as long as that Compiled Work is distributed in such a way that * the recipients may install the Compiled Work on their system exactly * as it would have been installed if they generated a Compiled Work * directly from the Work. */ #include #include #include #include #include #include #include #include #include #include enum { OPT_HELP = 'h', OPT_MAKEINDEX = 'm', OPT_IDENTIFY = 'i', OPT_RESULTIS = 'r', OPT_VERBOSE = 'v', OPT_SUFFIXIS = 's', OPT_VERSION = 'V' }; #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined (NO_LONGOPT) #define HAS_LONGOPT 0 #define HAPAR "" #else /* defined(__OpenBSD__) || defined(__FreeBSD__) || defined(NO_LONGOPT) */ #define HAS_LONGOPT 1 #define HAPAR "\n\t\t" #include static const struct option long_options[] = { { "help", 0, NULL, OPT_HELP }, { "makeindex", 1, NULL, OPT_MAKEINDEX }, { "identify", 1, NULL, OPT_IDENTIFY }, { "resultis", 1, NULL, OPT_RESULTIS }, { "suffixis", 1, NULL, OPT_SUFFIXIS }, { "verbose", 0, NULL, OPT_VERBOSE }, { "version", 0, NULL, OPT_VERSION }, { NULL, 0, NULL, 0 }, }; #endif static const char short_options[] = { OPT_HELP, OPT_MAKEINDEX, ':', OPT_IDENTIFY, ':', OPT_RESULTIS, ':', OPT_SUFFIXIS, ':', OPT_VERBOSE, OPT_VERSION, }; int Verbose = 0; char *MakeIndex = "makeindex"; char *Identify = "^(\\\\indexentry)\\[([^]]*)\\](.*)$"; char *ResultIs = "$1$3"; char *SuffixIs = "-$2"; char *IDX = NULL; char *Jobname = NULL; char * const * MakeIndexArgs = NULL; char *prgname = "splitindex"; int MakeIndexArgc = 0; static void show_version( void ) { printf( "splitindex 0.2a\n" "Copyright (c) 2002 Markus Kohm \n" ); } static void show_usage( FILE *out ) { fputs( "Usage: splitindex [OPTION]... RAWINDEXFILE [MAKEINDEXOPTION]...\n", out ); } static void usage_error( const char *format, ... ) { if ( format != NULL ) { va_list ap; va_start( ap, format ); vfprintf( stderr, format, ap ); va_end( ap ); } fputs( "Try `splitindex " #if HAS_LONGOPT "--help" #else "-h" #endif "' for more information.\n", stderr ); exit( 1 ); } static void show_help( void ) { show_version(); printf( "\n" ); show_usage( stdout ); printf( "Split a single raw index file into multiple raw index files.\n" "Example: splitindex.pl foo.idx.\n" "\n" "Options:\n" " -h" #if HAS_LONGOPT ", --help " #else " " #endif "\tshow this help and terminate\n" " -m" #if HAS_LONGOPT ", --makeindex" #endif " PROGNAME " HAPAR "\tcall PROGNAME instead of default `%s'.\n", MakeIndex ); printf( " -i" #if HAS_LONGOPT ", --identify" #endif " EXPRESSION " HAPAR "\tuse regular EXPRESSION to match entries\n" "\t\t\t(see also option --resultis and --suffixis).\n" "\t\t\tDefault is `%s'.\n", Identify ); printf( " -r" #if HAS_LONGOPT ", --resultis" #endif " PATTERN " HAPAR "\tcreate line to be written from PATTERN after matching\n" "\t\t\tlines (see also option --identify).\n" "\t\t\tDefault is `%s'.\n", ResultIs ); printf( " -s" #if HAS_LONGOPT ", --suffixis" #endif " PATTERN " HAPAR "\tcreate suffix to be used from PATTERN after matching\n" "\t\t\tlines (see also option --identify).\n" "\t\t\tDefault is `%s'.\n", SuffixIs ); printf( " -v" #if HAS_LONGOPT ", --verbose " #else " " #endif "\tbe more verbose\n" "\t\t\t(can be used multiple to increase verbosity)\n" " -V" #if HAS_LONGOPT ", --version " #else " " #endif "\tshow version and terminate\n" ); exit(0); } static void ScanArguments( int argc, char * const argv[] ) { int retVal; char *hs; if ( argc > 0 ) prgname = argv[0]; #if HAS_LONGOPT while ( ( retVal = getopt_long( argc, argv, short_options, long_options, NULL ) ) != - 1 ) { #else while ( ( retVal = getopt( argc, argv, short_options ) ) != - 1 ) { #endif switch( retVal ) { case OPT_HELP: show_help(); break; /* should never be reached */ case OPT_VERSION: show_version(); exit( 0 ); break; /* should never be reached */ case OPT_MAKEINDEX: MakeIndex = optarg; break; case OPT_IDENTIFY: Identify = optarg; break; case OPT_RESULTIS: ResultIs = optarg; break; case OPT_SUFFIXIS: SuffixIs = optarg; break; case OPT_VERBOSE: Verbose++; break; case ':': case '?': usage_error( NULL ); break; /* should never be reached */ } } if ( optind < argc ) { IDX = argv[optind++]; MakeIndexArgc = argc - optind; MakeIndexArgs = argv + optind; } else { usage_error( "missing raw index file\n" ); return; /* should never be reached */ } if ( ( Jobname = strdup( IDX ) ) == NULL ) { perror( prgname ); exit( errno ); } if ( ( ( hs = strrchr( Jobname, '.' ) ) != NULL ) && !strcmp( hs, ".idx" ) ) *hs = 0; } static char *regsub( const char *string, const char *replace, int nmatch, regmatch_t pmatch[] ) { int pass; int size, n; const char *hs; char c; char *retVal = NULL; char *ts; /* Two times parsing to get size and replace */ for ( pass = size = 0; pass < 2; pass++ ) { ts = retVal; for ( hs = replace; *hs; hs++ ) { switch( *hs ) { case '$': if ( isdigit( hs[1] ) ) { n = *++hs - '0'; if ( ( pmatch[n].rm_so >= 0 ) && ( pmatch[n].rm_so < pmatch[n].rm_eo ) ) { /* maybe group n is not empty */ if ( pass ) { strncpy( ts, string + pmatch[n].rm_so, pmatch[n].rm_eo - pmatch[n].rm_so ); ts += pmatch[n].rm_eo - pmatch[n].rm_so; } else { size += pmatch[n].rm_eo - pmatch[n].rm_so; } } } else { if ( pass ) { *ts++ = '$'; } else { size++; } } break; case '\\': switch( c = *++hs ) { case 0: c = '\\'; hs--; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; } if ( pass ) { *ts++ = c; } else { size++; } break; default: if ( pass ) { *ts++ = *hs; } else { size++; } break; } } if ( pass ) { *ts = '\0'; } else { if ( ( retVal = malloc( size + 1 ) ) == NULL ) { perror( prgname ); exit( 1 ); } } } return retVal; } static struct list { struct list *next; char *name; FILE *file; } *IDXfiles = NULL; static FILE *findsuffix( const char *name ) { struct list *run; for ( run = IDXfiles; run != NULL; run = run->next ) { const char *suffix = strchr( run->name, '\0' ); suffix -= 4; /* .idx */ suffix -= strlen( name ); if ( ( suffix >= run->name ) && !strncmp( suffix, name, strlen( name ) ) ) { return run->file; } } return NULL; } static struct list *addentry( char *name, FILE *file ) { struct list *newentry = malloc( sizeof( struct list ) ); if ( !newentry ) { perror( prgname ); exit( 1 ); } newentry->name = name; newentry->file = file; if ( IDXfiles == NULL ) { newentry->next = NULL; } else { newentry->next = IDXfiles; } IDXfiles = newentry; return IDXfiles; } static FILE *newentry( const char *suffix ) { char *name; FILE *file; if ( ( name = malloc( strlen( Jobname ) + strlen( suffix ) + 5 ) ) == NULL ) { perror( prgname ); exit( 1 ); } strcpy( name, Jobname ); strcat( name, suffix ); strcat( name, ".idx" ); if ( ( file = fopen( name, "w" ) ) == NULL ) { perror( name ); exit( errno ); } if ( Verbose > 1 ) printf( "New index file %s\n", name ); addentry( name, file ); return file; } static void CloseAllIDX( void ) { struct list *run; for ( run = IDXfiles; run != NULL; run = run->next ) if ( fclose( run->file ) ) { perror( run->name ); exit( 1 ); } } static void CallMakeIndex( void ) { char **argv; int idx; struct list *run, *next; if ( ( argv = calloc( MakeIndexArgc + 3, sizeof( char * ) ) ) == NULL ) { perror( prgname ); exit( 0 ); } for ( idx = 0; idx < MakeIndexArgc; idx++ ) argv[idx+1] = MakeIndexArgs[idx]; idx++; argv[0] = MakeIndex; for ( run = IDXfiles; run != NULL; run = next ) { pid_t fret; int status; argv[idx] = run->name; if ( ( fret = fork() ) == 0 ) { /* This is the child */ if ( Verbose > 1 ) { int i; for ( i = 0; argv[i] != NULL; i++ ) printf( "\"%s\" ", argv[i] ); printf( "\n" ); } if ( execvp( MakeIndex, argv ) ) { perror( MakeIndex ); exit( 1 ); } } else if ( fret == -1 ) { perror( prgname ); exit( 1 ); } else { /* Parent */ waitpid( fret, &status, 0 ); if ( ! WIFEXITED( status ) ) { fprintf( stderr, "%s terminated abnormally!\n", MakeIndex ); exit( 1 ); } } free( (void *)run->name ); next = run->next; free( run ); } } static void ProcessIDXFile( void ) { FILE *fIDX; char *line; regex_t preg; int retVal; int buffersize = 2050; regmatch_t pmatch[10]; if ( ( line = malloc( buffersize ) ) == NULL ) { perror( prgname ); exit( errno ); } if ( ( retVal = regcomp( &preg, Identify, REG_EXTENDED ) ) != 0 ) { regerror( retVal, &preg, line, buffersize ); fprintf( stderr, "Error at identify: %s\n", line ); free( line ); exit( 1 ); } if ( ( fIDX = fopen( IDX, "r" ) ) == NULL ) { if ( !strcmp ( IDX, Jobname ) ) { if ( ( IDX = malloc( strlen( Jobname + 5 ) ) ) == NULL ) { perror( prgname ); regfree( &preg ); free( line ); exit( errno ); } strcpy( IDX, Jobname ); strcat( IDX, ".idx" ); if ( ( fIDX = fopen( IDX, "r" ) ) == NULL ) { fprintf( stderr, "Can read neither file %s nor file %s\n", Jobname, IDX ); regfree( &preg ); free( line ); exit( 1 ); } } else { fprintf( stderr, "Can't red file %s\n", IDX ); regfree( &preg ); free( line ); exit( 1 ); } } while ( fgets( line, buffersize, fIDX ) != NULL ) { char *hs; char *result, *suffix; FILE *f; while ( ( ( hs = strchr( line, '\n' ) ) == NULL ) && ! feof( fIDX ) ) { if ( ( line = realloc( line, 2 * buffersize ) ) == NULL ) { perror( prgname ); exit( errno ); } if ( fgets( line + buffersize - 1, buffersize + 1, fIDX ) == NULL ) break; buffersize *= 2; } if ( ferror( fIDX ) ) break; if ( ( hs = strchr( line, '\n' ) ) != NULL ) // remove newline *hs = 0; if ( ( retVal = regexec( &preg, line, 10, pmatch, 0 ) ) == 0 ) { result = regsub( line, ResultIs, 10, pmatch ); suffix = regsub( line, SuffixIs, 10, pmatch ); } else { int i; result = strdup( line ); for ( i = 0; i < 10; i++ ) { pmatch[i].rm_so = 0; pmatch[i].rm_eo = 3; } suffix = regsub( "idx", SuffixIs, 10, pmatch ); } if ( !result || !suffix ) { perror( prgname ); exit ( 1 ); } if ( ( f = findsuffix( suffix ) ) == NULL ) f = newentry( suffix ); fprintf( f, "%s\n", result ); free( suffix ); free( result ); } regfree( &preg ); free( line ); if ( ferror( fIDX ) ) { perror( IDX ); fclose( fIDX ); exit( errno ); } fclose( fIDX ); CloseAllIDX(); CallMakeIndex(); } int main( int argc, char * const argv[] ) { int retVal = 0; ScanArguments( argc, argv ); if ( Verbose > 0 ) { show_version(); if ( Verbose > 9 ) { int i; printf( "Identify: \"%s\"\n", Identify ); printf( "ResultIs: \"%s\"\n", ResultIs ); printf( "SuffixIs: \"%s\"\n", SuffixIs ); printf( "IDX: \"%s\"\n", IDX ); printf( "Jobname: \"%s\"\n", Jobname ); printf( "MakeIndex: \"%s\"", MakeIndex ); for ( i = 0; i < MakeIndexArgc; i++ ) printf( " \"%s\"", MakeIndexArgs[i] ); printf( "\n" ); } } ProcessIDXFile(); return retVal; }