/*
**
** Copyright 2004 Hewlett-Packard Development Company, L.P.
**
** Confidential computer software. Valid license from HP required for
** possession, use or copying. Consistent with FAR 12.211 and 12.212,
** Commercial Computer Software, Computer Software Documentation, and
** Technical Data for Commercial Items are licensed to the U.S.
** Government under vendor's standard commercial license.
**
*/

/*
**  Program to extract elements from text libraries
**
**  Building this utility is straightforward.  It can be built with either
**  the C or the C++ compilers.  It should then be linked using the LINK
**  command.  It is not necessary to use CXXLINK since there are no C++
**  templates used in this software.  The resulting image should then be 
**  defined as a foreign command:
**
**      $ CC LIBEXT.C  or $ CXX LIBEXT.C
**      $ LINK LIBEXT
**      $ LIBEXT :== "$SYS$DISK:[]LIBEXT.EXE"
**
**
**  The actions accomplished by this extraction utility depend on the
**  parameters that are being passed:
**
**	library_name 
**
**	    the name of the library from which to extract all modules.
**	    If this is the only parameter passed, all modules are
**	    extracted to files whose names correspond to the module names
**	    as they are found in the library.  This argument is required.
**
**	output_template
**
**	    this argument specifies information to be used when
**	    extracting the modules.  This may include device, directory,
**	    and extension information.  The extension is used for modules
**	    which have only a name and no extension.  The default is ""
**	    (current directory and no default extension).
**
**
**  Examples:
**
**      $ LIBEXT SYS$LIBRARY:DECC$RTLDEF.TLB SYS$DISK:[].H
**
**      Extracts all headers from the C RTL text library.  The files are
**      placed in the current directory using a default extension of .H.
**      The utility logs each of the modules extracted and the name of
**      the file created.
**
**      $ LIBEXT SYS$LIBRARY:DECC$RTLDEF.TLB DISK:[DIRECTORY].H
**      Module ASSERT written to DISK:[DIRECTORY]ASSERT.H;1
**      Module BITYPES written to DISK:[DIRECTORY]BITYPES.H;1
**      ...
**      Module WCHAR written to DISK:[DIRECTORY]WCHAR.H;1
**      Module WCTYPE written to DISK:[DIRECTORY]WCTYPE.H;1
*/

/*
**  Include files
*/
#include <descrip.h>
#include <fab.h>
#include <file.h>
#include <lbr$routines.h>
#include <lbrdef.h>
#include <rab.h>
#include <signal.h>
#include <starlet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unixio.h>


/* 
**  Librarian condition values
*/
extern unsigned int LBR$_NORMAL;
extern unsigned int LBR$_ILLOUTROU;
extern unsigned int RMS$_EOF;


/*
**  Define the pointers to the program arguments
*/
static char *library_name;
static char *output_template = "SYS$DISK:[]";
static char *extension;
static char dna_str[255] = "dna=";
static int unsigned lbr_ndx;
static int logging = 1;


/*
**  Prototype for our extract a module routine
*/
static unsigned int extract_mod(
	const struct dsc$descriptor *const mod_desc,
	const unsigned int *rfa[2]);


/*
**  err_callback is called for fopen failures and will output the RMS STS
**  and STV values
*/
static int err_param; /* the callback requires that you pass a parameter */
static int err_callback(void *ignored, struct FAB *fabp, struct RAB *rabp) {
    int msgvec[2];
    msgvec[0] = 1;  /* 1st longword is arg count */
    msgvec[1] = fabp->fab$l_sts;    /* 2nd longword is message code */
    (void) sys$putmsg ( msgvec, 0, 0, 0 );
    msgvec[1] = fabp->fab$l_stv;
    (void) sys$putmsg ( msgvec, 0, 0, 0 );
    return -1;
}

/*
**  Routine to detect interrupts
*/
void sigint_handler (int signal_code) {
   (void) printf ("Aborting library extraction\n");
   exit(EXIT_FAILURE);

}

