diff options
Diffstat (limited to 'support/texlab/crates/bibutils_sys/src/modsout.c')
-rw-r--r-- | support/texlab/crates/bibutils_sys/src/modsout.c | 1177 |
1 files changed, 1177 insertions, 0 deletions
diff --git a/support/texlab/crates/bibutils_sys/src/modsout.c b/support/texlab/crates/bibutils_sys/src/modsout.c new file mode 100644 index 0000000000..7187308c7f --- /dev/null +++ b/support/texlab/crates/bibutils_sys/src/modsout.c @@ -0,0 +1,1177 @@ +/* + * modsout.c + * + * Copyright (c) Chris Putnam 2003-2019 + * + * Source code released under the GPL version 2 + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include "is_ws.h" +#include "str.h" +#include "charsets.h" +#include "str_conv.h" +#include "fields.h" +#include "iso639_2.h" +#include "utf8.h" +#include "modstypes.h" +#include "bu_auth.h" +#include "marc_auth.h" +#include "bibformats.h" +#include "msvc_fix.h" + +/***************************************************** + PUBLIC: int modsout_initparams() +*****************************************************/ + +static void modsout_writeheader( FILE *outptr, param *p ); +static void modsout_writefooter( FILE *outptr ); +static int modsout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs ); + +int +modsout_initparams( param *pm, const char *progname ) +{ + pm->writeformat = BIBL_MODSOUT; + pm->format_opts = 0; + pm->charsetout = BIBL_CHARSET_UNICODE; + pm->charsetout_src = BIBL_SRC_DEFAULT; + pm->latexout = 0; + pm->utf8out = 1; + pm->utf8bom = 1; + pm->xmlout = BIBL_XMLOUT_TRUE; + pm->nosplittitle = 0; + pm->verbose = 0; + pm->addcount = 0; + pm->singlerefperfile = 0; + + pm->headerf = modsout_writeheader; + pm->footerf = modsout_writefooter; + pm->assemblef = NULL; + pm->writef = modsout_write; + + if ( !pm->progname ) { + if ( !progname ) pm->progname = NULL; + else { + pm->progname = strdup( progname ); + if ( !pm->progname ) return BIBL_ERR_MEMERR; + } + } + + return BIBL_OK; +} + +/***************************************************** + PUBLIC: int modsout_write() +*****************************************************/ + +/* output_tag() + * + * mode = TAG_OPEN, "<tag>" + * mode = TAG_CLOSE, "</tag>" + * mode = TAG_OPENCLOSE, "<tag>data</tag>" + * mode = TAG_SELFCLOSE, "<tag/>" + * + * newline = TAG_NONEWLINE, "<tag>" + * newline = TAG_NEWLINE, "<tag>\n" + * + */ +#define TAG_OPEN (0) +#define TAG_CLOSE (1) +#define TAG_OPENCLOSE (2) +#define TAG_SELFCLOSE (3) + +#define TAG_NONEWLINE (0) +#define TAG_NEWLINE (1) + +static void +output_tag_core( FILE *outptr, int nindents, char *tag, char *data, unsigned char mode, unsigned char newline, va_list *attrs ) +{ + char *attr, *val; + int i; + + for ( i=0; i<nindents; ++i ) fprintf( outptr, " " ); + + if ( mode!=TAG_CLOSE ) + fprintf( outptr, "<" ); + else + fprintf( outptr, "</" ); + + fprintf( outptr, "%s", tag ); + + do { + attr = va_arg( *attrs, char * ); + if ( attr ) val = va_arg( *attrs, char * ); + if ( attr && val ) + fprintf( outptr, " %s=\"%s\"", attr, val ); + } while ( attr && val ); + + if ( mode!=TAG_SELFCLOSE ) + fprintf( outptr, ">" ); + else + fprintf( outptr, "/>" ); + + if ( mode==TAG_OPENCLOSE ) { + fprintf( outptr, "%s</%s>", data, tag ); + } + + if ( newline==TAG_NEWLINE ) + fprintf( outptr, "\n" ); +} + +/* output_tag() + * + * output XML tag + * + * mode = [ TAG_OPEN | TAG_CLOSE | TAG_OPENCLOSE | TAG_SELFCLOSE ] + * newline = [ TAG_NEWLINE | TAG_NONEWLINE ] + * + * for mode TAG_OPENCLOSE, ensure that value is non-NULL, as string pointed to by value + * will be output in the tag + */ +static void +output_tag( FILE *outptr, int nindents, char *tag, char *value, unsigned char mode, unsigned char newline, ... ) +{ + va_list attrs; + + va_start( attrs, newline ); + output_tag_core( outptr, nindents, tag, value, mode, newline, &attrs ); + va_end( attrs ); +} + +/* output_fil() + * + * output XML tag, but lookup data in fields struct + * + * mode = [ TAG_OPEN | TAG_CLOSE | TAG_OPENCLOSE | TAG_SELFCLOSE ] + * newline = [ TAG_NEWLINE | TAG_NONEWLINE ] + * + * value looked up in fields will only be used in mode TAG_OPENCLOSE + */ +static void +output_fil( FILE *outptr, int nindents, char *tag, fields *f, int n, unsigned char mode, unsigned char newline, ... ) +{ + va_list attrs; + char *value; + + if ( n!=-1 ) { + value = (char *) fields_value( f, n, FIELDS_CHRP ); + va_start( attrs, newline ); + output_tag_core( outptr, nindents, tag, value, mode, newline, &attrs ); + va_end( attrs ); + } +} + +static inline int +lvl2indent( int level ) +{ + if ( level < -1 ) return -level + 1; + else return level + 1; +} + +static inline int +incr_level( int level, int amt ) +{ + if ( level > -1 ) return level+amt; + else return level-amt; +} + +/* convert_findallfields() + * + * Find the positions of all convert.internal tags in the fields + * structure and store the locations in convert.pos element. + * + * Return number of the tags found. + */ +static int +convert_findallfields( fields *f, convert *parts, int nparts, int level ) +{ + int i, n = 0; + + for ( i=0; i<nparts; ++i ) { + parts[i].pos = fields_find( f, parts[i].internal, level ); + n += ( parts[i].pos!=FIELDS_NOTFOUND ); + } + + return n; +} + +static void +output_title( fields *f, FILE *outptr, int level ) +{ + int ttl = fields_find( f, "TITLE", level ); + int subttl = fields_find( f, "SUBTITLE", level ); + int shrttl = fields_find( f, "SHORTTITLE", level ); + int parttl = fields_find( f, "PARTTITLE", level ); + char *val; + + output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "title", f, ttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "subTitle", f, subttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "partName", f, parttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + /* MODS output doesn't verify if we don't at least have a <title/> element */ + if ( ttl==-1 && subttl==-1 ) + output_tag( outptr, lvl2indent(incr_level(level,1)), "title", NULL, TAG_SELFCLOSE, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + + /* output shorttitle if it's different from normal title */ + if ( shrttl!=FIELDS_NOTFOUND ) { + val = (char *) fields_value( f, shrttl, FIELDS_CHRP ); + if ( ttl==FIELDS_NOTFOUND || subttl!=FIELDS_NOTFOUND || strcmp(f->data[ttl].data,val) ) { + output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_OPEN, TAG_NEWLINE, "type", "abbreviated", NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "title", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } + } +} + +static void +output_name( FILE *outptr, char *p, int level ) +{ + str family, part, suffix; + int n=0; + + strs_init( &family, &part, &suffix, NULL ); + + while ( *p && *p!='|' ) str_addchar( &family, *p++ ); + if ( *p=='|' ) p++; + + while ( *p ) { + while ( *p && *p!='|' ) str_addchar( &part, *p++ ); + /* truncate periods from "A. B. Jones" names */ + if ( part.len ) { + if ( part.len==2 && part.data[1]=='.' ) { + part.len=1; + part.data[1]='\0'; + } + if ( n==0 ) + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", part.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "given", NULL ); + n++; + } + if ( *p=='|' ) { + p++; + if ( *p=='|' ) { + p++; + while ( *p && *p!='|' ) str_addchar( &suffix, *p++ ); + } + str_empty( &part ); + } + } + + if ( family.len ) { + if ( n==0 ) + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", family.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "family", NULL ); + n++; + } + + if ( suffix.len ) { + if ( n==0 ) + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", suffix.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "suffix", NULL ); + } + + strs_free( &part, &family, &suffix, NULL ); +} + + +/* MODS v 3.4 + * + * <name [type="corporation"/type="conference"]> + * <namePart></namePart> + * <displayForm></displayForm> + * <affiliation></affiliation> + * <role> + * <roleTerm [authority="marcrealtor"] type="text"></roleTerm> + * </role> + * <description></description> + * </name> + */ + +#define NO_AUTHORITY (0) +#define MARC_AUTHORITY (1) + +static void +output_names( fields *f, FILE *outptr, int level ) +{ + convert names[] = { + { "author", "AUTHOR", 0, MARC_AUTHORITY }, + { "editor", "EDITOR", 0, MARC_AUTHORITY }, + { "annotator", "ANNOTATOR", 0, MARC_AUTHORITY }, + { "artist", "ARTIST", 0, MARC_AUTHORITY }, + { "author", "2ND_AUTHOR", 0, MARC_AUTHORITY }, + { "author", "3RD_AUTHOR", 0, MARC_AUTHORITY }, + { "author", "SUB_AUTHOR", 0, MARC_AUTHORITY }, + { "author", "COMMITTEE", 0, MARC_AUTHORITY }, + { "author", "COURT", 0, MARC_AUTHORITY }, + { "author", "LEGISLATIVEBODY", 0, MARC_AUTHORITY }, + { "author of afterword, colophon, etc.", "AFTERAUTHOR", 0, MARC_AUTHORITY }, + { "author of introduction, etc.", "INTROAUTHOR", 0, MARC_AUTHORITY }, + { "cartographer", "CARTOGRAPHER", 0, MARC_AUTHORITY }, + { "collaborator", "COLLABORATOR", 0, MARC_AUTHORITY }, + { "commentator", "COMMENTATOR", 0, MARC_AUTHORITY }, + { "compiler", "COMPILER", 0, MARC_AUTHORITY }, + { "degree grantor", "DEGREEGRANTOR", 0, MARC_AUTHORITY }, + { "director", "DIRECTOR", 0, MARC_AUTHORITY }, + { "event", "EVENT", 0, NO_AUTHORITY }, + { "inventor", "INVENTOR", 0, MARC_AUTHORITY }, + { "organizer of meeting", "ORGANIZER", 0, MARC_AUTHORITY }, + { "patent holder", "ASSIGNEE", 0, MARC_AUTHORITY }, + { "performer", "PERFORMER", 0, MARC_AUTHORITY }, + { "producer", "PRODUCER", 0, MARC_AUTHORITY }, + { "addressee", "ADDRESSEE", 0, MARC_AUTHORITY }, + { "redactor", "REDACTOR", 0, MARC_AUTHORITY }, + { "reporter", "REPORTER", 0, MARC_AUTHORITY }, + { "sponsor", "SPONSOR", 0, MARC_AUTHORITY }, + { "translator", "TRANSLATOR", 0, MARC_AUTHORITY }, + { "writer", "WRITER", 0, MARC_AUTHORITY }, + }; + int i, n, nfields, ntypes = sizeof( names ) / sizeof( convert ); + int f_asis, f_corp, f_conf; + str role; + + str_init( &role ); + nfields = fields_num( f ); + for ( n=0; n<ntypes; ++n ) { + for ( i=0; i<nfields; ++i ) { + if ( fields_level( f, i )!=level ) continue; + if ( f->data[i].len==0 ) continue; + f_asis = f_corp = f_conf = 0; + str_strcpyc( &role, f->tag[i].data ); + if ( str_findreplace( &role, ":ASIS", "" )) f_asis=1; + if ( str_findreplace( &role, ":CORP", "" )) f_corp=1; + if ( str_findreplace( &role, ":CONF", "" )) f_conf=1; + if ( strcasecmp( role.data, names[n].internal ) ) + continue; + if ( f_asis ) { + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } else if ( f_corp ) { + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "corporate", NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } else if ( f_conf ) { + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "conference", NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } else { + output_name(outptr, f->data[i].data, level); + } + output_tag( outptr, lvl2indent(incr_level(level,1)), "role", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + if ( names[n].code & MARC_AUTHORITY ) + output_tag( outptr, lvl2indent(incr_level(level,2)), "roleTerm", names[n].mods, TAG_OPENCLOSE, TAG_NEWLINE, "authority", "marcrelator", "type", "text", NULL ); + else + output_tag( outptr, lvl2indent(incr_level(level,2)), "roleTerm", names[n].mods, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "role", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(level), "name", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + fields_setused( f, i ); + } + } + str_free( &role ); +} + +/* datepos[ NUM_DATE_TYPES ] + * use define to ensure that the array and loops don't get out of sync + * datepos[0] -> DATE:YEAR/PARTDATE:YEAR + * datepos[1] -> DATE:MONTH/PARTDATE:MONTH + * datepos[2] -> DATE:DAY/PARTDATE:DAY + * datepos[3] -> DATE/PARTDATE + */ +#define DATE_YEAR (0) +#define DATE_MONTH (1) +#define DATE_DAY (2) +#define DATE_ALL (3) +#define NUM_DATE_TYPES (4) + +static int +find_datepos( fields *f, int level, unsigned char use_altnames, int datepos[NUM_DATE_TYPES] ) +{ + char *src_names[] = { "DATE:YEAR", "DATE:MONTH", "DATE:DAY", "DATE" }; + char *alt_names[] = { "PARTDATE:YEAR", "PARTDATE:MONTH", "PARTDATE:DAY", "PARTDATE" }; + int found = 0; + int i; + + for ( i=0; i<NUM_DATE_TYPES; ++i ) { + if ( !use_altnames ) + datepos[i] = fields_find( f, src_names[i], level ); + else + datepos[i] = fields_find( f, alt_names[i], level ); + if ( datepos[i]!=FIELDS_NOTFOUND ) found = 1; + } + + return found; +} + +/* find_dateinfo() + * + * fill datepos[] array with position indexes to date information in fields *f + * + * when generating dates for LEVEL_MAIN, first look at level=LEVEL_MAIN, but if that + * fails, use LEVEL_ANY (-1) + * + * returns 1 if date information found, 0 otherwise + */ +static int +find_dateinfo( fields *f, int level, int datepos[ NUM_DATE_TYPES ] ) +{ + int found; + + /* default to finding date information for the current level */ + found = find_datepos( f, level, 0, datepos ); + + /* for LEVEL_MAIN, do whatever it takes to find a date */ + if ( !found && level == LEVEL_MAIN ) { + found = find_datepos( f, -1, 0, datepos ); + } + if ( !found && level == LEVEL_MAIN ) { + found = find_datepos( f, -1, 1, datepos ); + } + + return found; +} + +static void +output_datepieces( fields *f, FILE *outptr, int pos[ NUM_DATE_TYPES ] ) +{ + str *s; + int i; + + for ( i=0; i<3 && pos[i]!=-1; ++i ) { + if ( i>0 ) fprintf( outptr, "-" ); + /* zero pad month or days written as "1", "2", "3" ... */ + if ( i==DATE_MONTH || i==DATE_DAY ) { + s = fields_value( f, pos[i], FIELDS_STRP_NOUSE ); + if ( s->len==1 ) { + fprintf( outptr, "0" ); + } + } + fprintf( outptr, "%s", (char *) fields_value( f, pos[i], FIELDS_CHRP ) ); + } +} + +static void +output_dateissued( fields *f, FILE *outptr, int level, int pos[ NUM_DATE_TYPES ] ) +{ + output_tag( outptr, lvl2indent(incr_level(level,1)), "dateIssued", NULL, TAG_OPEN, TAG_NONEWLINE, NULL ); + if ( pos[ DATE_YEAR ]!=-1 || pos[ DATE_MONTH ]!=-1 || pos[ DATE_DAY ]!=-1 ) { + output_datepieces( f, outptr, pos ); + } else { + fprintf( outptr, "%s", (char *) fields_value( f, pos[ DATE_ALL ], FIELDS_CHRP ) ); + } + fprintf( outptr, "</dateIssued>\n" ); +} + +static void +output_origin( fields *f, FILE *outptr, int level ) +{ + convert parts[] = { + { "issuance", "ISSUANCE", 0, 0 }, + { "publisher", "PUBLISHER", 0, 0 }, + { "place", "ADDRESS", 0, 1 }, + { "place", "ADDRESS:PUBLISHER", 0, 0 }, + { "place", "ADDRESS:AUTHOR", 0, 0 }, + { "edition", "EDITION", 0, 0 }, + { "dateCaptured", "URLDATE", 0, 0 } + }; + int nparts = sizeof( parts ) / sizeof( parts[0] ); + int i, found, datefound, datepos[ NUM_DATE_TYPES ]; + + found = convert_findallfields( f, parts, nparts, level ); + datefound = find_dateinfo( f, level, datepos ); + if ( !found && !datefound ) return; + + + output_tag( outptr, lvl2indent(level), "originInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + + /* issuance must precede date */ + if ( parts[0].pos!=-1 ) + output_fil( outptr, lvl2indent(incr_level(level,1)), "issuance", f, parts[0].pos, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + + /* date */ + if ( datefound ) + output_dateissued( f, outptr, level, datepos ); + + /* rest of the originInfo elements */ + for ( i=1; i<nparts; i++ ) { + + /* skip missing originInfo elements */ + if ( parts[i].pos==-1 ) continue; + + /* normal originInfo element */ + if ( parts[i].code==0 ) { + output_fil( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, f, parts[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } + + /* originInfo with placeTerm info */ + else { + output_tag( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,2)), "placeTerm", f, parts[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } + } + + output_tag( outptr, lvl2indent(level), "originInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); +} + +/* output_language_core() + * + * generates language output for tag="langauge" or tag="languageOfCataloging" + * if possible, outputs iso639-2b code for the language + * + * <language> + * <languageTerm type="text">xxx</languageTerm> + * </language> + * + * <language> + * <languageTerm type="text">xxx</languageTerm> + * <languageTerm type="code" authority="iso639-2b">xxx</languageTerm> + * </language> + * + */ +static void +output_language_core( fields *f, int n, FILE *outptr, char *tag, int level ) +{ + char *lang, *code; + + lang = (char *) fields_value( f, n, FIELDS_CHRP ); + code = iso639_2_from_language( lang ); + + output_tag( outptr, lvl2indent(level), tag, NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "languageTerm", lang, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL ); + if ( code ) { + output_tag( outptr, lvl2indent(incr_level(level,1)), "languageTerm", code, TAG_OPENCLOSE, TAG_NEWLINE, "type", "code", "authority", "iso639-2b", NULL ); + } + output_tag( outptr, lvl2indent(level), tag, NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); +} + +static void +output_language( fields *f, FILE *outptr, int level ) +{ + int n; + n = fields_find( f, "LANGUAGE", level ); + if ( n!=FIELDS_NOTFOUND ) + output_language_core( f, n, outptr, "language", level ); +} + +static void +output_description( fields *f, FILE *outptr, int level ) +{ + char *val; + int n; + + n = fields_find( f, "DESCRIPTION", level ); + if ( n!=FIELDS_NOTFOUND ) { + val = ( char * ) fields_value( f, n, FIELDS_CHRP ); + output_tag( outptr, lvl2indent(level), "physicalDescription", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(incr_level(level,1)), "note", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(level), "physicalDescription", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } +} + +static void +output_toc( fields *f, FILE *outptr, int level ) +{ + char *val; + int n; + + n = fields_find( f, "CONTENTS", level ); + if ( n!=FIELDS_NOTFOUND ) { + val = (char *) fields_value( f, n, FIELDS_CHRP ); + output_tag( outptr, lvl2indent(level), "tableOfContents", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } +} + +/* detail output + * + * for example: + * + * <detail type="volume"><number>xxx</number></detail + */ +static void +mods_output_detail( fields *f, FILE *outptr, int n, char *item_name, int level ) +{ + if ( n!=-1 ) { + output_tag( outptr, lvl2indent(incr_level(level,1)), "detail", NULL, TAG_OPEN, TAG_NONEWLINE, "type", item_name, NULL ); + output_fil( outptr, 0, "number", f, n, TAG_OPENCLOSE, TAG_NONEWLINE, NULL ); + output_tag( outptr, 0, "detail", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } +} + + +/* extents output + * + * <extent unit="page"> + * <start>xxx</start> + * <end>xxx</end> + * </extent> + */ +static void +mods_output_extents( fields *f, FILE *outptr, int start, int end, int total, char *extype, int level ) +{ + char *val; + + output_tag( outptr, lvl2indent(incr_level(level,1)), "extent", NULL, TAG_OPEN, TAG_NEWLINE, "unit", extype, NULL ); + if ( start!=-1 ) { + val = (char *) fields_value( f, start, FIELDS_CHRP ); + output_tag( outptr, lvl2indent(incr_level(level,2)), "start", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } + if ( end!=-1 ) { + val = (char *) fields_value( f, end, FIELDS_CHRP ); + output_tag( outptr, lvl2indent(incr_level(level,2)), "end", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } + if ( total!=-1 ) { + val = (char *) fields_value( f, total, FIELDS_CHRP ); + output_tag( outptr, lvl2indent(incr_level(level,2)), "total", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } + output_tag( outptr, lvl2indent(incr_level(level,1)), "extent", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); +} + +static void +try_output_partheader( FILE *outptr, int wrote_header, int level ) +{ + if ( !wrote_header ) + output_tag( outptr, lvl2indent(level), "part", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); +} + +static void +try_output_partfooter( FILE *outptr, int wrote_header, int level ) +{ + if ( wrote_header ) + output_tag( outptr, lvl2indent(level), "part", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); +} + +/* part date output + * + * <date>xxxx-xx-xx</date> + * + */ +static int +output_partdate( fields *f, FILE *outptr, int level, int wrote_header ) +{ + convert parts[] = { + { "", "PARTDATE:YEAR", 0, 0 }, + { "", "PARTDATE:MONTH", 0, 0 }, + { "", "PARTDATE:DAY", 0, 0 }, + }; + int nparts = sizeof(parts)/sizeof(parts[0]); + + if ( !convert_findallfields( f, parts, nparts, level ) ) return 0; + + try_output_partheader( outptr, wrote_header, level ); + + output_tag( outptr, lvl2indent(incr_level(level,1)), "date", NULL, TAG_OPEN, TAG_NONEWLINE, NULL ); + + if ( parts[0].pos!=-1 ) { + fprintf( outptr, "%s", (char *) fields_value( f, parts[0].pos, FIELDS_CHRP ) ); + } else fprintf( outptr, "XXXX" ); + + if ( parts[1].pos!=-1 ) { + fprintf( outptr, "-%s", (char *) fields_value( f, parts[1].pos, FIELDS_CHRP ) ); + } + + if ( parts[2].pos!=-1 ) { + if ( parts[1].pos==-1 ) + fprintf( outptr, "-XX" ); + fprintf( outptr, "-%s", (char *) fields_value( f, parts[2].pos, FIELDS_CHRP ) ); + } + + fprintf( outptr,"</date>\n"); + + return 1; +} + +static int +output_partpages( fields *f, FILE *outptr, int level, int wrote_header ) +{ + convert parts[] = { + { "", "PAGES:START", 0, 0 }, + { "", "PAGES:STOP", 0, 0 }, + { "", "PAGES", 0, 0 }, + { "", "PAGES:TOTAL", 0, 0 } + }; + int nparts = sizeof(parts)/sizeof(parts[0]); + + if ( !convert_findallfields( f, parts, nparts, level ) ) return 0; + + try_output_partheader( outptr, wrote_header, level ); + + /* If PAGES:START or PAGES:STOP are undefined */ + if ( parts[0].pos==-1 || parts[1].pos==-1 ) { + if ( parts[0].pos!=-1 ) + mods_output_detail( f, outptr, parts[0].pos, "page", level ); + if ( parts[1].pos!=-1 ) + mods_output_detail( f, outptr, parts[1].pos, "page", level ); + if ( parts[2].pos!=-1 ) + mods_output_detail( f, outptr, parts[2].pos, "page", level ); + if ( parts[3].pos!=-1 ) + mods_output_extents( f, outptr, -1, -1, parts[3].pos, "page", level ); + } + /* If both PAGES:START and PAGES:STOP are defined */ + else { + mods_output_extents( f, outptr, parts[0].pos, parts[1].pos, parts[3].pos, "page", level ); + } + + return 1; +} + +static int +output_partelement( fields *f, FILE *outptr, int level, int wrote_header ) +{ + convert parts[] = { + { "", "NUMVOLUMES", 0, 0 }, + { "volume", "VOLUME", 0, 0 }, + { "section", "SECTION", 0, 0 }, + { "issue", "ISSUE", 0, 0 }, + { "number", "NUMBER", 0, 0 }, + { "publiclawnumber", "PUBLICLAWNUMBER", 0, 0 }, + { "session", "SESSION", 0, 0 }, + { "articlenumber", "ARTICLENUMBER", 0, 0 }, + { "part", "PART", 0, 0 }, + { "chapter", "CHAPTER", 0, 0 }, + { "report number", "REPORTNUMBER", 0, 0 }, + }; + int i, nparts = sizeof( parts ) / sizeof( convert ); + + if ( !convert_findallfields( f, parts, nparts, level ) ) return 0; + + try_output_partheader( outptr, wrote_header, level ); + + /* start loop at 1 to skip NUMVOLUMES */ + for ( i=1; i<nparts; ++i ) { + if ( parts[i].pos==-1 ) continue; + mods_output_detail( f, outptr, parts[i].pos, parts[i].mods, level ); + } + + if ( parts[0].pos!=-1 ) + mods_output_extents( f, outptr, -1, -1, parts[0].pos, "volumes", level ); + + return 1; +} + +static void +output_part( fields *f, FILE *outptr, int level ) +{ + int wrote_hdr; + wrote_hdr = output_partdate( f, outptr, level, 0 ); + wrote_hdr += output_partelement( f, outptr, level, wrote_hdr ); + wrote_hdr += output_partpages( f, outptr, level, wrote_hdr ); + try_output_partfooter( outptr, wrote_hdr, level ); +} + +static void +output_recordInfo( fields *f, FILE *outptr, int level ) +{ + int n; + n = fields_find( f, "LANGCATALOG", level ); + if ( n!=FIELDS_NOTFOUND ) { + output_tag( outptr, lvl2indent(level), "recordInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_language_core( f, n, outptr, "languageOfCataloging", incr_level(level,1) ); + output_tag( outptr, lvl2indent(level), "recordInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } +} + +/* output_genre() + * + * <genre authority="marcgt">thesis</genre> + * <genre authority="bibutilsgt">Diploma thesis</genre> + */ +static void +output_genre( fields *f, FILE *outptr, int level ) +{ + char *value, *attr = NULL, *attrvalue = NULL; + int i, n; + + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) != level ) continue; + if ( !fields_match_tag( f, i, "GENRE:MARC" ) && !fields_match_tag( f, i, "GENRE:BIBUTILS" ) && !fields_match_tag( f, i, "GENRE:UNKNOWN" ) ) continue; + value = fields_value( f, i, FIELDS_CHRP ); + if ( is_marc_genre( value ) ) { + attr = "authority"; + attrvalue = "marcgt"; + } + else if ( is_bu_genre( value ) ) { + attr = "authority"; + attrvalue = "bibutilsgt"; + } + output_tag( outptr, lvl2indent(level), "genre", value, TAG_OPENCLOSE, TAG_NEWLINE, attr, attrvalue, NULL ); + } +} + +/* output_resource() + * + * <typeOfResource>text</typeOfResource> + */ +static void +output_resource( fields *f, FILE *outptr, int level ) +{ + char *value; + int n; + + n = fields_find( f, "RESOURCE", level ); + if ( n!=FIELDS_NOTFOUND ) { + value = fields_value( f, n, FIELDS_CHRP ); + if ( is_marc_resource( value ) ) { + output_fil( outptr, lvl2indent(level), "typeOfResource", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } else { + fprintf( stderr, "Illegal typeofResource = '%s'\n", value ); + } + } +} + +static void +output_type( fields *f, FILE *outptr, int level ) +{ + int n; + + /* silence warnings about INTERNAL_TYPE being unused */ + n = fields_find( f, "INTERNAL_TYPE", LEVEL_MAIN ); + if ( n!=FIELDS_NOTFOUND ) fields_setused( f, n ); + + output_resource( f, outptr, level ); + output_genre( f, outptr, level ); +} + +/* output_abs() + * + * <abstract>xxxx</abstract> + */ +static void +output_abs( fields *f, FILE *outptr, int level ) +{ + int n; + + n = fields_find( f, "ABSTRACT", level ); + output_fil( outptr, lvl2indent(level), "abstract", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); +} + +static void +output_notes( fields *f, FILE *outptr, int level ) +{ + int i, n; + char *t; + + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) != level ) continue; + t = fields_tag( f, i, FIELDS_CHRP_NOUSE ); + if ( !strcasecmp( t, "NOTES" ) ) + output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + else if ( !strcasecmp( t, "PUBSTATE" ) ) + output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "publication status", NULL ); + else if ( !strcasecmp( t, "ANNOTE" ) ) + output_fil( outptr, lvl2indent(level), "bibtex-annote", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + else if ( !strcasecmp( t, "TIMESCITED" ) ) + output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "times cited", NULL ); + else if ( !strcasecmp( t, "ANNOTATION" ) ) + output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "annotation", NULL ); + else if ( !strcasecmp( t, "ADDENDUM" ) ) + output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "addendum", NULL ); + else if ( !strcasecmp( t, "BIBKEY" ) ) + output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "bibliography key", NULL ); + } +} + +/* output_key() + * + * <subject> + * <topic>xxxx</topic> + * </subject> + */ +static void +output_key( fields *f, FILE *outptr, int level ) +{ + int i, n; + + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) != level ) continue; + if ( !strcasecmp( f->tag[i].data, "KEYWORD" ) ) { + output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "topic", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } + else if ( !strcasecmp( f->tag[i].data, "EPRINTCLASS" ) ) { + output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + output_fil( outptr, lvl2indent(incr_level(level,1)), "topic", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "class", "primary", NULL ); + output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } + } +} + +static void +output_sn( fields *f, FILE *outptr, int level ) +{ + convert sn_types[] = { + { "isbn", "ISBN", 0, 0 }, + { "isbn", "ISBN13", 0, 0 }, + { "lccn", "LCCN", 0, 0 }, + { "issn", "ISSN", 0, 0 }, + { "coden", "CODEN", 0, 0 }, + { "citekey", "REFNUM", 0, 0 }, + { "doi", "DOI", 0, 0 }, + { "eid", "EID", 0, 0 }, + { "eprint", "EPRINT", 0, 0 }, + { "eprinttype","EPRINTTYPE",0, 0 }, + { "pubmed", "PMID", 0, 0 }, + { "MRnumber", "MRNUMBER", 0, 0 }, + { "medline", "MEDLINE", 0, 0 }, + { "pii", "PII", 0, 0 }, + { "pmc", "PMC", 0, 0 }, + { "arXiv", "ARXIV", 0, 0 }, + { "isi", "ISIREFNUM", 0, 0 }, + { "accessnum", "ACCESSNUM", 0, 0 }, + { "jstor", "JSTOR", 0, 0 }, + { "isrn", "ISRN", 0, 0 }, + }; + int ntypes = sizeof( sn_types ) / sizeof( sn_types[0] ); + int i, n, found; + + /* output call number */ + n = fields_find( f, "CALLNUMBER", level ); + output_fil( outptr, lvl2indent(level), "classification", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + + /* output specialized serialnumber */ + found = convert_findallfields( f, sn_types, ntypes, level ); + if ( found ) { + for ( i=0; i<ntypes; ++i ) { + if ( sn_types[i].pos==-1 ) continue; + output_fil( outptr, lvl2indent(level), "identifier", f, sn_types[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", sn_types[i].mods, NULL ); + } + } + + /* output _all_ elements of type SERIALNUMBER */ + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( f->level[i]!=level ) continue; + if ( strcasecmp( f->tag[i].data, "SERIALNUMBER" ) ) continue; + output_fil( outptr, lvl2indent(level), "identifier", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "serial number", NULL ); + } +} + +/* output_url() + * + * <location> + * <url>URL</url> + * <url urlType="pdf">PDFLINK</url> + * <url displayLabel="Electronic full text" access="raw object">PDFLINK</url> + * <physicalLocation>LOCATION</physicalLocation> + * </location> + */ +static void +output_url( fields *f, FILE *outptr, int level ) +{ + int location = fields_find( f, "LOCATION", level ); + int url = fields_find( f, "URL", level ); + int fileattach = fields_find( f, "FILEATTACH", level ); + int pdflink = fields_find( f, "PDFLINK", level ); + int i, n; + + if ( url==FIELDS_NOTFOUND && location==FIELDS_NOTFOUND && pdflink==FIELDS_NOTFOUND && fileattach==FIELDS_NOTFOUND ) return; + output_tag( outptr, lvl2indent(level), "location", NULL, TAG_OPEN, TAG_NEWLINE, NULL ); + + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( f->level[i]!=level ) continue; + if ( strcasecmp( f->tag[i].data, "URL" ) ) continue; + output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } + for ( i=0; i<n; ++i ) { + if ( f->level[i]!=level ) continue; + if ( strcasecmp( f->tag[i].data, "PDFLINK" ) ) continue; +/* output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "urlType", "pdf", NULL ); */ + output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + } + for ( i=0; i<n; ++i ) { + if ( f->level[i]!=level ) continue; + if ( strcasecmp( f->tag[i].data, "FILEATTACH" ) ) continue; + output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "displayLabel", "Electronic full text", "access", "raw object", NULL ); + } + if ( location!=-1 ) + output_fil( outptr, lvl2indent(incr_level(level,1)), "physicalLocation", f, location, TAG_OPENCLOSE, TAG_NEWLINE, NULL ); + + output_tag( outptr, lvl2indent(level), "location", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); +} + +/* refnum should start with a non-number and not include spaces -- ignore this */ +static void +output_refnum( fields *f, int n, FILE *outptr ) +{ + char *p = fields_value( f, n, FIELDS_CHRP_NOUSE ); +/* if ( p && ((*p>='0' && *p<='9') || *p=='-' || *p=='_' )) + fprintf( outptr, "ref" );*/ + while ( p && *p ) { + if ( !is_ws(*p) ) fprintf( outptr, "%c", *p ); +/* if ( (*p>='A' && *p<='Z') || + (*p>='a' && *p<='z') || + (*p>='0' && *p<='9') || + (*p=='-') || (*p==' + (*p=='_') ) fprintf( outptr, "%c", *p );*/ + p++; + } +} + +static void +output_head( fields *f, FILE *outptr, int dropkey, unsigned long numrefs ) +{ + int n; + fprintf( outptr, "<mods"); + if ( !dropkey ) { + n = fields_find( f, "REFNUM", LEVEL_MAIN ); + if ( n!=FIELDS_NOTFOUND ) { + fprintf( outptr, " ID=\""); + output_refnum( f, n, outptr ); + fprintf( outptr, "\""); + } + } + fprintf( outptr, ">\n" ); +} + +static int +original_items( fields *f, int level ) +{ + int i, targetlevel, n; + if ( level < 0 ) return 0; + targetlevel = -( level + 2 ); + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) == targetlevel ) + return targetlevel; + } + return 0; +} + +static void +output_citeparts( fields *f, FILE *outptr, int level, int max ) +{ + int orig_level; + + output_title( f, outptr, level ); + output_names( f, outptr, level ); + output_origin( f, outptr, level ); + output_type( f, outptr, level ); + output_language( f, outptr, level ); + output_description( f, outptr, level ); + + if ( level >= 0 && level < max ) { + output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_OPEN, TAG_NEWLINE, "type", "host", NULL ); + output_citeparts( f, outptr, incr_level(level,1), max ); + output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } + /* Look for original item things */ + orig_level = original_items( f, level ); + if ( orig_level ) { + output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_OPEN, TAG_NEWLINE, "type", "original", NULL ); + output_citeparts( f, outptr, orig_level, max ); + output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_CLOSE, TAG_NEWLINE, NULL ); + } + output_abs( f, outptr, level ); + output_notes( f, outptr, level ); + output_toc( f, outptr, level ); + output_key( f, outptr, level ); + output_sn( f, outptr, level ); + output_url( f, outptr, level ); + output_part( f, outptr, level ); + + output_recordInfo( f, outptr, level ); +} + +static void +modsout_report_unused_tags( fields *f, param *p, unsigned long numrefs ) +{ + int i, n, nwritten, nunused = 0, level; + char *tag, *value; + n = fields_num( f ); + for ( i=0; i<n; ++i ) { + if ( fields_used( f, i ) ) continue; + nunused++; + } + if ( nunused ) { + if ( p->progname ) fprintf( stderr, "%s: ", p->progname ); + fprintf( stderr, "Reference %lu has unused tags.\n", numrefs+1 ); + /* Find author from level 0 */ + nwritten = 0; + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) != 0 ) continue; + tag = fields_tag( f, i, FIELDS_CHRP_NOUSE ); + if ( strcasecmp( tag, "AUTHOR" ) && strcasecmp( tag, "AUTHOR:ASIS" ) && strcasecmp( tag, "AUTHOR:CORP" ) ) continue; + value = fields_value( f, i, FIELDS_CHRP_NOUSE ); + if ( nwritten==0 ) fprintf( stderr, "\tAuthor(s) (level=0):\n" ); + fprintf( stderr, "\t\t'%s'\n", value ); + nwritten++; + } + nwritten = 0; + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) != 0 ) continue; + tag = fields_tag( f, i, FIELDS_CHRP_NOUSE ); + if ( strcasecmp( tag, "DATE:YEAR" ) && strcasecmp( tag, "PARTDATE:YEAR" ) ) continue; + value = fields_value( f, i, FIELDS_CHRP_NOUSE ); + if ( nwritten==0 ) fprintf( stderr, "\tYear(s) (level=0):\n" ); + fprintf( stderr, "\t\t'%s'\n", value ); + nwritten++; + } + nwritten = 0; + for ( i=0; i<n; ++i ) { + if ( fields_level( f, i ) != 0 ) continue; + tag = fields_tag( f, i, FIELDS_CHRP_NOUSE ); + if ( strncasecmp( tag, "TITLE", 5 ) ) continue; + value = fields_value( f, i, FIELDS_CHRP_NOUSE ); + if ( nwritten==0 ) fprintf( stderr, "\tTitle(s) (level=0):\n" ); + fprintf( stderr, "\t\t'%s'\n", value ); + nwritten++; + } + + fprintf( stderr, "\tUnused tags:\n" ); + for ( i=0; i<n; ++i ) { + if ( fields_used( f, i ) ) continue; + tag = fields_tag( f, i, FIELDS_CHRP_NOUSE ); + value = fields_value( f, i, FIELDS_CHRP_NOUSE ); + level = fields_level( f, i ); + fprintf( stderr, "\t\ttag: '%s' value: '%s' level: %d\n", + tag, value, level ); + } + } +} + +static int +modsout_write( fields *f, FILE *outptr, param *p, unsigned long numrefs ) +{ + int max, dropkey; + max = fields_maxlevel( f ); + dropkey = ( p->format_opts & BIBL_FORMAT_MODSOUT_DROPKEY ); + + output_head( f, outptr, dropkey, numrefs ); + output_citeparts( f, outptr, 0, max ); + modsout_report_unused_tags( f, p, numrefs ); + + fprintf( outptr, "</mods>\n" ); + fflush( outptr ); + + return BIBL_OK; +} + +/***************************************************** + PUBLIC: int modsout_writeheader() +*****************************************************/ + +static void +modsout_writeheader( FILE *outptr, param *p ) +{ + if ( p->utf8bom ) utf8_writebom( outptr ); + fprintf(outptr,"<?xml version=\"1.0\" encoding=\"%s\"?>\n", + charset_get_xmlname( p->charsetout ) ); + fprintf(outptr,"<modsCollection xmlns=\"http://www.loc.gov/mods/v3\">\n"); +} + +/***************************************************** + PUBLIC: int modsout_writefooter() +*****************************************************/ + +static void +modsout_writefooter( FILE *outptr ) +{ + fprintf(outptr,"</modsCollection>\n"); + fflush( outptr ); +} + |