戻る 目次へ
§5.1.14 reader.cc
戻る 目次へ

  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,"&nbsp;");
 85:   strcat(buf,tmp);
 86:   strcat(buf,":&nbsp;");
 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,"&lt;"); break;
239:       case '>': s = _ins_esc(s,"&gt;"); break;
240:       case '"': s = _ins_esc(s,"&quot;"); break;
241:       case '&': s = _ins_esc(s,"&amp;"); 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,"&nbsp;");
247:           else{
248:             if( tabspace=="" ){
249:               int tab_spaces = atoi(TAB_SPACES);
250:               for(int i=0;i<tab_spaces;i++)
251:                 tabspace += "&nbsp;";
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: }




Copyright 2004 Tradcrafts. ALL RIGHTS RESERVED.