1 /******************************************************************************
2  *
3  *  vpl2mod.cpp -	Utility to import VPL formatted modules
4  *
5  * $Id: vpl2mod.cpp 3063 2014-03-04 13:04:11Z chrislit $
6  *
7  * Copyright 2000-2013 CrossWire Bible Society (http://www.crosswire.org)
8  *	CrossWire Bible Society
9  *	P. O. Box 2528
10  *	Tempe, AZ  85280-2528
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  */
22 
23 #ifdef _MSC_VER
24 	#pragma warning( disable: 4251 )
25 	#pragma warning( disable: 4996 )
26 #endif
27 
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <sys/stat.h>
34 
35 #ifndef __GNUC__
36 #include <io.h>
37 #else
38 #include <unistd.h>
39 #endif
40 
41 #include <filemgr.h>
42 #include <swmgr.h>
43 #include <rawtext.h>
44 #include <swbuf.h>
45 #include <versekey.h>
46 
47 #ifndef NO_SWORD_NAMESPACE
48 using sword::FileMgr;
49 using sword::SWMgr;
50 using sword::RawText;
51 using sword::VerseKey;
52 using sword::SWBuf;
53 using sword::SW_POSITION;
54 #endif
55 
56 
readline(int fd,char ** buf)57 char readline(int fd, char **buf) {
58 	char ch;
59 	if (*buf)
60 		delete [] *buf;
61 	*buf = 0;
62 	int len;
63 
64 
65 	long index = lseek(fd, 0, SEEK_CUR);
66 	// clean up any preceding white space
67 	while ((len = read(fd, &ch, 1)) == 1) {
68 		if ((ch != 13) && (ch != ' ') && (ch != '\t'))
69 			break;
70 		else index++;
71 	}
72 
73 
74 	while (ch != 10) {
75         if ((len = read(fd, &ch, 1)) != 1)
76 			break;
77 	}
78 
79 	int size = (lseek(fd, 0, SEEK_CUR) - index) - 1;
80 
81 	*buf = new char [ size + 1 ];
82 
83 	if (size > 0) {
84 		lseek(fd, index, SEEK_SET);
85 		read(fd, *buf, size);
86 		read(fd, &ch, 1);   //pop terminating char
87 		(*buf)[size] = 0;
88 
89 		// clean up any trailing junk on buf
90 		for (char *it = *buf+(strlen(*buf)-1); it > *buf; it--) {
91 			if ((*it != 10) && (*it != 13) && (*it != ' ') && (*it != '\t'))
92 				break;
93 			else *it = 0;
94 		}
95 	}
96 	else **buf = 0;
97 	return !len;
98 }
99 
100 
parseVReg(char * buf)101 char *parseVReg(char *buf) {
102 	char stage = 0;
103 
104 	while (*buf) {
105 		switch (stage) {
106 		case 0:
107 			if (isalpha(*buf))
108 				stage++;
109 			break;
110 		case 1:
111 			if (isdigit(*buf))
112 				stage++;
113 			break;
114 		case 2:
115 			if (*buf == ':')
116 				stage++;
117 			break;
118 		case 3:
119 			if (isdigit(*buf))
120 				stage++;
121 			break;
122 	   case 4:
123 			if (*buf == ' ') {
124 				*buf = 0;
125 				return ++buf;
126 			}
127 			break;
128 		}
129 		buf++;
130 	}
131 	return (stage == 4) ? buf : 0;  // if we got to stage 4 return after key buf, else return 0;
132 }
133 
134 
isKJVRef(const char * buf)135 bool isKJVRef(const char *buf) {
136 	VerseKey vk, test;
137 	vk.setAutoNormalize(false);
138 	vk.setIntros(true);	// turn on mod/testmnt/book/chap headings
139 	vk.setPersist(true);
140 	// lets do some tests on the verse --------------
141 	vk = buf;
142 	test = buf;
143 
144 	if (vk.getTestament() && vk.getBook() && vk.getChapter() && vk.getVerse()) { // if we're not a heading
145 //		std::cerr << (const char*)vk << " == "  << (const char*)test << std::endl;
146 		return (vk == test);
147 	}
148 	else return true;	// no check if we're a heading... Probably bad.
149 }
150 
151 
fixText(char * text)152 void fixText(char *text) {
153 	char *to = text;
154 	while(*text) {
155 		*to++ = *text++;
156 		*to++ = *text++;
157 		if (!*text)
158 			break;
159 		if (*text != ' ')
160 			std::cerr << "problem\n";
161 		else	text++;
162 	}
163 	*to = 0;
164 }
165 
main(int argc,char ** argv)166 int main(int argc, char **argv) {
167 
168 	// Let's test our command line arguments
169 	if (argc < 2) {
170 //		fprintf(stderr, "usage: %s <vpl_file> </path/to/mod> [0|1 - file includes prepended verse references]\n", argv[0]);
171 		fprintf(stderr, "usage: %s <source_vpl_file> </path/to/output/mod/> [0|1 - prepended verse refs] [0|1 - NT only]\n\n", argv[0]);
172 		fprintf(stderr, "\tWARNING: THIS IS CURRENTLY A KJV-VERSIFICATION-ONLY UTILITY\n");
173 		fprintf(stderr, "\tWith no verse refs, source file must contain exactly 31102 lines.\n");
174 		fprintf(stderr, "\tThis is KJV verse count plus headings for MODULE,\n");
175 		fprintf(stderr, "\tTESTAMENT, BOOK, CHAPTER. An example snippet follows:\n\n");
176 		fprintf(stderr, "\t\tMODULE HEADER\n");
177 		fprintf(stderr, "\t\tOLD TESTAMENT HEADER\n");
178 		fprintf(stderr, "\t\tGENESIS HEADER\n");
179 		fprintf(stderr, "\t\tCHAPTER 1 HEADER\n");
180 		fprintf(stderr, "\t\tIn the beginning...\n\n");
181 		fprintf(stderr, "\t... implying there must also be a CHAPTER2 HEADER,\n");
182 		fprintf(stderr, "\tEXODUS HEADER, NEW TESTAMENT HEADER, etc.  If there is no text for\n");
183 		fprintf(stderr, "\tthe header, a blank line must, at least, hold place.\n\n");
184 		fprintf(stderr, "\tWith verse refs, source file must simply contain any number of lines,\n");
185 		fprintf(stderr, "\tthat begin with the verse reference for which it is an entry.  e.g.:\n\n");
186 		fprintf(stderr, "\t\tgen 1:0 CHAPTER 1 HEADER\n");
187 		fprintf(stderr, "\t\tgen 1:1 In the beginning...\n\n");
188 		exit(-1);
189 	}
190 
191 	// Let's see if we can open our input file
192 	int fd = FileMgr::openFileReadOnly(argv[1]);
193 	if (fd < 0) {
194 		fprintf(stderr, "error: %s: couldn't open input file: %s \n", argv[0], argv[1]);
195 		exit(-2);
196 	}
197 
198 	// Try to initialize a default set of datafiles and indicies at our
199 	// datapath location passed to us from the user.
200 	if (RawText::createModule(argv[2])) {
201 		fprintf(stderr, "error: %s: couldn't create module at path: %s \n", argv[0], argv[2]);
202 		exit(-3);
203 	}
204 
205 	// not used yet, but for future support of a vpl file with each line
206 	// prepended with verse reference, eg. "Gen 1:1 In the beginning..."
207 	bool vref = false;
208 	if (argc > 3)
209 		vref = (argv[3][0] == '0') ? false : true;
210 
211 	// if 'nt' is the 4th arg, our vpl file only has the NT
212 	bool ntonly = false;
213 	if (argc > 4)
214                 ntonly = (argv[4][0] == '0') ? false : true;
215 
216 	// Do some initialization stuff
217 	char *buffer = 0;
218 	RawText mod(argv[2]);	// open our datapath with our RawText driver.
219 	VerseKey vk;
220 	vk.setAutoNormalize(false);
221 	vk.setIntros(true);	// turn on mod/testmnt/book/chap headings
222 	vk.setPersist(true);
223 
224 	mod.setKey(vk);
225 
226 	// Loop through module from TOP to BOTTOM and set next line from
227 	// input file as text for this entry in the module
228 	mod = TOP;
229 	if (ntonly) vk = "Matthew 1:1";
230 
231 	int successive = 0;  //part of hack below
232 	while ((!mod.popError()) && (!readline(fd, &buffer))) {
233 		if (*buffer == '|')	// comments, ignore line
234 			continue;
235 		if (vref) {
236 			const char *verseText = parseVReg(buffer);
237 			if (!verseText) {	// if we didn't find a valid verse ref
238 				std::cerr << "No valid verse ref found on line: " << buffer << "\n";
239 				exit(-4);
240 			}
241 
242 			vk = buffer;
243 			if (vk.popError()) {
244 				std::cerr << "Error parsing key: " << buffer << "\n";
245 				exit(-5);
246 			}
247 			SWBuf orig = mod.getRawEntry();
248 
249 			if (!isKJVRef(buffer)) {
250 				VerseKey origVK = vk;
251 				/* This block is functioning improperly -- problem with AutoNormalize???
252 				do {
253 					vk--;
254 				}
255 				while (!vk.popError() && !isKJVRef(vk)); */
256 				//hack to replace above:
257 				successive++;
258 				vk -= successive;
259 				orig = mod.getRawEntry();
260 
261 				std::cerr << "Not a valid KJV ref: " << origVK << "\n";
262 				std::cerr << "appending to ref: " << vk << "\n";
263 				orig += " [ (";
264 				orig += origVK;
265 				orig += ") ";
266 				orig += verseText;
267 				orig += " ] ";
268 				verseText = orig;
269 			}
270 			else {
271 			  successive = 0;
272 			}
273 
274 			if (orig.length() > 1)
275 				   std::cerr << "Warning, overwriting verse: " << vk << std::endl;
276 
277 			// ------------- End verse tests -----------------
278 			mod << verseText;	// save text to module at current position
279 		}
280 		else {
281 			fixText(buffer);
282 			mod << buffer;	// save text to module at current position
283 			mod++;	// increment module position
284 		}
285 	}
286 
287 	// clear up our buffer that readline might have allocated
288 	if (buffer)
289 		delete [] buffer;
290 }
291