/*
**  The main program validates the arguments, opens the library, calls
**  lbr$get_index to do the actual work, and cleans up at the end.
*/
int main (int argc, char *argv[]) {

    /*
    **  Local variables
    */
    unsigned int icond;
    const unsigned int lib_func = LBR$C_READ;
    const unsigned int one = 1;
    $DESCRIPTOR(fns_desc, "");


    /*
    **  Prepare for interrupt
    */
    (void) signal (SIGINT, sigint_handler);


    /*
    **	 First, validate the number of arguments and store them into the
    **	 appropriate variables.
    */
    if (argc < 2) {
	(void) printf("Usage:  libext library-name [output-pattern]");
	exit(EXIT_FAILURE);
	};


    /*
    **	Store the argument pointers into the static copies
    */
    library_name = argv[1];
    if (argc >= 3) output_template = argv[2];
    extension = strrchr(output_template, '.');
    (void) strcat(dna_str, output_template);


    /*
    **  Open a library file with the first argument we've been given
    */
    icond = lbr$ini_control(&lbr_ndx, &lib_func);
    if (icond != (unsigned int) &LBR$_NORMAL) exit(icond);
    fns_desc.dsc$w_length = strlen(library_name);
    fns_desc.dsc$a_pointer = library_name;
    icond = lbr$open(&lbr_ndx, &fns_desc);
    if (icond != (unsigned int) &LBR$_NORMAL) exit(icond);
    icond = lbr$set_locate(&lbr_ndx);
    if (icond != (unsigned int) &LBR$_NORMAL) exit(icond);


    /*
    **	Now do the extraction.  LBR$GET_INDEX calls the supplied function
    **	for each module, that is where the bulk of the work is done.
    */
    icond = lbr$get_index(&lbr_ndx, &one, &extract_mod);
    if (icond != (unsigned int) &LBR$_NORMAL) exit(icond);


    /*
    **  Close the library file
    */
    icond = lbr$close(&lbr_ndx);
    if (icond != (unsigned int) &LBR$_NORMAL) exit(icond);


    /*
    **  Exit successfully
    */
    exit(EXIT_SUCCESS);
}

static unsigned int extract_mod(
	const struct dsc$descriptor *const mod_desc,
	const unsigned int *rfa[2]) {

    /*
    **  Local variables
    */
    int mod_fd;
    unsigned int icond;
    char modname[256];
    char orig_modname[256];
    char *modnamep = modname;
    struct dsc$descriptor_s outbuf;


    /*
    **  Capture the name of the module file in a C string.
    */
    (void) strncpy(modnamep, mod_desc->dsc$a_pointer, mod_desc->dsc$w_length);
    modname[mod_desc->dsc$w_length] = '\0';

    (void) strncpy(orig_modname, mod_desc->dsc$a_pointer, mod_desc->dsc$w_length);
    orig_modname[mod_desc->dsc$w_length] = '\0';


    /*
    **	If given a default extension and the module name has no
    **	extension, then add one
    */
    if ((strchr(modnamep, '.') == NULL) && (extension != NULL)) 
	(void) strcat(modnamep, extension);


    /*
    **  Create the module file
    */
    mod_fd = open(modnamep, O_WRONLY | O_CREAT | O_TRUNC, 0777, "ctx=bin", 
                  &dna_str, "rfm=var", "rat=cr", "err", err_callback, 
                  &err_param);


    /*
    **  We cannot handle any errors on the file creation
    */
    if (-1 == mod_fd) {
	perror("module open");
	(void) printf("file: %s, dna: %s", modnamep, dna_str);
	return (unsigned int) &LBR$_ILLOUTROU;
    };


    /* 
    **  Set the read context for writing the module file
    */
    icond = lbr$find(&lbr_ndx, rfa);
    if ((icond & 3) != 1) return icond;


    /*
    **  Now write the data records into the module file
    */
    while (icond = lbr$get_record(&lbr_ndx, 0, &outbuf),
	   icond != (unsigned int) &RMS$_EOF) {

	if ((icond & 3) != 1) return icond;

	if (-1 == write(mod_fd, outbuf.dsc$a_pointer, outbuf.dsc$w_length)) {
	    perror("module write");
	    return 0;
	    };
    }


    /*
    **  Issue a logging message if necessary before closing the file
    */
    if (logging) {
       char filename[256];
       (void) getname(mod_fd, filename);
       (void) printf ("Module %s written to %s\n", orig_modname, filename);
    }


    /*
    **  End of file seen, close the module file
    */
    if (-1 == close(mod_fd)) {
	perror("module close"); 
	return (unsigned int) &LBR$_ILLOUTROU;
    };


    /*
    **  Finally, return success
    */
    return (unsigned int) &LBR$_NORMAL;
}
    
