1 /*
2  * wordout.c
3  *
4  * (Word 2007 format)
5  *
6  * Copyright (c) Chris Putnam 2007-2021
7  *
8  * Source code released under the GPL version 2
9  *
10  */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include "str.h"
15 #include "fields.h"
16 #include "month.h"
17 #include "utf8.h"
18 #include "bibformats.h"
19 
20 /*****************************************************
21  PUBLIC: int wordout_initparams()
22 *****************************************************/
23 
24 static void wordout_writeheader( FILE *outptr, param *p );
25 static void wordout_writefooter( FILE *outptr );
26 static int  wordout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs );
27 
28 int
wordout_initparams(param * pm,const char * progname)29 wordout_initparams( param *pm, const char *progname )
30 {
31 	pm->writeformat      = BIBL_WORD2007OUT;
32 	pm->format_opts      = 0;
33 	pm->charsetout       = BIBL_CHARSET_UNICODE;
34 	pm->charsetout_src   = BIBL_SRC_DEFAULT;
35 	pm->latexout         = 0;
36 	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
37 	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
38 	if ( !pm->utf8out )
39 		pm->xmlout   = BIBL_XMLOUT_ENTITIES;
40 	else
41 		pm->xmlout   = BIBL_XMLOUT_TRUE;
42 	pm->nosplittitle     = 0;
43 	pm->verbose          = 0;
44 	pm->addcount         = 0;
45 	pm->singlerefperfile = 0;
46 
47 	pm->headerf   = wordout_writeheader;
48 	pm->footerf   = wordout_writefooter;
49 	pm->assemblef = NULL;
50 	pm->writef    = wordout_write;
51 
52 	if ( !pm->progname ) {
53 		if ( !progname ) pm->progname = NULL;
54 		else {
55 			pm->progname = strdup( progname );
56 			if ( !pm->progname ) return BIBL_ERR_MEMERR;
57 		}
58 	}
59 
60 	return BIBL_OK;
61 }
62 
63 /*****************************************************
64  PUBLIC: int wordout_write()
65 *****************************************************/
66 
67 typedef struct convert {
68 	char *oldtag;
69 	char *newtag;
70 	char *prefix;
71 	int  code;
72 } convert;
73 
74 /*
75 At the moment 17 unique types of sources are defined:
76 
77 {code}
78 	Art
79 	ArticleInAPeriodical
80 	Book
81 	BookSection
82 	Case
83 	Conference
84 	DocumentFromInternetSite
85 	ElectronicSource
86 	Film
87 	InternetSite
88 	Interview
89 	JournalArticle
90 	Report
91 	Misc
92 	Patent
93 	Performance
94 	Proceedings
95 	SoundRecording
96 {code}
97 
98 */
99 
100 enum {
101 	TYPE_UNKNOWN = 0,
102 	TYPE_ART,
103 	TYPE_ARTICLEINAPERIODICAL,
104 	TYPE_BOOK,
105 	TYPE_BOOKSECTION,
106 	TYPE_CASE,
107 	TYPE_CONFERENCE,
108 	TYPE_DOCUMENTFROMINTERNETSITE,
109 	TYPE_ELECTRONICSOURCE,
110 	TYPE_FILM,
111 	TYPE_INTERNETSITE,
112 	TYPE_INTERVIEW,
113 	TYPE_JOURNALARTICLE,
114 	TYPE_MISC,
115 	TYPE_PATENT,
116 	TYPE_PERFORMANCE,
117 	TYPE_PROCEEDINGS,
118 	TYPE_REPORT,
119 	TYPE_SOUNDRECORDING,
120 
121 	TYPE_THESIS,
122 	TYPE_MASTERSTHESIS,
123 	TYPE_PHDTHESIS,
124 };
125 
126 static void
output_level(FILE * outptr,int level)127 output_level( FILE *outptr, int level )
128 {
129 	int i;
130 	for ( i=0; i<level; ++i )
131 		fprintf( outptr, " " );
132 }
133 
134 /* fixed output
135  *
136  * <TAG>value</TAG>
137  */
138 static void
output_fixed(FILE * outptr,const char * tag,const char * value,int level)139 output_fixed( FILE *outptr, const char *tag, const char *value, int level )
140 {
141 	output_level( outptr, level );
142 	fprintf( outptr, "<%s>%s</%s>\n", tag, value, tag );
143 }
144 
145 /* detail output
146  *
147  * <TAG>value</TAG>
148  */
149 static void
output_item(fields * info,FILE * outptr,const char * tag,const char * prefix,int item,int level)150 output_item( fields *info, FILE *outptr, const char *tag, const char *prefix, int item, int level )
151 {
152 	if ( item!=-1 ) {
153 		output_level( outptr, level );
154 		fprintf( outptr, "<%s>%s%s</%s>\n",
155 			tag,
156 			prefix,
157 			(char*) fields_value( info, item, FIELDS_CHRP ),
158 			tag
159 		);
160 	}
161 }
162 
163 static void
output_itemv(FILE * outptr,const char * tag,const char * item,int level)164 output_itemv( FILE *outptr, const char *tag, const char *item, int level )
165 {
166 	output_level( outptr, level );
167 	fprintf( outptr, "<%s>%s</%s>\n", tag, item, tag );
168 }
169 
170 /* range output
171  *
172  * <TAG>start-end</TAG>
173  *
174  */
175 static void
output_nodash(FILE * outptr,const char * p)176 output_nodash( FILE *outptr, const char *p )
177 {
178 	while ( *p ) {
179 		/* -30 is the first character of a UTF8 em-dash and en-dash */
180 		if ( *p==-30 && ( utf8_is_emdash( p ) || utf8_is_endash( p ) ) ) {
181 			fprintf( outptr, "-" );
182 			p+=3;
183 		} else {
184 			fprintf( outptr, "%c", *p );
185 			p+=1;
186 		}
187 	}
188 }
189 static void
output_range(FILE * outptr,const char * tag,const char * start,const char * end,int level)190 output_range( FILE *outptr, const char *tag, const char *start, const char *end, int level )
191 {
192 	if ( !start && !end ) return;
193 	fprintf( outptr, "<%s>", tag );
194 	if ( start ) output_nodash( outptr, start );
195 	if ( start && end ) fprintf( outptr, "-" );
196 	if ( end ) output_nodash( outptr, end );
197 	fprintf( outptr, "</%s>\n", tag );
198 }
199 
200 static void
output_list(fields * info,FILE * outptr,convert * c,int nc)201 output_list( fields *info, FILE *outptr, convert *c, int nc )
202 {
203         int i, n;
204         for ( i=0; i<nc; ++i ) {
205                 n = fields_find( info, c[i].oldtag, c[i].code );
206                 if ( n!=FIELDS_NOTFOUND ) output_item( info, outptr, c[i].newtag, c[i].prefix, n, 0 );
207         }
208 
209 }
210 
211 typedef struct outtype {
212 	int value;
213 	const char *out;
214 } outtype;
215 
216 static
217 outtype genres[] = {
218 	{ TYPE_PATENT,           "patent" },
219 	{ TYPE_REPORT,           "report" },
220 	{ TYPE_REPORT,           "technical report" },
221 	{ TYPE_CASE,             "legal case and case notes" },
222 	{ TYPE_ART,              "art original" },
223 	{ TYPE_ART,              "art reproduction" },
224 	{ TYPE_ART,              "comic strip" },
225 	{ TYPE_ART,              "diorama" },
226 	{ TYPE_ART,              "graphic" },
227 	{ TYPE_ART,              "model" },
228 	{ TYPE_ART,              "picture" },
229 	{ TYPE_ELECTRONICSOURCE, "electronic" },
230 	{ TYPE_FILM,             "videorecording" },
231 	{ TYPE_FILM,             "motion picture" },
232 	{ TYPE_SOUNDRECORDING,   "sound" },
233 	{ TYPE_PERFORMANCE,      "rehersal" },
234 	{ TYPE_INTERNETSITE,     "web site" },
235 	{ TYPE_INTERVIEW,        "interview" },
236 	{ TYPE_INTERVIEW,        "communication" },
237 	{ TYPE_MISC,             "misc" },
238 };
239 int ngenres = sizeof( genres ) / sizeof( genres[0] );
240 
241 static int
get_type_from_genre(fields * info)242 get_type_from_genre( fields *info )
243 {
244 	int type = TYPE_UNKNOWN, i, j, level;
245 	const char *genre, *tag;
246 	for ( i=0; i<info->n; ++i ) {
247 		tag = (const char *) fields_tag( info, i, FIELDS_CHRP );
248 		if ( strcasecmp( tag, "GENRE:MARC" ) && strcasecmp( tag, "GENRE:BIBUTILS" ) && strcasecmp( tag, "GENRE:UNKNOWN" ) ) continue;
249 		genre = (const char *) fields_value( info, i, FIELDS_CHRP );
250 		for ( j=0; j<ngenres; ++j ) {
251 			if ( !strcasecmp( genres[j].out, genre ) )
252 				type = genres[j].value;
253 		}
254 		if ( type==TYPE_UNKNOWN ) {
255 			level = fields_level( info, i );
256 			if ( !strcasecmp( genre, "academic journal" ) ) {
257 				type = TYPE_JOURNALARTICLE;
258 			}
259 			else if ( !strcasecmp( genre, "periodical" ) ) {
260 				if ( type == TYPE_UNKNOWN )
261 					type = TYPE_ARTICLEINAPERIODICAL;
262 			}
263 			else if ( !strcasecmp( genre, "book" ) ||
264 				!strcasecmp( genre, "collection" ) ) {
265 				if ( fields_level( info, i ) == 0 ) type = TYPE_BOOK;
266 				else type = TYPE_BOOKSECTION;
267 			}
268 			else if ( !strcasecmp( genre, "conference publication" ) ) {
269 				if ( level==0 ) type=TYPE_CONFERENCE;
270 				else type = TYPE_PROCEEDINGS;
271 			}
272 			else if ( !strcasecmp( genre, "thesis" ) ) {
273 	                        if ( type==TYPE_UNKNOWN ) type=TYPE_THESIS;
274 			}
275 			else if ( !strcasecmp( genre, "Ph.D. thesis" ) ) {
276 				type = TYPE_PHDTHESIS;
277 			}
278 			else if ( !strcasecmp( genre, "Masters thesis" ) ) {
279 				type = TYPE_MASTERSTHESIS;
280 			}
281 		}
282 	}
283 	return type;
284 }
285 
286 static int
get_type_from_resource(fields * info)287 get_type_from_resource( fields *info )
288 {
289 	int type = TYPE_UNKNOWN, i;
290 	char *tag, *resource;
291 	for ( i=0; i<info->n; ++i ) {
292 		tag = (char *) fields_tag( info, i, FIELDS_CHRP );
293 		if ( strcasecmp( tag, "RESOURCE" ) ) continue;
294 		resource = (char *) fields_value( info, i, FIELDS_CHRP );
295 		if ( !strcasecmp( resource, "moving image" ) )
296 			type = TYPE_FILM;
297 	}
298 	return type;
299 }
300 
301 static int
get_type(fields * info)302 get_type( fields *info )
303 {
304 	int type;
305 	type = get_type_from_genre( info );
306 	if ( type==TYPE_UNKNOWN )
307 		type = get_type_from_resource( info );
308 	return type;
309 }
310 
311 static void
output_titlebits(const char * mainttl,const char * subttl,FILE * outptr)312 output_titlebits( const char *mainttl, const char *subttl, FILE *outptr )
313 {
314 	if ( mainttl ) fprintf( outptr, "%s", mainttl );
315 	if ( subttl ) {
316 		if ( mainttl ) {
317 			if ( mainttl[ strlen( mainttl ) - 1 ] != '?' )
318 				fprintf( outptr, ": " );
319 			else fprintf( outptr, " " );
320 		}
321 		fprintf( outptr, "%s", subttl );
322 	}
323 }
324 
325 static void
output_titleinfo(const char * mainttl,const char * subttl,FILE * outptr,const char * tag,int level)326 output_titleinfo( const char *mainttl, const char *subttl, FILE *outptr, const char *tag, int level )
327 {
328 	if ( mainttl || subttl ) {
329 		fprintf( outptr, "<%s>", tag );
330 		output_titlebits( mainttl, subttl, outptr );
331 		fprintf( outptr, "</%s>\n", tag );
332 	}
333 }
334 
335 static void
output_generaltitle(fields * info,FILE * outptr,const char * tag,int level)336 output_generaltitle( fields *info, FILE *outptr, const char *tag, int level )
337 {
338 	const char *ttl       = fields_findv( info, level, FIELDS_CHRP, "TITLE" );
339 	const char *subttl    = fields_findv( info, level, FIELDS_CHRP, "SUBTITLE" );
340 	const char *shrttl    = fields_findv( info, level, FIELDS_CHRP, "SHORTTITLE" );
341 	const char *shrsubttl = fields_findv( info, level, FIELDS_CHRP, "SHORTSUBTITLE" );
342 
343 	if ( ttl ) {
344 		output_titleinfo( ttl, subttl, outptr, tag, level );
345 	}
346 	else if ( shrttl ) {
347 		output_titleinfo( shrttl, shrsubttl, outptr, tag, level );
348 	}
349 }
350 
351 static void
output_maintitle(fields * info,FILE * outptr,int level)352 output_maintitle( fields *info, FILE *outptr, int level )
353 {
354 	const char *ttl       = fields_findv( info, level, FIELDS_CHRP, "TITLE" );
355 	const char *subttl    = fields_findv( info, level, FIELDS_CHRP, "SUBTITLE" );
356 	const char *shrttl    = fields_findv( info, level, FIELDS_CHRP, "SHORTTITLE" );
357 	const char *shrsubttl = fields_findv( info, level, FIELDS_CHRP, "SHORTSUBTITLE" );
358 
359 	if ( ttl ) {
360 		output_titleinfo( ttl, subttl, outptr, "b:Title", level );
361 
362 		/* output shorttitle if it's different from normal title */
363 		if ( shrttl ) {
364 			if ( !ttl || ( strcmp( shrttl, ttl ) || subttl ) ) {
365 				fprintf( outptr,  " <b:ShortTitle>" );
366 				output_titlebits( shrttl, shrsubttl, outptr );
367 				fprintf( outptr, "</b:ShortTitle>\n" );
368 			}
369 		}
370 	}
371 	else if ( shrttl ) {
372 		output_titleinfo( shrttl, shrsubttl, outptr, "b:Title", level );
373 	}
374 }
375 
376 static void
output_name_nomangle(FILE * outptr,const char * p)377 output_name_nomangle( FILE *outptr, const char *p )
378 {
379 	fprintf( outptr, "<b:Person>" );
380 	fprintf( outptr, "<b:Last>%s</b:Last>", p );
381 	fprintf( outptr, "</b:Person>\n" );
382 }
383 
384 static void
output_name(FILE * outptr,const char * p)385 output_name( FILE *outptr, const char *p )
386 {
387 	str family, part;
388 	int n=0, npart=0;
389 
390 	str_init( &family );
391 	while ( *p && *p!='|' ) str_addchar( &family, *p++ );
392 	if ( *p=='|' ) p++;
393 	if ( str_has_value( &family ) ) {
394 		fprintf( outptr, "<b:Person>" );
395 		fprintf( outptr, "<b:Last>%s</b:Last>", str_cstr( &family ) );
396 		n++;
397 	}
398 	str_free( &family );
399 
400 	str_init( &part );
401 	while ( *p ) {
402 		while ( *p && *p!='|' ) str_addchar( &part, *p++ );
403 		if ( str_has_value( &part ) ) {
404 			if ( n==0 ) fprintf( outptr, "<b:Person>" );
405 			if ( npart==0 )
406 				fprintf( outptr, "<b:First>%s</b:First>", str_cstr( &part ) );
407 			else
408 				fprintf( outptr, "<b:Middle>%s</b:Middle>", str_cstr( &part ) );
409 			n++;
410 			npart++;
411 		}
412 		if ( *p=='|' ) {
413 			p++;
414 			str_empty( &part );
415 		}
416 	}
417 	if ( n ) fprintf( outptr, "</b:Person>\n" );
418 
419 	str_free( &part );
420 }
421 
422 
423 #define NAME (1)
424 #define NAME_ASIS (2)
425 #define NAME_CORP (4)
426 
427 static int
extract_name_and_info(str * outtag,str * intag)428 extract_name_and_info( str *outtag, str *intag )
429 {
430 	int code = NAME;
431 	str_strcpy( outtag, intag );
432 	if ( str_findreplace( outtag, ":ASIS", "" ) ) code = NAME_ASIS;
433 	if ( str_findreplace( outtag, ":CORP", "" ) ) code = NAME_CORP;
434 	return code;
435 }
436 
437 static void
output_name_type(fields * info,FILE * outptr,int level,char * map[],int nmap,const char * tag)438 output_name_type( fields *info, FILE *outptr, int level, char *map[], int nmap, const char *tag )
439 {
440 	str ntag;
441 	int i, j, n=0, code, nfields;
442 	str_init( &ntag );
443 	nfields = fields_num( info );
444 	for ( j=0; j<nmap; ++j ) {
445 		for ( i=0; i<nfields; ++i ) {
446 			code = extract_name_and_info( &ntag, fields_tag( info, i, FIELDS_STRP ) );
447 			if ( strcasecmp( str_cstr( &ntag ), map[j] ) ) continue;
448 			if ( n==0 )
449 				fprintf( outptr, "<%s><b:NameList>\n", tag );
450 			if ( code != NAME )
451 				output_name_nomangle( outptr, (char *) fields_value( info, i, FIELDS_CHRP ) );
452 			else
453 				output_name( outptr, (char *) fields_value( info, i, FIELDS_CHRP ) );
454 			n++;
455 		}
456 	}
457 	str_free( &ntag );
458 	if ( n )
459 		fprintf( outptr, "</b:NameList></%s>\n", tag );
460 }
461 
462 static void
output_names(fields * info,FILE * outptr,int level,int type)463 output_names( fields *info, FILE *outptr, int level, int type )
464 {
465 	char *authors[] = { "AUTHOR", "WRITER", "ASSIGNEE", "ARTIST",
466 		"CARTOGRAPHER", "INVENTOR", "ORGANIZER", "DIRECTOR",
467 		"PERFORMER", "REPORTER", "TRANSLATOR", "ADDRESSEE",
468 		"2ND_AUTHOR", "3RD_AUTHOR", "SUB_AUTHOR", "COMMITTEE",
469 		"COURT", "LEGISLATIVEBODY" };
470 	int nauthors = sizeof( authors ) / sizeof( authors[0] );
471 
472 	char *editors[] = { "EDITOR" };
473 	int neditors = sizeof( editors ) / sizeof( editors[0] );
474 
475 	char author_default[] = "b:Author", inventor[] = "b:Inventor";
476 	char *author_type = author_default;
477 
478 	if ( type == TYPE_PATENT ) author_type = inventor;
479 
480 	fprintf( outptr, "<b:Author>\n" );
481 	output_name_type( info, outptr, level, authors, nauthors, author_type );
482 	output_name_type( info, outptr, level, editors, neditors, "b:Editor" );
483 	fprintf( outptr, "</b:Author>\n" );
484 }
485 
486 static void
output_date(fields * info,FILE * outptr,int level)487 output_date( fields *info, FILE *outptr, int level )
488 {
489 	const char *use;
490 
491 	const char *year  = fields_findv_firstof( info, level, FIELDS_CHRP,
492 			"PARTDATE:YEAR", "DATE:YEAR", NULL );
493 	const char *month = fields_findv_firstof( info, level, FIELDS_CHRP,
494 			"PARTDATE:MONTH", "DATE:MONTH", NULL );
495 	const char *day   = fields_findv_firstof( info, level, FIELDS_CHRP,
496 			"PARTDATE:DAY", "DATE:DAY", NULL );
497 	if ( year )  output_itemv( outptr, "b:Year", year, 0 );
498 	if ( month ) {
499 		(void) number_to_full_month( month, &use );
500 		output_itemv( outptr, "b:Month", use, 0 );
501 	}
502 	if ( day )   output_itemv( outptr, "b:Day", day, 0 );
503 }
504 
505 static void
output_pages(fields * info,FILE * outptr,int level)506 output_pages( fields *info, FILE *outptr, int level )
507 {
508 	const char *sn = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGES:START" );
509 	const char *en = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGES:STOP" );
510 	const char *ar = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "ARTICLENUMBER" );
511 	if ( sn || en )
512 		output_range( outptr, "b:Pages", sn, en, level );
513 	else if ( ar )
514 		output_range( outptr, "b:Pages", ar, NULL, level );
515 }
516 
517 static void
output_includedin(fields * info,FILE * outptr,int type)518 output_includedin( fields *info, FILE *outptr, int type )
519 {
520 	if ( type==TYPE_JOURNALARTICLE ) {
521 		output_generaltitle( info, outptr, "b:JournalName", 1 );
522 	} else if ( type==TYPE_ARTICLEINAPERIODICAL ) {
523 		output_generaltitle( info, outptr, "b:PeriodicalTitle", 1 );
524 	} else if ( type==TYPE_BOOKSECTION ) {
525 		output_generaltitle( info, outptr, "b:ConferenceName", 1 ); /*??*/
526 	} else if ( type==TYPE_PROCEEDINGS ) {
527 		output_generaltitle( info, outptr, "b:ConferenceName", 1 );
528 	}
529 }
530 
531 static int
type_is_thesis(int type)532 type_is_thesis( int type )
533 {
534 	if ( type==TYPE_THESIS ||
535 	     type==TYPE_PHDTHESIS ||
536 	     type==TYPE_MASTERSTHESIS )
537 		return 1;
538 	else
539 		return 0;
540 }
541 
542 static void
output_thesisdetails(fields * info,FILE * outptr,int type)543 output_thesisdetails( fields *info, FILE *outptr, int type )
544 {
545 	char *tag;
546 	int i, n;
547 
548 	if ( type==TYPE_PHDTHESIS )
549 		output_fixed( outptr, "b:ThesisType", "Ph.D. Thesis", 0 );
550 	else if ( type==TYPE_MASTERSTHESIS )
551 		output_fixed( outptr, "b:ThesisType", "Masters Thesis", 0 );
552 
553 	n = fields_num( info );
554 	for ( i=0; i<n; ++i ) {
555 		tag = fields_tag( info, i, FIELDS_CHRP );
556 		if ( strcasecmp( tag, "DEGREEGRANTOR" ) &&
557 			strcasecmp( tag, "DEGREEGRANTOR:ASIS") &
558 			strcasecmp( tag, "DEGREEGRANTOR:CORP"))
559 				continue;
560 		output_item( info, outptr, "b:Institution", "", i, 0 );
561 	}
562 }
563 
564 static
565 outtype types[] = {
566 	{ TYPE_UNKNOWN,                  "Misc" },
567 	{ TYPE_MISC,                     "Misc" },
568 	{ TYPE_BOOK,                     "Book" },
569 	{ TYPE_BOOKSECTION,              "BookSection" },
570 	{ TYPE_CASE,                     "Case" },
571 	{ TYPE_CONFERENCE,               "Conference" },
572 	{ TYPE_ELECTRONICSOURCE,         "ElectronicSource" },
573 	{ TYPE_FILM,                     "Film" },
574 	{ TYPE_INTERNETSITE,             "InternetSite" },
575 	{ TYPE_INTERVIEW,                "Interview" },
576 	{ TYPE_SOUNDRECORDING,           "SoundRecording" },
577 	{ TYPE_ARTICLEINAPERIODICAL,     "ArticleInAPeriodical" },
578 	{ TYPE_DOCUMENTFROMINTERNETSITE, "DocumentFromInternetSite" },
579 	{ TYPE_JOURNALARTICLE,           "JournalArticle" },
580 	{ TYPE_REPORT,                   "Report" },
581 	{ TYPE_PATENT,                   "Patent" },
582 	{ TYPE_PERFORMANCE,              "Performance" },
583 	{ TYPE_PROCEEDINGS,              "Proceedings" },
584 };
585 static
586 int ntypes = sizeof( types ) / sizeof( types[0] );
587 
588 static void
output_type(fields * info,FILE * outptr,int type)589 output_type( fields *info, FILE *outptr, int type )
590 {
591 	int i, found = 0;
592 	fprintf( outptr, "<b:SourceType>" );
593 	for ( i=0; i<ntypes && !found; ++i ) {
594 		if ( types[i].value!=type ) continue;
595 		found = 1;
596 		fprintf( outptr, "%s", types[i].out );
597 	}
598 	if ( !found ) {
599 		if (  type_is_thesis( type ) ) fprintf( outptr, "Report" );
600 		else fprintf( outptr, "Misc" );
601 	}
602 	fprintf( outptr, "</b:SourceType>\n" );
603 
604 	if ( type_is_thesis( type ) )
605 		output_thesisdetails( info, outptr, type );
606 }
607 
608 static void
output_comments(fields * info,FILE * outptr,int level)609 output_comments( fields *info, FILE *outptr, int level )
610 {
611 	const char *abs;
612 	vplist_index i;
613 	vplist notes;
614 
615 	vplist_init( &notes );
616 
617 	abs = fields_findv( info, level, FIELDS_CHRP, "ABSTRACT" );
618 	fields_findv_each( info, level, FIELDS_CHRP, &notes, "NOTES" );
619 
620 	if ( abs || notes.n ) fprintf( outptr, "<b:Comments>" );
621 	if ( abs ) fprintf( outptr, "%s", abs );
622 	for ( i=0; i<notes.n; ++i )
623 		fprintf( outptr, "%s", (char*)vplist_get( &notes, i ) );
624 	if ( abs || notes.n ) fprintf( outptr, "</b:Comments>\n" );
625 
626 	vplist_free( &notes );
627 }
628 
629 static void
output_bibkey(fields * info,FILE * outptr)630 output_bibkey( fields *info, FILE *outptr )
631 {
632 	const char *bibkey = fields_findv_firstof( info, LEVEL_ANY, FIELDS_CHRP,
633 			"REFNUM", "BIBKEY", NULL );
634 	if ( bibkey ) output_itemv( outptr, "b:Tag", bibkey, 0 );
635 }
636 
637 static void
output_citeparts(fields * info,FILE * outptr,int level,int max,int type)638 output_citeparts( fields *info, FILE *outptr, int level, int max, int type )
639 {
640 	convert origin[] = {
641 		{ "ADDRESS",	"b:City",	"", LEVEL_ANY },
642 		{ "PUBLISHER",	"b:Publisher",	"", LEVEL_ANY },
643 		{ "EDITION",	"b:Edition",	"", LEVEL_ANY }
644 	};
645 	int norigin = sizeof( origin ) / sizeof ( convert );
646 
647 	convert parts[] = {
648 		{ "VOLUME",          "b:Volume",  "", LEVEL_ANY },
649 		{ "SECTION",         "b:Section", "", LEVEL_ANY },
650 		{ "ISSUE",           "b:Issue",   "", LEVEL_ANY },
651 		{ "NUMBER",          "b:Issue",   "", LEVEL_ANY },
652 		{ "PUBLICLAWNUMBER", "b:Volume",  "", LEVEL_ANY },
653 		{ "SESSION",         "b:Issue",   "", LEVEL_ANY },
654 		{ "URL",             "b:Url",     "", LEVEL_ANY },
655 		{ "JSTOR",           "b:Url",     "http://www.jstor.org/stable/", LEVEL_ANY },
656 		{ "ARXIV",           "b:Url",     "http://arxiv.org/abs/",        LEVEL_ANY },
657 		{ "PMID",            "b:Url",     "http://www.ncbi.nlm.nih.gov/pubmed/", LEVEL_ANY },
658 		{ "PMC",             "b:Url",     "http://www.ncbi.nlm.nih.gov/pmc/articles/", LEVEL_ANY },
659 		{ "DOI",             "b:Url",     "https://doi.org/", LEVEL_ANY },
660 		{ "MRNUMBER",        "b:Url",     "http://www.ams.org/mathscinet-getitem?mr=", LEVEL_ANY },
661 	};
662 	int nparts=sizeof(parts)/sizeof(convert);
663 
664 	output_bibkey( info, outptr );
665 	output_type( info, outptr, type );
666 	output_list( info, outptr, origin, norigin );
667 	output_date( info, outptr, level );
668 	output_includedin( info, outptr, type );
669 	output_list( info, outptr, parts, nparts );
670 	output_pages( info, outptr, level );
671 	output_names( info, outptr, level, type );
672 	output_maintitle( info, outptr, 0 );
673 	output_comments( info, outptr, level );
674 }
675 
676 static int
wordout_write(fields * info,FILE * outptr,param * p,unsigned long numrefs)677 wordout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs )
678 {
679 	int max = fields_maxlevel( info );
680 	int type = get_type( info );
681 
682 	fprintf( outptr, "<b:Source>\n" );
683 	output_citeparts( info, outptr, -1, max, type );
684 	fprintf( outptr, "</b:Source>\n" );
685 
686 	fflush( outptr );
687 
688 	return BIBL_OK;
689 }
690 
691 /*****************************************************
692  PUBLIC: void wordout_writeheader()
693 *****************************************************/
694 
695 static void
wordout_writeheader(FILE * outptr,param * p)696 wordout_writeheader( FILE *outptr, param *p )
697 {
698 	if ( p->utf8bom ) utf8_writebom( outptr );
699 	fprintf(outptr,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
700 	fprintf(outptr,"<b:Sources SelectedStyle=\"\" "
701 		"xmlns:b=\"http://schemas.openxmlformats.org/officeDocument/2006/bibliography\" "
702 		" xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/bibliography\" >\n");
703 }
704 
705 /*****************************************************
706  PUBLIC: void wordout_writefooter()
707 *****************************************************/
708 
709 static void
wordout_writefooter(FILE * outptr)710 wordout_writefooter( FILE *outptr )
711 {
712 	fprintf(outptr,"</b:Sources>\n");
713 	fflush( outptr );
714 }
715