1: /******************************************************************************
2: ezht - Copyright (C) 2004 Tradcrafts
3: This file is part of the ezht.
4:
5: ezht is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License as published by the
7: Free Software Foundation; either version 2 of the License, or (at your
8: option) any later version.
9:
10: ezht is distributed in the hope that it will be useful, but
11: WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12: or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13: more details.
14:
15: You should have received a copy of the GNU General Public License along with
16: this program; see the file COPYING. If not, write to the Free Software
17: Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18: ******************************************************************************/
19:
20: #include <unistd.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <errno.h>
24: #include <string>
25: #include <vector>
26:
27: #include "reader.h"
28: #include "include.h"
29: #include "begintext.h"
30: #include "error.h"
31: #include "stack.h"
32: #include "image.h"
33: #include "refer.h"
34: #include "ezht_glob.h"
35:
36: Reader reader;
37:
38: Reader::Reader()
39: {
40: sourcemode_columns = 0;
41: sourcemode_line_cnt = 0;
42: }
43:
44: Reader::~Reader()
45: {
46: }
47:
48: void Reader::set_current_readopt(int opt)
49: {
50: infoque[0].readopt = opt;
51: }
52:
53: void Reader::set_sourcemode_columns()
54: {
55: int n_line=0;
56: int c;
57: // 総行数を計算
58: while( EOF != (c=get()) ){
59: if( c=='\n' )
60: n_line++;
61: }
62: rewind(); // rewind
63:
64: sourcemode_columns = 0;
65: while( n_line != 0 ){
66: sourcemode_columns ++;
67: n_line /= 10;
68: }
69:
70: sourcemode_line_cnt = 1; // initialize
71: }
72:
73: std::string Reader::sourcemode_linenumber()
74: {
75: char tmp[16];
76: sprintf(tmp,"%d",sourcemode_line_cnt++);
77:
78: int numlen = strlen(tmp);
79: int pudding = sourcemode_columns - numlen;
80:
81: char buf[256];
82: buf[0] = '\0';
83: while( pudding-- )
84: strcat(buf," ");
85: strcat(buf,tmp);
86: strcat(buf,": ");
87:
88: return buf;
89: }
90:
91: bool Reader::open(const char *fnam,int readopt,const char *filter)
92: {
93: FILE *newfp;
94: if( 0 != strcmp(fnam,"STDIN") ){
95: if( filter )
96: newfp = popen(filter,"r");
97: else
98: newfp = fopen(fnam,"r");
99: }
100: else
101: newfp = stdin;
102:
103: if( ! newfp ){
104: // ERROR
105: return false;
106: }
107:
108: infoque.push_front( fileinfo_t(fnam,readopt) );
109: fileinfo_t &info = infoque[0];
110:
111: int c;
112: while( EOF != (c=fgetc(newfp)) )
113: info.buf.push_back((char)c);
114: rewind(); // ポインタを先頭にセット
115:
116: if( newfp != stdin )
117: fclose(newfp);
118:
119: if( readopt&READOPT_INCLUDE_NUMBERED )
120: set_sourcemode_columns();
121:
122: return true;
123: }
124:
125: int Reader::get()
126: {
127: if( infoque.size() > 0 ){
128: fileinfo_t &info = infoque[0];
129: if( info.buf_pnt == info.buf.end() )
130: return EOF;
131: else
132: return *info.buf_pnt++;
133: }
134:
135: return EOF;
136: }
137:
138: void Reader::rewind()
139: {
140: if( infoque.size() > 0 ){
141: infoque[0].buf_pnt = infoque[0].buf.begin();
142: }
143: }
144:
145: const char* Reader::get_fnam()
146: {
147: if( infoque.size() > 0 )
148: return infoque[0].fnam.c_str();
149: else
150: return NULL;
151: }
152:
153: int Reader::get_linenum()
154: {
155: if( infoque.size() > 0)
156: return infoque[0].linenum;
157: else
158: return -1;
159: }
160:
161: std::string Reader::number_info()
162: {
163: std::string num_info;
164:
165: int i = (int)infoque.size() - 1;
166: while(i >= 0){
167: char tmp[16];
168: sprintf(tmp,"%d",infoque[0].linenum);
169: if( num_info == "" )
170: num_info = tmp;
171: else
172: num_info += (std::string)tmp + '-';
173: i--;
174: }
175:
176: return num_info;
177: }
178:
179:
180: static int _mb_check_euc(unsigned char c)
181: {
182: if( c == 0x8F ) // 補助漢字(3バイト文字)
183: return 3;
184: else if( c >= 0xA1 && c <= 0xFE ) // 2バイト文字
185: return 2;
186: else
187: return 1; // 半角
188: }
189:
190: static int _mb_check_sjis(unsigned char c)
191: {
192: if( c >= 0x81 && c <= 0x9F ||
193: c >= 0xE0 && c <= 0xEF )
194: return 2; // 2バイト文字
195: else
196: return 1; // 半角 or 半角カナ
197: }
198:
199: static int _mb_check_iso88591(unsigned char c)
200: {
201: return 1;
202: }
203:
204: static char* _ins_esc(char *s,const char *repstr)
205: {
206: int repstr_len = strlen(repstr);
207: memmove(s+repstr_len,s+1,strlen(s+1)+1);
208: memcpy(s,repstr,repstr_len);
209: return s+repstr_len;
210: }
211:
212: static void _text_chars_escape(char *s)
213: {
214: int (*mb_check)(unsigned char);
215:
216: const char *charset = CHARSET;
217: if( strcasecmp(charset,"EUC-JP") == 0 )
218: mb_check = _mb_check_euc;
219: else if( strcasecmp(charset,"Shift_JIS") == 0 )
220: mb_check = _mb_check_sjis;
221: else if( strcasecmp(charset,"iso-8859-1") == 0 )
222: mb_check = _mb_check_iso88591;
223: else{
224: error("unknown charset `%s'",charset); return;
225: }
226:
227:
228: std::string tabspace = "";
229:
230: unsigned char c;
231: while( '\0' != (c=(unsigned char)*s) ){
232: int chlen = (*mb_check)(c);
233: if( chlen > 1 ){
234: for(int i=0; i<chlen && *s; i++,s++)
235: ; // skip
236: }else{ // 1バイト文字
237: switch( (char)c ){
238: case '<': s = _ins_esc(s,"<"); break;
239: case '>': s = _ins_esc(s,">"); break;
240: case '"': s = _ins_esc(s,"""); break;
241: case '&': s = _ins_esc(s,"&"); break;
242: case '\n': s= _ins_esc(s,"<br>\n"); break;
243: default:
244: if( isspace((char)c) ){
245: if( (char)c != '\t' )
246: s = _ins_esc(s," ");
247: else{
248: if( tabspace=="" ){
249: int tab_spaces = atoi(TAB_SPACES);
250: for(int i=0;i<tab_spaces;i++)
251: tabspace += " ";
252: }
253: s = _ins_esc(s,tabspace.c_str());
254: }
255: }
256: else if( !isascii((char)c) ){
257: char tmp[8];
258: sprintf(tmp,"&#%d;",(int)c);
259: s = _ins_esc(s,tmp);
260: }
261: else
262: s++;
263: break;
264: }
265: }
266: }
267:
268: }
269:
270:
271: void Reader::set_current_header(const char *header)
272: {
273: infoque[0].header = header;
274: }
275:
276: void Reader::set_current_footer(const char *footer)
277: {
278: infoque[0].footer = footer;
279: }
280:
281:
282: void Reader::set_textmode()
283: {
284: infoque[0].need_head_signal = infoque[0].need_foot_signal = true;
285: }
286:
287: int Reader::read_line(std::string &buf)
288: {
289: static char rd_buf[MAX_LINE_LEN];
290:
291: if( infoque.size() == 0 ){
292: return EOF;
293: }
294:
295: fileinfo_t &info = infoque[0];
296:
297: if( info.header.size() > 0 ){
298: buf = info.header;
299: info.header = ""; // invalidate
300: return buf.length();
301: }
302: if( info.need_head_signal ){
303: info.need_head_signal = false; // incvalidate;
304: buf = READER_INCLUDE_BEGIN + info.fnam ;
305: return buf.length();
306: }
307:
308:
309: rd_buf[0] = '\0';
310:
311: int i=0;
312:
313: int c;
314: while(1){
315: c = get();
316: if( c != EOF ){
317:
318: rd_buf[i++] = (unsigned char)c;
319: if( c == '\n' ){
320: info.linenum ++;
321: break;
322: }
323: }else if( i>0 ){
324: break;
325: }else if(info.need_foot_signal ){
326: info.need_foot_signal = false; //invalidate;
327: buf = READER_INCLUDE_END;
328: return strlen(READER_INCLUDE_END);
329: }else if(info.footer.size() > 0 ){
330: buf = info.footer;
331: info.footer = ""; // invalidate
332: return buf.length();
333: }else if( infoque.size() > 0 ){
334: infoque.pop_front();
335: return read_line(buf);
336: }else{
337: return EOF;
338:
339: }
340:
341: }
342:
343: rd_buf[i]='\0';
344:
345: if( info.readopt & (READOPT_INCLUDE_HTML|READOPT_INCLUDE_TEXT|READOPT_TEXTMODE|READOPT_INCLUDE_NUMBERED) ){
346:
347: if( info.readopt==READOPT_TEXTMODE && strncmp(rd_buf,"@endtext",8)==0 ){
348: info.readopt = 0; // テキストモードから抜けた
349: return read_line(buf);
350: }
351:
352: if( info.readopt & (READOPT_INCLUDE_TEXT|READOPT_INCLUDE_NUMBERED|READOPT_TEXTMODE) ){
353: _text_chars_escape(rd_buf); // < > などをエスケープ
354: }
355:
356: if( info.readopt & READOPT_INCLUDE_NUMBERED ){
357: buf = sourcemode_linenumber() + rd_buf;
358: }
359: else if( rd_buf[0] == '@' || rd_buf[0] == ';' ){
360: char tmp[8];
361: sprintf(tmp,"&#%d;",(int)rd_buf[0]);
362: buf = tmp;
363: buf += rd_buf+1;
364: }
365: else
366: buf = rd_buf;
367: }else
368: buf = rd_buf;
369:
370:
371: return i;
372: }
373:
374:
375: char* _parse_next(char *p,char **start_r)
376: {
377: while( isspace(*p) )
378: p++;
379:
380: char mode = 0;
381: if( *p == '\"' || *p == '\''){ // " '
382: mode = *p;
383: }else if( *p == '(' ){ // (
384: mode = ')';
385: }
386:
387: if( mode )
388: p++;
389:
390: *start_r = p;
391:
392: if( mode )
393: while( *p && *p!='\n' && *p!=mode )
394: p++;
395: else
396: while( *p && ! isspace(*p) )
397: p++;
398:
399: if( *p=='\0' )
400: return NULL;
401:
402: //else
403:
404: *p = '\0';
405: p++;
406: while( isspace(*p) )
407: p++;
408:
409: if( !*p || (*p==';' && *(p+1)==';') )
410: return NULL; // 終端,もしくは,残りは全てコメントである
411: else
412: return p;
413:
414:
415: }
416:
417: int parse(const char *line,std::vector<std::string> &vec)
418: {
419: char *b = new char[strlen(line)+1];
420: strcpy(b,line);
421:
422: char *p = b;
423: p++; // 先頭の@をスキップ
424:
425: vec.clear();
426: char *next_p;
427: do{
428: next_p = _parse_next(p,&p);
429: vec.push_back(p);
430: //printf("'%s' ",p);
431: p = next_p;
432: }while( p != NULL );
433:
434: return vec.size();
435: }
436:
437: static bool is_nullline(std::string &s)
438: {
439: if( s.length() > 1 && s[0]==';' && s[1]==';' )
440: return true; // コメント行なので真
441:
442: const char *p = s.c_str();
443: while( *p && isspace(*p))
444: p++;
445: return *p=='\0'; // 空もしくはスペース要素だけからなる行であるので真
446: }
447:
448:
449: #define TEMPFILENAME "_ezht_files_inc_.tmp"
450: static bool tempfile_created = false;
451:
452: static void cmd_files(const std::vector<std::string> &args)
453: {
454: if( args.size() < 4 ){
455: error("@files: too few arguments");
456: return;
457: }
458:
459: std::string mode = args[1];
460: if( mode != "text" &&
461: mode != "numbered" ){
462: error("@files: unknown mode `%s'",mode.c_str());
463: return;
464: }
465:
466: char curdir[1024];
467: getcwd(curdir, sizeof curdir);
468: std::string dir = args[2];
469:
470: // ディレクトリ変更
471: if( 0 != chdir(dir.c_str()) ){
472: error("@files: cannot enter directory `%s': %s",dir.c_str(),strerror(errno));
473: return;
474: }
475:
476: std::list<std::string> results;
477:
478: int n = (int) args.size();
479: for(int i=3; i<n; i++){
480: const char *pattern = args[i].c_str();
481: if( 0 == ezht_glob(pattern,results) ){
482: warn("@files: pattern `%s' not matched",pattern);
483: }
484: }
485:
486: chdir(curdir); // 戻す
487:
488:
489: FILE *tmpf = fopen(TEMPFILENAME,"w");
490:
491:
492: if( ! tmpf ){
493: error("can't create temporary file: %s",strerror(errno));
494: return;
495: }
496:
497: tempfile_created = true;
498:
499: for(std::list<std::string>::iterator p=results.begin(); p!=results.end(); p++){
500: std::string &fnam = *p;
501: std::string path = dir + '/' + fnam;
502: fprintf(tmpf,"@section \"%s\"\n",fnam.c_str());
503: fprintf(tmpf,"@include \"%s\" %s\n",path.c_str(),mode.c_str());
504: fprintf(tmpf,"@end\n");
505: }
506:
507: fclose(tmpf);
508:
509: if( ! reader.open(TEMPFILENAME) ){
510: error("@include: Unable to open temporary file `%s' ",TEMPFILENAME);
511: }
512:
513: }
514:
515: void reader_tempfile_delete()
516: {
517: if( tempfile_created )
518: unlink(TEMPFILENAME);
519: }
520:
521: const char * getline(bool accept_terminator)
522: {
523: static std::string buf;
524:
525: while( EOF != reader.read_line(buf) ){
526:
527: if( buf.length() > 0 && buf[0]=='@' ){
528: std::vector<std::string> vec;
529: parse(buf.c_str(),vec);
530:
531: std::string cmd = vec[0];
532:
533: if( cmd == "include" )
534: cmd_include(vec);
535: else if( cmd == "jump" ){
536: cmd_jump(vec,buf);
537: return buf.c_str();
538: }
539: else if( cmd == "begintext" )
540: cmd_begintext(vec);
541: else if( cmd == "image" ){
542: cmd_image(vec,buf);
543: return buf.c_str();
544: }else if( cmd == "refer" ){
545: cmd_refer(vec,buf);
546: return buf.c_str();
547: }else if( cmd == "files" ){
548: cmd_files(vec);
549: }
550: else if( cmd == "end" ){
551: if( ! accept_terminator ){
552: error("misplaced @end");
553: }else if( vec.size() != 1 ){
554: error("unknown parameter(s) in @end");
555: }else
556: return "@end";
557: }else{
558: return buf.c_str();
559: }
560:
561: }else if( is_nullline(buf) ){
562: // コメント行もしくは空行なので無視
563: ;
564: }else
565: return buf.c_str();
566: }
567:
568:
569:
570: return NULL;
571: }