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