summaryrefslogtreecommitdiff
path: root/biblio/bibtex/utils/r2bib/risDateAdj.ll
blob: 5bef2c8f5fa4d4edb6e8b9043e1b15accceee7ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
	/* This is a lex(1) file, see http://dinosaur.compilertools.net/
	* or http://en.wikipedia.org/wiki/Lex_programming_tool .
	*
	* Compilation on UNIX systems is done by
	*     make risDateAdj
	* On other systems one may need to call lex or flex and cc explicitly:
	*     lex -8 -o risDateAdj.c risDateAdj.l
	*     cc [-std=gnu99] [-s] [-O] -o risDateAdj risDateAdj.c -ll
	*
	* The executable works as a filter and patches the Y1, Y2 and PY fields of RIS
	* files (the standard input) from a
	* Apr., YYYY
	* type produced for example by JSTOR or a
	* YYYY-MM-DD
	* or a
	* YYYY
	* or a
	* YYYY/DD
	* type to the standard defined in http://www.refman.com/support/risformat_intro.asp .
	*
	* TODO:
	* ingentaconnect produces lines PY  -- ///January 2009
	* which also fall into the category of faulty but recoverable inputs.
	* And this ought also be implemented.
	* Richard J. Mathar, 2009-02-11
	*/
%option noyywrap

%{
#include <strings.h>
#include <sys/types.h>
#include <regex.h>

#define Y1PATCH_PMAT_SIZ 7

regex_t preg1,
	preg2 ,
	preg3 ,
	preg4 ;
regmatch_t pmat[Y1PATCH_PMAT_SIZ] ;

/* Fit monam month to integer in the range 1 to 12.
* @return Return a number from 1 to 12 if the month is recognized, 0 if not.
*/
int name2Month(const char *monam)
{
	static const char *jan2dec[] = {"jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"} ;
	for(int i=0; i < 12 ; i++)
		if( strncasecmp(monam,jan2dec[i],3) == 0 )
			return i+1 ;
	return 0 ;
}

/* Generate a line YYY/MM/DD/otherinfo line on output.
* @param tagline the original line, including the tag and the <CR></LF>
*/
void y1patch(const char *yytext)
{
		/* the original tag including the mandatory white space copied thru
		*/
		fprintf(yyout,"%.6s",yytext) ;

                if( regexec(&preg2,yytext+6,0,0,0) == 0 )
		{
			/* The ISO case. Replace dashes by slashes
			*/
                        fprintf(yyout,"%.4s/%.2s/%.2s/ %s",yytext+6,yytext+11,yytext+14,yytext+17) ;
		}
                else if( regexec(&preg1,yytext+6,Y1PATCH_PMAT_SIZ,pmat,0) == 0 )
                {
	                /* pmat[0] all, pmat[1] the mont, pmat[2] any intermediate dots and blanks, pmat[3] the year.
			*/
			int mon = name2Month(yytext+6) ;
			int yr = atoi(yytext+6+pmat[3].rm_so) ;
			if ( mon )
	                        fprintf(yyout,"%d/%02d// ",yr,mon) ;
			else
	                        fprintf(yyout,"%d/// ",yr) ;

			/* the original pattern plus any <CR><NL> 
			*/
			fprintf(yyout,"%s",yytext+6) ;
                }
                else if( regexec(&preg3,yytext+6,Y1PATCH_PMAT_SIZ,pmat,0) == 0 )
                {
	                /* pmat[0] all, pmat[1] the mont, pmat[2] the date, pmat[3] the year.
			*/
			int mon = name2Month(yytext+6) ;
			int dat = atoi(yytext+6+pmat[2].rm_so) ;
			int yr = atoi(yytext+6+pmat[3].rm_so) ;
			if ( mon )
	                        fprintf(yyout,"%d/%02d/%02d/ ",yr,mon,dat) ;
			else
       				fprintf(yyout,"%d//%02d/ ",yr,dat) ;

			/* the original pattern plus any <CR><NL> 
			*/
			fprintf(yyout,"%s",yytext+6) ;
                }
                else if( regexec(&preg4,yytext+6,Y1PATCH_PMAT_SIZ,pmat,0) == 0 )
                {
			int yr = atoi(yytext+6) ;
			if ( pmat[1].rm_so >=0 )
			{
				int mon = atoi(yytext+6+pmat[1].rm_so+1) ;
	                        fprintf(yyout,"%d/%02d//",yr,mon) ;
			}
			else
	                        fprintf(yyout,"%d///",yr) ;

			/* the original <CR><NL> 
			*/
			fprintf(yyout,"%s",yytext+6+pmat[0].rm_eo) ;
                }
		else
			/* give up and copy through
			*/
			fprintf(yyout,"%s",yytext+6) ;

}
#undef Y1PATCH_PMAT_SIZ

%}

