1 /*
2 * modsout.c
3 *
4 * Copyright (c) Chris Putnam 2003-2020
5 *
6 * Source code released under the GPL version 2
7 *
8 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include "is_ws.h"
14 #include "str.h"
15 #include "charsets.h"
16 #include "str_conv.h"
17 #include "fields.h"
18 #include "iso639_2.h"
19 #include "utf8.h"
20 #include "modstypes.h"
21 #include "bu_auth.h"
22 #include "marc_auth.h"
23 #include "bibformats.h"
24
25 /*****************************************************
26 PUBLIC: int modsout_initparams()
27 *****************************************************/
28
29 static void modsout_writeheader( FILE *outptr, param *p );
30 static void modsout_writefooter( FILE *outptr );
31 static int modsout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs );
32
33 int
modsout_initparams(param * pm,const char * progname)34 modsout_initparams( param *pm, const char *progname )
35 {
36 pm->writeformat = BIBL_MODSOUT;
37 pm->format_opts = 0;
38 pm->charsetout = BIBL_CHARSET_UNICODE;
39 pm->charsetout_src = BIBL_SRC_DEFAULT;
40 pm->latexout = 0;
41 pm->utf8out = 1;
42 pm->utf8bom = 1;
43 pm->xmlout = BIBL_XMLOUT_TRUE;
44 pm->nosplittitle = 0;
45 pm->verbose = 0;
46 pm->addcount = 0;
47 pm->singlerefperfile = 0;
48
49 pm->headerf = modsout_writeheader;
50 pm->footerf = modsout_writefooter;
51 pm->assemblef = NULL;
52 pm->writef = modsout_write;
53
54 if ( !pm->progname ) {
55 if ( !progname ) pm->progname = NULL;
56 else {
57 pm->progname = strdup( progname );
58 if ( !pm->progname ) return BIBL_ERR_MEMERR;
59 }
60 }
61
62 return BIBL_OK;
63 }
64
65 /*****************************************************
66 PUBLIC: int modsout_write()
67 *****************************************************/
68
69 /* output_tag()
70 *
71 * mode = TAG_OPEN, "<tag>"
72 * mode = TAG_CLOSE, "</tag>"
73 * mode = TAG_OPENCLOSE, "<tag>data</tag>"
74 * mode = TAG_SELFCLOSE, "<tag/>"
75 *
76 * newline = TAG_NONEWLINE, "<tag>"
77 * newline = TAG_NEWLINE, "<tag>\n"
78 *
79 */
80 #define TAG_OPEN (0)
81 #define TAG_CLOSE (1)
82 #define TAG_OPENCLOSE (2)
83 #define TAG_SELFCLOSE (3)
84
85 #define TAG_NONEWLINE (0)
86 #define TAG_NEWLINE (1)
87
88 static void
output_tag_core(FILE * outptr,int nindents,char * tag,char * data,unsigned char mode,unsigned char newline,va_list * attrs)89 output_tag_core( FILE *outptr, int nindents, char *tag, char *data, unsigned char mode, unsigned char newline, va_list *attrs )
90 {
91 char *attr, *val;
92 int i;
93
94 for ( i=0; i<nindents; ++i ) fprintf( outptr, " " );
95
96 if ( mode!=TAG_CLOSE )
97 fprintf( outptr, "<" );
98 else
99 fprintf( outptr, "</" );
100
101 fprintf( outptr, "%s", tag );
102
103 do {
104 attr = va_arg( *attrs, char * );
105 if ( attr ) val = va_arg( *attrs, char * );
106 if ( attr && val )
107 fprintf( outptr, " %s=\"%s\"", attr, val );
108 } while ( attr && val );
109
110 if ( mode!=TAG_SELFCLOSE )
111 fprintf( outptr, ">" );
112 else
113 fprintf( outptr, "/>" );
114
115 if ( mode==TAG_OPENCLOSE ) {
116 fprintf( outptr, "%s</%s>", data, tag );
117 }
118
119 if ( newline==TAG_NEWLINE )
120 fprintf( outptr, "\n" );
121 }
122
123 /* output_tag()
124 *
125 * output XML tag
126 *
127 * mode = [ TAG_OPEN | TAG_CLOSE | TAG_OPENCLOSE | TAG_SELFCLOSE ]
128 * newline = [ TAG_NEWLINE | TAG_NONEWLINE ]
129 *
130 * for mode TAG_OPENCLOSE, ensure that value is non-NULL, as string pointed to by value
131 * will be output in the tag
132 */
133 static void
output_tag(FILE * outptr,int nindents,char * tag,char * value,unsigned char mode,unsigned char newline,...)134 output_tag( FILE *outptr, int nindents, char *tag, char *value, unsigned char mode, unsigned char newline, ... )
135 {
136 va_list attrs;
137
138 va_start( attrs, newline );
139 output_tag_core( outptr, nindents, tag, value, mode, newline, &attrs );
140 va_end( attrs );
141 }
142
143 /* output_fil()
144 *
145 * output XML tag, but lookup data in fields struct
146 *
147 * mode = [ TAG_OPEN | TAG_CLOSE | TAG_OPENCLOSE | TAG_SELFCLOSE ]
148 * newline = [ TAG_NEWLINE | TAG_NONEWLINE ]
149 *
150 * value looked up in fields will only be used in mode TAG_OPENCLOSE
151 */
152 static void
output_fil(FILE * outptr,int nindents,char * tag,fields * f,int n,unsigned char mode,unsigned char newline,...)153 output_fil( FILE *outptr, int nindents, char *tag, fields *f, int n, unsigned char mode, unsigned char newline, ... )
154 {
155 va_list attrs;
156 char *value;
157
158 if ( n!=-1 ) {
159 value = (char *) fields_value( f, n, FIELDS_CHRP );
160 va_start( attrs, newline );
161 output_tag_core( outptr, nindents, tag, value, mode, newline, &attrs );
162 va_end( attrs );
163 }
164 }
165
166 static inline int
lvl2indent(int level)167 lvl2indent( int level )
168 {
169 if ( level < -1 ) return -level + 1;
170 else return level + 1;
171 }
172
173 static inline int
incr_level(int level,int amt)174 incr_level( int level, int amt )
175 {
176 if ( level > -1 ) return level+amt;
177 else return level-amt;
178 }
179
180 /* convert_findallfields()
181 *
182 * Find the positions of all convert.internal tags in the fields
183 * structure and store the locations in convert.pos element.
184 *
185 * Return number of the tags found.
186 */
187 static int
convert_findallfields(fields * f,convert * parts,int nparts,int level)188 convert_findallfields( fields *f, convert *parts, int nparts, int level )
189 {
190 int i, n = 0;
191
192 for ( i=0; i<nparts; ++i ) {
193 parts[i].pos = fields_find( f, parts[i].internal, level );
194 n += ( parts[i].pos!=FIELDS_NOTFOUND );
195 }
196
197 return n;
198 }
199
200 static void
output_title(fields * f,FILE * outptr,int level)201 output_title( fields *f, FILE *outptr, int level )
202 {
203 int ttl = fields_find( f, "TITLE", level );
204 int subttl = fields_find( f, "SUBTITLE", level );
205 int shrttl = fields_find( f, "SHORTTITLE", level );
206 int parttl = fields_find( f, "PARTTITLE", level );
207 char *val;
208
209 output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
210 output_fil( outptr, lvl2indent(incr_level(level,1)), "title", f, ttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
211 output_fil( outptr, lvl2indent(incr_level(level,1)), "subTitle", f, subttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
212 output_fil( outptr, lvl2indent(incr_level(level,1)), "partName", f, parttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
213 /* MODS output doesn't verify if we don't at least have a <title/> element */
214 if ( ttl==-1 && subttl==-1 )
215 output_tag( outptr, lvl2indent(incr_level(level,1)), "title", NULL, TAG_SELFCLOSE, TAG_NEWLINE, NULL );
216 output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
217
218 /* output shorttitle if it's different from normal title */
219 if ( shrttl!=FIELDS_NOTFOUND ) {
220 val = (char *) fields_value( f, shrttl, FIELDS_CHRP );
221 if ( ttl==FIELDS_NOTFOUND || subttl!=FIELDS_NOTFOUND || strcmp(fields_value(f,ttl,FIELDS_CHRP),val) ) {
222 output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_OPEN, TAG_NEWLINE, "type", "abbreviated", NULL );
223 output_tag( outptr, lvl2indent(incr_level(level,1)), "title", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
224 output_tag( outptr, lvl2indent(level), "titleInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
225 }
226 }
227 }
228
229 static void
output_name(FILE * outptr,char * p,int level)230 output_name( FILE *outptr, char *p, int level )
231 {
232 str family, part, suffix;
233 int n=0;
234
235 strs_init( &family, &part, &suffix, NULL );
236
237 while ( *p && *p!='|' ) str_addchar( &family, *p++ );
238 if ( *p=='|' ) p++;
239
240 while ( *p ) {
241 while ( *p && *p!='|' ) str_addchar( &part, *p++ );
242 /* truncate periods from "A. B. Jones" names */
243 if ( part.len ) {
244 if ( part.len==2 && part.data[1]=='.' ) {
245 part.len=1;
246 part.data[1]='\0';
247 }
248 if ( n==0 )
249 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL );
250 output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", part.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "given", NULL );
251 n++;
252 }
253 if ( *p=='|' ) {
254 p++;
255 if ( *p=='|' ) {
256 p++;
257 while ( *p && *p!='|' ) str_addchar( &suffix, *p++ );
258 }
259 str_empty( &part );
260 }
261 }
262
263 if ( family.len ) {
264 if ( n==0 )
265 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL );
266 output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", family.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "family", NULL );
267 n++;
268 }
269
270 if ( suffix.len ) {
271 if ( n==0 )
272 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL );
273 output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", suffix.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "suffix", NULL );
274 }
275
276 strs_free( &part, &family, &suffix, NULL );
277 }
278
279
280 /* MODS v 3.4
281 *
282 * <name [type="corporation"/type="conference"]>
283 * <namePart></namePart>
284 * <displayForm></displayForm>
285 * <affiliation></affiliation>
286 * <role>
287 * <roleTerm [authority="marcrealtor"] type="text"></roleTerm>
288 * </role>
289 * <description></description>
290 * </name>
291 */
292
293 #define NO_AUTHORITY (0)
294 #define MARC_AUTHORITY (1)
295
296 static void
output_names(fields * f,FILE * outptr,int level)297 output_names( fields *f, FILE *outptr, int level )
298 {
299 convert names[] = {
300 { "author", "AUTHOR", 0, MARC_AUTHORITY },
301 { "editor", "EDITOR", 0, MARC_AUTHORITY },
302 { "annotator", "ANNOTATOR", 0, MARC_AUTHORITY },
303 { "artist", "ARTIST", 0, MARC_AUTHORITY },
304 { "author", "2ND_AUTHOR", 0, MARC_AUTHORITY },
305 { "author", "3RD_AUTHOR", 0, MARC_AUTHORITY },
306 { "author", "SUB_AUTHOR", 0, MARC_AUTHORITY },
307 { "author", "COMMITTEE", 0, MARC_AUTHORITY },
308 { "author", "COURT", 0, MARC_AUTHORITY },
309 { "author", "LEGISLATIVEBODY", 0, MARC_AUTHORITY },
310 { "author of afterword, colophon, etc.", "AFTERAUTHOR", 0, MARC_AUTHORITY },
311 { "author of introduction, etc.", "INTROAUTHOR", 0, MARC_AUTHORITY },
312 { "cartographer", "CARTOGRAPHER", 0, MARC_AUTHORITY },
313 { "collaborator", "COLLABORATOR", 0, MARC_AUTHORITY },
314 { "commentator", "COMMENTATOR", 0, MARC_AUTHORITY },
315 { "compiler", "COMPILER", 0, MARC_AUTHORITY },
316 { "degree grantor", "DEGREEGRANTOR", 0, MARC_AUTHORITY },
317 { "director", "DIRECTOR", 0, MARC_AUTHORITY },
318 { "event", "EVENT", 0, NO_AUTHORITY },
319 { "inventor", "INVENTOR", 0, MARC_AUTHORITY },
320 { "organizer of meeting", "ORGANIZER", 0, MARC_AUTHORITY },
321 { "patent holder", "ASSIGNEE", 0, MARC_AUTHORITY },
322 { "performer", "PERFORMER", 0, MARC_AUTHORITY },
323 { "producer", "PRODUCER", 0, MARC_AUTHORITY },
324 { "addressee", "ADDRESSEE", 0, MARC_AUTHORITY },
325 { "redactor", "REDACTOR", 0, MARC_AUTHORITY },
326 { "reporter", "REPORTER", 0, MARC_AUTHORITY },
327 { "sponsor", "SPONSOR", 0, MARC_AUTHORITY },
328 { "translator", "TRANSLATOR", 0, MARC_AUTHORITY },
329 { "writer", "WRITER", 0, MARC_AUTHORITY },
330 };
331 int i, n, nfields, ntypes = sizeof( names ) / sizeof( convert );
332 int f_asis, f_corp, f_conf;
333 str role;
334
335 str_init( &role );
336 nfields = fields_num( f );
337 for ( n=0; n<ntypes; ++n ) {
338 for ( i=0; i<nfields; ++i ) {
339 if ( fields_level( f, i )!=level ) continue;
340 if ( fields_no_value( f, i ) ) continue;
341 f_asis = f_corp = f_conf = 0;
342 str_strcpyc( &role, f->tag[i].data );
343 if ( str_findreplace( &role, ":ASIS", "" )) f_asis=1;
344 if ( str_findreplace( &role, ":CORP", "" )) f_corp=1;
345 if ( str_findreplace( &role, ":CONF", "" )) f_conf=1;
346 if ( strcasecmp( role.data, names[n].internal ) )
347 continue;
348 if ( f_asis ) {
349 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
350 output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
351 } else if ( f_corp ) {
352 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "corporate", NULL );
353 output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
354 } else if ( f_conf ) {
355 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "conference", NULL );
356 output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
357 } else {
358 output_name(outptr, fields_value( f, i, FIELDS_CHRP ), level);
359 }
360 output_tag( outptr, lvl2indent(incr_level(level,1)), "role", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
361 if ( names[n].code & MARC_AUTHORITY )
362 output_tag( outptr, lvl2indent(incr_level(level,2)), "roleTerm", names[n].mods, TAG_OPENCLOSE, TAG_NEWLINE, "authority", "marcrelator", "type", "text", NULL );
363 else
364 output_tag( outptr, lvl2indent(incr_level(level,2)), "roleTerm", names[n].mods, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL );
365 output_tag( outptr, lvl2indent(incr_level(level,1)), "role", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
366 output_tag( outptr, lvl2indent(level), "name", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
367 fields_set_used( f, i );
368 }
369 }
370 str_free( &role );
371 }
372
373 /* datepos[ NUM_DATE_TYPES ]
374 * use define to ensure that the array and loops don't get out of sync
375 * datepos[0] -> DATE:YEAR/PARTDATE:YEAR
376 * datepos[1] -> DATE:MONTH/PARTDATE:MONTH
377 * datepos[2] -> DATE:DAY/PARTDATE:DAY
378 * datepos[3] -> DATE/PARTDATE
379 */
380 #define DATE_YEAR (0)
381 #define DATE_MONTH (1)
382 #define DATE_DAY (2)
383 #define DATE_ALL (3)
384 #define NUM_DATE_TYPES (4)
385
386 static int
find_datepos(fields * f,int level,unsigned char use_altnames,int datepos[NUM_DATE_TYPES])387 find_datepos( fields *f, int level, unsigned char use_altnames, int datepos[NUM_DATE_TYPES] )
388 {
389 char *src_names[] = { "DATE:YEAR", "DATE:MONTH", "DATE:DAY", "DATE" };
390 char *alt_names[] = { "PARTDATE:YEAR", "PARTDATE:MONTH", "PARTDATE:DAY", "PARTDATE" };
391 int found = 0;
392 int i;
393
394 for ( i=0; i<NUM_DATE_TYPES; ++i ) {
395 if ( !use_altnames )
396 datepos[i] = fields_find( f, src_names[i], level );
397 else
398 datepos[i] = fields_find( f, alt_names[i], level );
399 if ( datepos[i]!=FIELDS_NOTFOUND ) found = 1;
400 }
401
402 return found;
403 }
404
405 /* find_dateinfo()
406 *
407 * fill datepos[] array with position indexes to date information in fields *f
408 *
409 * when generating dates for LEVEL_MAIN, first look at level=LEVEL_MAIN, but if that
410 * fails, use LEVEL_ANY (-1)
411 *
412 * returns 1 if date information found, 0 otherwise
413 */
414 static int
find_dateinfo(fields * f,int level,int datepos[NUM_DATE_TYPES])415 find_dateinfo( fields *f, int level, int datepos[ NUM_DATE_TYPES ] )
416 {
417 int found;
418
419 /* default to finding date information for the current level */
420 found = find_datepos( f, level, 0, datepos );
421
422 /* for LEVEL_MAIN, do whatever it takes to find a date */
423 if ( !found && level == LEVEL_MAIN ) {
424 found = find_datepos( f, -1, 0, datepos );
425 }
426 if ( !found && level == LEVEL_MAIN ) {
427 found = find_datepos( f, -1, 1, datepos );
428 }
429
430 return found;
431 }
432
433 static void
output_datepieces(fields * f,FILE * outptr,int pos[NUM_DATE_TYPES])434 output_datepieces( fields *f, FILE *outptr, int pos[ NUM_DATE_TYPES ] )
435 {
436 str *s;
437 int i;
438
439 for ( i=0; i<3 && pos[i]!=-1; ++i ) {
440 if ( i>0 ) fprintf( outptr, "-" );
441 /* zero pad month or days written as "1", "2", "3" ... */
442 if ( i==DATE_MONTH || i==DATE_DAY ) {
443 s = fields_value( f, pos[i], FIELDS_STRP_NOUSE );
444 if ( s->len==1 ) {
445 fprintf( outptr, "0" );
446 }
447 }
448 fprintf( outptr, "%s", (char *) fields_value( f, pos[i], FIELDS_CHRP ) );
449 }
450 }
451
452 static void
output_dateissued(fields * f,FILE * outptr,int level,int pos[NUM_DATE_TYPES])453 output_dateissued( fields *f, FILE *outptr, int level, int pos[ NUM_DATE_TYPES ] )
454 {
455 output_tag( outptr, lvl2indent(incr_level(level,1)), "dateIssued", NULL, TAG_OPEN, TAG_NONEWLINE, NULL );
456 if ( pos[ DATE_YEAR ]!=-1 || pos[ DATE_MONTH ]!=-1 || pos[ DATE_DAY ]!=-1 ) {
457 output_datepieces( f, outptr, pos );
458 } else {
459 fprintf( outptr, "%s", (char *) fields_value( f, pos[ DATE_ALL ], FIELDS_CHRP ) );
460 }
461 fprintf( outptr, "</dateIssued>\n" );
462 }
463
464 static void
output_origin(fields * f,FILE * outptr,int level)465 output_origin( fields *f, FILE *outptr, int level )
466 {
467 convert parts[] = {
468 { "issuance", "ISSUANCE", 0, 0 },
469 { "publisher", "PUBLISHER", 0, 0 },
470 { "place", "ADDRESS", 0, 1 },
471 { "place", "ADDRESS:PUBLISHER", 0, 0 },
472 { "place", "ADDRESS:AUTHOR", 0, 0 },
473 { "edition", "EDITION", 0, 0 },
474 { "dateCaptured", "URLDATE", 0, 0 }
475 };
476 int nparts = sizeof( parts ) / sizeof( parts[0] );
477 int i, found, datefound, datepos[ NUM_DATE_TYPES ];
478
479 found = convert_findallfields( f, parts, nparts, level );
480 datefound = find_dateinfo( f, level, datepos );
481 if ( !found && !datefound ) return;
482
483
484 output_tag( outptr, lvl2indent(level), "originInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
485
486 /* issuance must precede date */
487 if ( parts[0].pos!=-1 )
488 output_fil( outptr, lvl2indent(incr_level(level,1)), "issuance", f, parts[0].pos, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
489
490 /* date */
491 if ( datefound )
492 output_dateissued( f, outptr, level, datepos );
493
494 /* rest of the originInfo elements */
495 for ( i=1; i<nparts; i++ ) {
496
497 /* skip missing originInfo elements */
498 if ( parts[i].pos==-1 ) continue;
499
500 /* normal originInfo element */
501 if ( parts[i].code==0 ) {
502 output_fil( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, f, parts[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
503 }
504
505 /* originInfo with placeTerm info */
506 else {
507 output_tag( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, NULL, TAG_OPEN, TAG_NEWLINE, NULL );
508 output_fil( outptr, lvl2indent(incr_level(level,2)), "placeTerm", f, parts[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL );
509 output_tag( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
510 }
511 }
512
513 output_tag( outptr, lvl2indent(level), "originInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
514 }
515
516 /* output_language_core()
517 *
518 * generates language output for tag="langauge" or tag="languageOfCataloging"
519 * if possible, outputs iso639-2b code for the language
520 *
521 * <language>
522 * <languageTerm type="text">xxx</languageTerm>
523 * </language>
524 *
525 * <language>
526 * <languageTerm type="text">xxx</languageTerm>
527 * <languageTerm type="code" authority="iso639-2b">xxx</languageTerm>
528 * </language>
529 *
530 */
531 static void
output_language_core(fields * f,int n,FILE * outptr,char * tag,int level)532 output_language_core( fields *f, int n, FILE *outptr, char *tag, int level )
533 {
534 char *lang, *code;
535
536 lang = (char *) fields_value( f, n, FIELDS_CHRP );
537 code = iso639_2_from_language( lang );
538
539 output_tag( outptr, lvl2indent(level), tag, NULL, TAG_OPEN, TAG_NEWLINE, NULL );
540 output_tag( outptr, lvl2indent(incr_level(level,1)), "languageTerm", lang, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL );
541 if ( code ) {
542 output_tag( outptr, lvl2indent(incr_level(level,1)), "languageTerm", code, TAG_OPENCLOSE, TAG_NEWLINE, "type", "code", "authority", "iso639-2b", NULL );
543 }
544 output_tag( outptr, lvl2indent(level), tag, NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
545 }
546
547 static void
output_language(fields * f,FILE * outptr,int level)548 output_language( fields *f, FILE *outptr, int level )
549 {
550 int n;
551 n = fields_find( f, "LANGUAGE", level );
552 if ( n!=FIELDS_NOTFOUND )
553 output_language_core( f, n, outptr, "language", level );
554 }
555
556 static void
output_description(fields * f,FILE * outptr,int level)557 output_description( fields *f, FILE *outptr, int level )
558 {
559 char *val;
560 int n;
561
562 n = fields_find( f, "DESCRIPTION", level );
563 if ( n!=FIELDS_NOTFOUND ) {
564 val = ( char * ) fields_value( f, n, FIELDS_CHRP );
565 output_tag( outptr, lvl2indent(level), "physicalDescription", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
566 output_tag( outptr, lvl2indent(incr_level(level,1)), "note", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
567 output_tag( outptr, lvl2indent(level), "physicalDescription", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
568 }
569 }
570
571 static void
output_toc(fields * f,FILE * outptr,int level)572 output_toc( fields *f, FILE *outptr, int level )
573 {
574 char *val;
575 int n;
576
577 n = fields_find( f, "CONTENTS", level );
578 if ( n!=FIELDS_NOTFOUND ) {
579 val = (char *) fields_value( f, n, FIELDS_CHRP );
580 output_tag( outptr, lvl2indent(level), "tableOfContents", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
581 }
582 }
583
584 /* detail output
585 *
586 * for example:
587 *
588 * <detail type="volume"><number>xxx</number></detail
589 */
590 static void
mods_output_detail(fields * f,FILE * outptr,int n,char * item_name,int level)591 mods_output_detail( fields *f, FILE *outptr, int n, char *item_name, int level )
592 {
593 if ( n!=-1 ) {
594 output_tag( outptr, lvl2indent(incr_level(level,1)), "detail", NULL, TAG_OPEN, TAG_NONEWLINE, "type", item_name, NULL );
595 output_fil( outptr, 0, "number", f, n, TAG_OPENCLOSE, TAG_NONEWLINE, NULL );
596 output_tag( outptr, 0, "detail", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
597 }
598 }
599
600
601 /* extents output
602 *
603 * <extent unit="page">
604 * <start>xxx</start>
605 * <end>xxx</end>
606 * </extent>
607 */
608 static void
mods_output_extents(fields * f,FILE * outptr,int start,int end,int total,char * extype,int level)609 mods_output_extents( fields *f, FILE *outptr, int start, int end, int total, char *extype, int level )
610 {
611 char *val;
612
613 output_tag( outptr, lvl2indent(incr_level(level,1)), "extent", NULL, TAG_OPEN, TAG_NEWLINE, "unit", extype, NULL );
614 if ( start!=-1 ) {
615 val = (char *) fields_value( f, start, FIELDS_CHRP );
616 output_tag( outptr, lvl2indent(incr_level(level,2)), "start", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
617 }
618 if ( end!=-1 ) {
619 val = (char *) fields_value( f, end, FIELDS_CHRP );
620 output_tag( outptr, lvl2indent(incr_level(level,2)), "end", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
621 }
622 if ( total!=-1 ) {
623 val = (char *) fields_value( f, total, FIELDS_CHRP );
624 output_tag( outptr, lvl2indent(incr_level(level,2)), "total", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
625 }
626 output_tag( outptr, lvl2indent(incr_level(level,1)), "extent", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
627 }
628
629 static void
try_output_partheader(FILE * outptr,int wrote_header,int level)630 try_output_partheader( FILE *outptr, int wrote_header, int level )
631 {
632 if ( !wrote_header )
633 output_tag( outptr, lvl2indent(level), "part", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
634 }
635
636 static void
try_output_partfooter(FILE * outptr,int wrote_header,int level)637 try_output_partfooter( FILE *outptr, int wrote_header, int level )
638 {
639 if ( wrote_header )
640 output_tag( outptr, lvl2indent(level), "part", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
641 }
642
643 /* part date output
644 *
645 * <date>xxxx-xx-xx</date>
646 *
647 */
648 static int
output_partdate(fields * f,FILE * outptr,int level,int wrote_header)649 output_partdate( fields *f, FILE *outptr, int level, int wrote_header )
650 {
651 convert parts[] = {
652 { "", "PARTDATE:YEAR", 0, 0 },
653 { "", "PARTDATE:MONTH", 0, 0 },
654 { "", "PARTDATE:DAY", 0, 0 },
655 };
656 int nparts = sizeof(parts)/sizeof(parts[0]);
657
658 if ( !convert_findallfields( f, parts, nparts, level ) ) return 0;
659
660 try_output_partheader( outptr, wrote_header, level );
661
662 output_tag( outptr, lvl2indent(incr_level(level,1)), "date", NULL, TAG_OPEN, TAG_NONEWLINE, NULL );
663
664 if ( parts[0].pos!=-1 ) {
665 fprintf( outptr, "%s", (char *) fields_value( f, parts[0].pos, FIELDS_CHRP ) );
666 } else fprintf( outptr, "XXXX" );
667
668 if ( parts[1].pos!=-1 ) {
669 fprintf( outptr, "-%s", (char *) fields_value( f, parts[1].pos, FIELDS_CHRP ) );
670 }
671
672 if ( parts[2].pos!=-1 ) {
673 if ( parts[1].pos==-1 )
674 fprintf( outptr, "-XX" );
675 fprintf( outptr, "-%s", (char *) fields_value( f, parts[2].pos, FIELDS_CHRP ) );
676 }
677
678 fprintf( outptr,"</date>\n");
679
680 return 1;
681 }
682
683 static int
output_partpages(fields * f,FILE * outptr,int level,int wrote_header)684 output_partpages( fields *f, FILE *outptr, int level, int wrote_header )
685 {
686 convert parts[] = {
687 { "", "PAGES:START", 0, 0 },
688 { "", "PAGES:STOP", 0, 0 },
689 { "", "PAGES", 0, 0 },
690 { "", "PAGES:TOTAL", 0, 0 }
691 };
692 int nparts = sizeof(parts)/sizeof(parts[0]);
693
694 if ( !convert_findallfields( f, parts, nparts, level ) ) return 0;
695
696 try_output_partheader( outptr, wrote_header, level );
697
698 /* If PAGES:START or PAGES:STOP are undefined */
699 if ( parts[0].pos==-1 || parts[1].pos==-1 ) {
700 if ( parts[0].pos!=-1 )
701 mods_output_detail( f, outptr, parts[0].pos, "page", level );
702 if ( parts[1].pos!=-1 )
703 mods_output_detail( f, outptr, parts[1].pos, "page", level );
704 if ( parts[2].pos!=-1 )
705 mods_output_detail( f, outptr, parts[2].pos, "page", level );
706 if ( parts[3].pos!=-1 )
707 mods_output_extents( f, outptr, -1, -1, parts[3].pos, "page", level );
708 }
709 /* If both PAGES:START and PAGES:STOP are defined */
710 else {
711 mods_output_extents( f, outptr, parts[0].pos, parts[1].pos, parts[3].pos, "page", level );
712 }
713
714 return 1;
715 }
716
717 static int
output_partelement(fields * f,FILE * outptr,int level,int wrote_header)718 output_partelement( fields *f, FILE *outptr, int level, int wrote_header )
719 {
720 convert parts[] = {
721 { "", "NUMVOLUMES", 0, 0 },
722 { "volume", "VOLUME", 0, 0 },
723 { "section", "SECTION", 0, 0 },
724 { "issue", "ISSUE", 0, 0 },
725 { "number", "NUMBER", 0, 0 },
726 { "publiclawnumber", "PUBLICLAWNUMBER", 0, 0 },
727 { "session", "SESSION", 0, 0 },
728 { "articlenumber", "ARTICLENUMBER", 0, 0 },
729 { "part", "PART", 0, 0 },
730 { "chapter", "CHAPTER", 0, 0 },
731 { "report number", "REPORTNUMBER", 0, 0 },
732 };
733 int i, nparts = sizeof( parts ) / sizeof( convert );
734
735 if ( !convert_findallfields( f, parts, nparts, level ) ) return 0;
736
737 try_output_partheader( outptr, wrote_header, level );
738
739 /* start loop at 1 to skip NUMVOLUMES */
740 for ( i=1; i<nparts; ++i ) {
741 if ( parts[i].pos==-1 ) continue;
742 mods_output_detail( f, outptr, parts[i].pos, parts[i].mods, level );
743 }
744
745 if ( parts[0].pos!=-1 )
746 mods_output_extents( f, outptr, -1, -1, parts[0].pos, "volumes", level );
747
748 return 1;
749 }
750
751 static void
output_part(fields * f,FILE * outptr,int level)752 output_part( fields *f, FILE *outptr, int level )
753 {
754 int wrote_hdr;
755 wrote_hdr = output_partdate( f, outptr, level, 0 );
756 wrote_hdr += output_partelement( f, outptr, level, wrote_hdr );
757 wrote_hdr += output_partpages( f, outptr, level, wrote_hdr );
758 try_output_partfooter( outptr, wrote_hdr, level );
759 }
760
761 static void
output_recordInfo(fields * f,FILE * outptr,int level)762 output_recordInfo( fields *f, FILE *outptr, int level )
763 {
764 int n;
765 n = fields_find( f, "LANGCATALOG", level );
766 if ( n!=FIELDS_NOTFOUND ) {
767 output_tag( outptr, lvl2indent(level), "recordInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
768 output_language_core( f, n, outptr, "languageOfCataloging", incr_level(level,1) );
769 output_tag( outptr, lvl2indent(level), "recordInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
770 }
771 }
772
773 /* output_genre()
774 *
775 * <genre authority="marcgt">thesis</genre>
776 * <genre authority="bibutilsgt">Diploma thesis</genre>
777 */
778 static void
output_genre(fields * f,FILE * outptr,int level)779 output_genre( fields *f, FILE *outptr, int level )
780 {
781 char *value, *attr = NULL, *attrvalue = NULL;
782 int i, n;
783
784 n = fields_num( f );
785 for ( i=0; i<n; ++i ) {
786 if ( fields_level( f, i ) != level ) continue;
787 if ( !fields_match_tag( f, i, "GENRE:MARC" ) && !fields_match_tag( f, i, "GENRE:BIBUTILS" ) && !fields_match_tag( f, i, "GENRE:UNKNOWN" ) ) continue;
788 value = fields_value( f, i, FIELDS_CHRP );
789 if ( is_marc_genre( value ) ) {
790 attr = "authority";
791 attrvalue = "marcgt";
792 }
793 else if ( is_bu_genre( value ) ) {
794 attr = "authority";
795 attrvalue = "bibutilsgt";
796 }
797 output_tag( outptr, lvl2indent(level), "genre", value, TAG_OPENCLOSE, TAG_NEWLINE, attr, attrvalue, NULL );
798 }
799 }
800
801 /* output_resource()
802 *
803 * <typeOfResource>text</typeOfResource>
804 */
805 static void
output_resource(fields * f,FILE * outptr,int level)806 output_resource( fields *f, FILE *outptr, int level )
807 {
808 char *value;
809 int n;
810
811 n = fields_find( f, "RESOURCE", level );
812 if ( n!=FIELDS_NOTFOUND ) {
813 value = fields_value( f, n, FIELDS_CHRP );
814 if ( is_marc_resource( value ) ) {
815 output_fil( outptr, lvl2indent(level), "typeOfResource", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
816 } else {
817 // Patch: Disable output logging
818 }
819 }
820 }
821
822 static void
output_type(fields * f,FILE * outptr,int level)823 output_type( fields *f, FILE *outptr, int level )
824 {
825 int n;
826
827 /* silence warnings about INTERNAL_TYPE being unused */
828 n = fields_find( f, "INTERNAL_TYPE", LEVEL_MAIN );
829 if ( n!=FIELDS_NOTFOUND ) fields_set_used( f, n );
830
831 output_resource( f, outptr, level );
832 output_genre( f, outptr, level );
833 }
834
835 /* output_abs()
836 *
837 * <abstract>xxxx</abstract>
838 */
839 static void
output_abs(fields * f,FILE * outptr,int level)840 output_abs( fields *f, FILE *outptr, int level )
841 {
842 int n;
843
844 n = fields_find( f, "ABSTRACT", level );
845 output_fil( outptr, lvl2indent(level), "abstract", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
846 }
847
848 static void
output_notes(fields * f,FILE * outptr,int level)849 output_notes( fields *f, FILE *outptr, int level )
850 {
851 int i, n;
852 char *t;
853
854 n = fields_num( f );
855 for ( i=0; i<n; ++i ) {
856 if ( fields_level( f, i ) != level ) continue;
857 t = fields_tag( f, i, FIELDS_CHRP_NOUSE );
858 if ( !strcasecmp( t, "NOTES" ) )
859 output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
860 else if ( !strcasecmp( t, "PUBSTATE" ) )
861 output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "publication status", NULL );
862 else if ( !strcasecmp( t, "ANNOTE" ) )
863 output_fil( outptr, lvl2indent(level), "bibtex-annote", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
864 else if ( !strcasecmp( t, "TIMESCITED" ) )
865 output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "times cited", NULL );
866 else if ( !strcasecmp( t, "ANNOTATION" ) )
867 output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "annotation", NULL );
868 else if ( !strcasecmp( t, "ADDENDUM" ) )
869 output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "addendum", NULL );
870 else if ( !strcasecmp( t, "BIBKEY" ) )
871 output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "bibliography key", NULL );
872 }
873 }
874
875 /* output_key()
876 *
877 * <subject>
878 * <topic>xxxx</topic>
879 * </subject>
880 */
881 static void
output_key(fields * f,FILE * outptr,int level)882 output_key( fields *f, FILE *outptr, int level )
883 {
884 int i, n;
885
886 n = fields_num( f );
887 for ( i=0; i<n; ++i ) {
888 if ( fields_level( f, i ) != level ) continue;
889 if ( !strcasecmp( f->tag[i].data, "KEYWORD" ) ) {
890 output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
891 output_fil( outptr, lvl2indent(incr_level(level,1)), "topic", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
892 output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
893 }
894 else if ( !strcasecmp( f->tag[i].data, "EPRINTCLASS" ) ) {
895 output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
896 output_fil( outptr, lvl2indent(incr_level(level,1)), "topic", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "class", "primary", NULL );
897 output_tag( outptr, lvl2indent(level), "subject", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
898 }
899 }
900 }
901
902 static void
output_sn(fields * f,FILE * outptr,int level)903 output_sn( fields *f, FILE *outptr, int level )
904 {
905 convert sn_types[] = {
906 { "isbn", "ISBN", 0, 0 },
907 { "isbn", "ISBN13", 0, 0 },
908 { "lccn", "LCCN", 0, 0 },
909 { "issn", "ISSN", 0, 0 },
910 { "coden", "CODEN", 0, 0 },
911 { "citekey", "REFNUM", 0, 0 },
912 { "doi", "DOI", 0, 0 },
913 { "eid", "EID", 0, 0 },
914 { "eprint", "EPRINT", 0, 0 },
915 { "eprinttype","EPRINTTYPE",0, 0 },
916 { "pubmed", "PMID", 0, 0 },
917 { "MRnumber", "MRNUMBER", 0, 0 },
918 { "medline", "MEDLINE", 0, 0 },
919 { "pii", "PII", 0, 0 },
920 { "pmc", "PMC", 0, 0 },
921 { "arXiv", "ARXIV", 0, 0 },
922 { "isi", "ISIREFNUM", 0, 0 },
923 { "accessnum", "ACCESSNUM", 0, 0 },
924 { "jstor", "JSTOR", 0, 0 },
925 { "isrn", "ISRN", 0, 0 },
926 };
927 int ntypes = sizeof( sn_types ) / sizeof( sn_types[0] );
928 int i, n, found;
929
930 /* output call number */
931 n = fields_find( f, "CALLNUMBER", level );
932 output_fil( outptr, lvl2indent(level), "classification", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
933
934 /* output specialized serialnumber */
935 found = convert_findallfields( f, sn_types, ntypes, level );
936 if ( found ) {
937 for ( i=0; i<ntypes; ++i ) {
938 if ( sn_types[i].pos==-1 ) continue;
939 output_fil( outptr, lvl2indent(level), "identifier", f, sn_types[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", sn_types[i].mods, NULL );
940 }
941 }
942
943 /* output _all_ elements of type SERIALNUMBER */
944 n = fields_num( f );
945 for ( i=0; i<n; ++i ) {
946 if ( f->level[i]!=level ) continue;
947 if ( strcasecmp( f->tag[i].data, "SERIALNUMBER" ) ) continue;
948 output_fil( outptr, lvl2indent(level), "identifier", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "serial number", NULL );
949 }
950 }
951
952 /* output_url()
953 *
954 * <location>
955 * <url>URL</url>
956 * <url urlType="pdf">PDFLINK</url>
957 * <url displayLabel="Electronic full text" access="raw object">PDFLINK</url>
958 * <physicalLocation>LOCATION</physicalLocation>
959 * </location>
960 */
961 static void
output_url(fields * f,FILE * outptr,int level)962 output_url( fields *f, FILE *outptr, int level )
963 {
964 int location = fields_find( f, "LOCATION", level );
965 int url = fields_find( f, "URL", level );
966 int fileattach = fields_find( f, "FILEATTACH", level );
967 int pdflink = fields_find( f, "PDFLINK", level );
968 int i, n;
969
970 if ( url==FIELDS_NOTFOUND && location==FIELDS_NOTFOUND && pdflink==FIELDS_NOTFOUND && fileattach==FIELDS_NOTFOUND ) return;
971 output_tag( outptr, lvl2indent(level), "location", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
972
973 n = fields_num( f );
974 for ( i=0; i<n; ++i ) {
975 if ( f->level[i]!=level ) continue;
976 if ( strcasecmp( f->tag[i].data, "URL" ) ) continue;
977 output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
978 }
979 for ( i=0; i<n; ++i ) {
980 if ( f->level[i]!=level ) continue;
981 if ( strcasecmp( f->tag[i].data, "PDFLINK" ) ) continue;
982 /* output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "urlType", "pdf", NULL ); */
983 output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
984 }
985 for ( i=0; i<n; ++i ) {
986 if ( f->level[i]!=level ) continue;
987 if ( strcasecmp( f->tag[i].data, "FILEATTACH" ) ) continue;
988 output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "displayLabel", "Electronic full text", "access", "raw object", NULL );
989 }
990 if ( location!=-1 )
991 output_fil( outptr, lvl2indent(incr_level(level,1)), "physicalLocation", f, location, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
992
993 output_tag( outptr, lvl2indent(level), "location", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
994 }
995
996 /* refnum should start with a non-number and not include spaces -- ignore this */
997 static void
output_refnum(fields * f,int n,FILE * outptr)998 output_refnum( fields *f, int n, FILE *outptr )
999 {
1000 char *p = fields_value( f, n, FIELDS_CHRP_NOUSE );
1001 /* if ( p && ((*p>='0' && *p<='9') || *p=='-' || *p=='_' ))
1002 fprintf( outptr, "ref" );*/
1003 while ( p && *p ) {
1004 if ( !is_ws(*p) ) fprintf( outptr, "%c", *p );
1005 /* if ( (*p>='A' && *p<='Z') ||
1006 (*p>='a' && *p<='z') ||
1007 (*p>='0' && *p<='9') ||
1008 (*p=='-') || (*p=='
1009 (*p=='_') ) fprintf( outptr, "%c", *p );*/
1010 p++;
1011 }
1012 }
1013
1014 static void
output_head(fields * f,FILE * outptr,int dropkey,unsigned long numrefs)1015 output_head( fields *f, FILE *outptr, int dropkey, unsigned long numrefs )
1016 {
1017 int n;
1018 fprintf( outptr, "<mods");
1019 if ( !dropkey ) {
1020 n = fields_find( f, "REFNUM", LEVEL_MAIN );
1021 if ( n!=FIELDS_NOTFOUND ) {
1022 fprintf( outptr, " ID=\"");
1023 output_refnum( f, n, outptr );
1024 fprintf( outptr, "\"");
1025 }
1026 }
1027 fprintf( outptr, ">\n" );
1028 }
1029
1030 static int
original_items(fields * f,int level)1031 original_items( fields *f, int level )
1032 {
1033 int i, targetlevel, n;
1034 if ( level < 0 ) return 0;
1035 targetlevel = -( level + 2 );
1036 n = fields_num( f );
1037 for ( i=0; i<n; ++i ) {
1038 if ( fields_level( f, i ) == targetlevel )
1039 return targetlevel;
1040 }
1041 return 0;
1042 }
1043
1044 static void
output_citeparts(fields * f,FILE * outptr,int level,int max)1045 output_citeparts( fields *f, FILE *outptr, int level, int max )
1046 {
1047 int orig_level;
1048
1049 output_title( f, outptr, level );
1050 output_names( f, outptr, level );
1051 output_origin( f, outptr, level );
1052 output_type( f, outptr, level );
1053 output_language( f, outptr, level );
1054 output_description( f, outptr, level );
1055
1056 if ( level >= 0 && level < max ) {
1057 output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_OPEN, TAG_NEWLINE, "type", "host", NULL );
1058 output_citeparts( f, outptr, incr_level(level,1), max );
1059 output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
1060 }
1061 /* Look for original item things */
1062 orig_level = original_items( f, level );
1063 if ( orig_level ) {
1064 output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_OPEN, TAG_NEWLINE, "type", "original", NULL );
1065 output_citeparts( f, outptr, orig_level, max );
1066 output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
1067 }
1068 output_abs( f, outptr, level );
1069 output_notes( f, outptr, level );
1070 output_toc( f, outptr, level );
1071 output_key( f, outptr, level );
1072 output_sn( f, outptr, level );
1073 output_url( f, outptr, level );
1074 output_part( f, outptr, level );
1075
1076 output_recordInfo( f, outptr, level );
1077 }
1078
1079 static void
modsout_report_unused_tags(fields * f,param * p,unsigned long numrefs)1080 modsout_report_unused_tags( fields *f, param *p, unsigned long numrefs )
1081 {
1082 int i, n, nwritten, nunused = 0, level;
1083 char *tag, *value;
1084 n = fields_num( f );
1085 for ( i=0; i<n; ++i ) {
1086 if ( fields_used( f, i ) ) continue;
1087 nunused++;
1088 }
1089 if ( nunused ) {
1090 // Patch: Disable output logging
1091 /* Find author from level 0 */
1092 nwritten = 0;
1093 for ( i=0; i<n; ++i ) {
1094 if ( fields_level( f, i ) != 0 ) continue;
1095 tag = fields_tag( f, i, FIELDS_CHRP_NOUSE );
1096 if ( strcasecmp( tag, "AUTHOR" ) && strcasecmp( tag, "AUTHOR:ASIS" ) && strcasecmp( tag, "AUTHOR:CORP" ) ) continue;
1097 value = fields_value( f, i, FIELDS_CHRP_NOUSE );
1098 // Patch: Disable output logging
1099 nwritten++;
1100 }
1101 nwritten = 0;
1102 for ( i=0; i<n; ++i ) {
1103 if ( fields_level( f, i ) != 0 ) continue;
1104 tag = fields_tag( f, i, FIELDS_CHRP_NOUSE );
1105 if ( strcasecmp( tag, "DATE:YEAR" ) && strcasecmp( tag, "PARTDATE:YEAR" ) ) continue;
1106 value = fields_value( f, i, FIELDS_CHRP_NOUSE );
1107 // Patch: Disable output logging
1108 nwritten++;
1109 }
1110 nwritten = 0;
1111 for ( i=0; i<n; ++i ) {
1112 if ( fields_level( f, i ) != 0 ) continue;
1113 tag = fields_tag( f, i, FIELDS_CHRP_NOUSE );
1114 if ( strncasecmp( tag, "TITLE", 5 ) ) continue;
1115 value = fields_value( f, i, FIELDS_CHRP_NOUSE );
1116 // Patch: Disable output logging;
1117 nwritten++;
1118 }
1119
1120 for ( i=0; i<n; ++i ) {
1121 if ( fields_used( f, i ) ) continue;
1122 tag = fields_tag( f, i, FIELDS_CHRP_NOUSE );
1123 value = fields_value( f, i, FIELDS_CHRP_NOUSE );
1124 level = fields_level( f, i );
1125 // Patch: Disable output logging
1126 }
1127 }
1128 }
1129
1130 static int
modsout_write(fields * f,FILE * outptr,param * p,unsigned long numrefs)1131 modsout_write( fields *f, FILE *outptr, param *p, unsigned long numrefs )
1132 {
1133 int max, dropkey;
1134 max = fields_maxlevel( f );
1135 dropkey = ( p->format_opts & BIBL_FORMAT_MODSOUT_DROPKEY );
1136
1137 output_head( f, outptr, dropkey, numrefs );
1138 output_citeparts( f, outptr, 0, max );
1139 modsout_report_unused_tags( f, p, numrefs );
1140
1141 fprintf( outptr, "</mods>\n" );
1142 fflush( outptr );
1143
1144 return BIBL_OK;
1145 }
1146
1147 /*****************************************************
1148 PUBLIC: int modsout_writeheader()
1149 *****************************************************/
1150
1151 static void
modsout_writeheader(FILE * outptr,param * p)1152 modsout_writeheader( FILE *outptr, param *p )
1153 {
1154 if ( p->utf8bom ) utf8_writebom( outptr );
1155 fprintf(outptr,"<?xml version=\"1.0\" encoding=\"%s\"?>\n",
1156 charset_get_xmlname( p->charsetout ) );
1157 fprintf(outptr,"<modsCollection xmlns=\"http://www.loc.gov/mods/v3\">\n");
1158 }
1159
1160 /*****************************************************
1161 PUBLIC: int modsout_writefooter()
1162 *****************************************************/
1163
1164 static void
modsout_writefooter(FILE * outptr)1165 modsout_writefooter( FILE *outptr )
1166 {
1167 fprintf(outptr,"</modsCollection>\n");
1168 fflush( outptr );
1169 }
1170
1171