RISTAG	"Y1  - "|"PY  - "|"Y2  - "
DIGIT	[0-9]

	/* the scanner ought be compiled with the "-i" flag to
	* trigger also on the various upper/lowercase variants of these patterns
	*/
MONTH	"Jan"|"Feb"|"Mar"|"Apr"|"May"|"Jun"|"Jul"|"Aug"|"Sep"|"Oct"|"Nov"|"Dec"

%%

	/* Lines which look correct are copied trough as they are.
	* This means they contain no 3-letter mont acronyms as substrings.
	*/

	/* If the tag is immediately followed by one of the TLA (three-letter
	* acronyms above), or looks like an ISO date we try conversion.
	*/
{RISTAG}{MONTH}.+\n |
{RISTAG}{DIGIT}{4}[[:blank:]]*\r?\n |
{RISTAG}{DIGIT}{4}\/{DIGIT}{1,2}[[:blank:]]*\r?\n |
{RISTAG}{DIGIT}{4}-{DIGIT}{2}-{DIGIT}{2}.+\n {

		/* debugging
		* printf("%d\n",__LINE__) ;
		*/
		y1patch(yytext) ;
	}


%%
int main(int argc, char *argv[])
{
	/* preg1: at least three characters (matching months supposedly)
	* followed by any comma, dot and white space, and the year
	*/
	int stat =regcomp(&preg1,
		"\\([[:alpha:]]\\{3,\\}\\)\\([[:space:],\\./]*\\)\\([[:digit:]]\\{4\\}\\)\\(.*\\)", REG_ICASE) ;
	if ( stat )
		fprintf(stderr,"line %d - Internal error %d\n",__LINE__,stat) ;

	/* preg2: the ISO YYYY-MM-DD format
	*/
	stat =regcomp(&preg2, "[[:digit:]]\\{4\\}-[[:digit:]]\\{2\\}-[[:digit:]]\\{2\\}.*", REG_ICASE) ;
	if ( stat )
		fprintf(stderr,"line %d - Internal error %d\n",__LINE__,stat) ;

	/* preg3: the fullmontname 1-or-2digit-date, 4-digityear
	*/
	stat =regcomp(&preg3,
		"\\([[:alpha:]]\\{3,\\}\\) \\([[:digit:]]\\{1,2\\}\\)[ ,]*\\([[:digit:]]\\{4\\}\\).*", REG_ICASE) ;
	if ( stat )
		fprintf(stderr,"line %d - Internal error %d\n",__LINE__,stat) ;

	/* preg4: a sole 4-digit year, optionally with a slash and 2-digit month
	*/
	stat =regcomp(&preg4, "[[:digit:]]{4}(/[[:digit:]]{1,2})?[[:blank:]]*", REG_ICASE|REG_EXTENDED) ;
	if ( stat )
		fprintf(stderr,"line %d - Internal error %d\n",__LINE__,stat) ;

	yylex() ;
	regfree(& preg4) ;
	regfree(& preg3) ;
	regfree(& preg2) ;
	regfree(& preg1) ;
	return 0 ;
}