mucgly 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/mucgly/mucgly.c CHANGED
@@ -1,3 +1,14 @@
1
+ /**
2
+ * @file mucgly.c
3
+ * @author Tero Isannainen <tero.isannainen@gmail.com>
4
+ * @date Sat Apr 23 12:41:02 2016
5
+ *
6
+ * @brief Inline macro processor.
7
+ *
8
+ * See README.rdoc for full documentation of Mucgly.
9
+ */
10
+
11
+
1
12
  #include <glib.h>
2
13
  #include <glib/gstdio.h>
3
14
  #include <ruby.h>
@@ -18,10 +29,14 @@ void mucgly_main( int argc, char** argv );
18
29
  * Debugging:
19
30
  * ------------------------------------------------------------ */
20
31
 
21
- /* Enable the define is input char based debugging is required. Set
22
- GDB breakpoint at mucgly_debug. */
32
+ /**
33
+ * Enable the define if input char based debugging is required. Set
34
+ * GDB breakpoint at mucgly_debug.
35
+ */
23
36
  void mucgly_debug( void ) {}
37
+
24
38
  #if 0
39
+ /** Special char for invoking the debugger. */
25
40
  # define DEBUG_CHAR '~'
26
41
  #endif
27
42
 
@@ -52,33 +67,55 @@ void mucgly_debug( void ) {}
52
67
  */
53
68
 
54
69
 
70
+ /** Default value for hookbeg. */
55
71
  #define HOOKBEG_DEFAULT "-<"
72
+
73
+ /** Default value for hookend. */
56
74
  #define HOOKEND_DEFAULT ">-"
75
+
76
+ /** Default value for hookesc. */
57
77
  #define HOOKESC_DEFAULT "\\"
58
78
 
79
+ /** Multihook count limit, also array size. */
80
+ #define MULTI_LIMIT 128
81
+
82
+
83
+ /**
84
+ * Pair of hooks for macro beginning and end.
85
+ */
86
+ typedef struct hookpair_s {
87
+ gchar* beg; /**< Hookbeg for input file. */
88
+ gchar* end; /**< Hookend for input file. */
89
+ } hookpair_t;
59
90
 
60
91
 
61
92
  /**
62
93
  * Stackfile is an entry in the Filestack. Stackfile is the input file
63
94
  * for Mucgly.
64
95
  */
65
- struct stackfile_s {
96
+ typedef struct stackfile_s {
66
97
 
67
- gchar* filename; /**< Filename. */
68
- FILE* fh; /**< IO stream handle. */
69
- GString* buf; /**< Put-back buffer (oldest-is-last). */
98
+ gchar* filename; /**< Filename. */
99
+ FILE* fh; /**< IO stream handle. */
100
+ GString* buf; /**< Put-back buffer (oldest-is-last). */
70
101
 
71
- int lineno; /**< Line number (0->). */
72
- int column; /**< Line column (0->). */
73
- int old_column; /**< Prev column (used with putback). */
102
+ int lineno; /**< Line number (0->). */
103
+ int column; /**< Line column (0->). */
104
+ int old_column; /**< Prev column (used with putback). */
105
+
106
+ gboolean macro; /**< Macro active. */
107
+ int macro_line; /**< Macro start line. */
108
+ int macro_col; /**< Macro start column. */
109
+ gboolean eat_tail; /**< Eat the char after macro (if not EOF). */
110
+
111
+ hookpair_t hook; /**< Pair of hooks for macro boundary. */
112
+ gchar* hookesc; /**< Hookesc for input file. */
74
113
 
75
- gboolean macro; /**< Macro active. */
76
- int macro_line; /**< Macro start line. */
77
- int macro_col; /**< Macro start column. */
114
+ hookpair_t* multi; /**< Pairs of hooks for multi-hooking. */
115
+ int multi_cnt; /**< Number of pairs in multi-hooking. */
78
116
 
79
- gchar* hookbeg; /**< Hookbeg for input file. */
80
- gchar* hookend; /**< Hookend for input file. */
81
- gchar* hookesc; /**< Hookesc for input file. */
117
+ /** Current hook, as stack to support nesting macros. */
118
+ GList* curhook;
82
119
 
83
120
  /** Hookesc is same as hookbeg. Speeds up input processing. */
84
121
  gboolean hook_esc_eq_beg;
@@ -86,11 +123,10 @@ struct stackfile_s {
86
123
  /** Hookesc is same as hookend. Speeds up input processing. */
87
124
  gboolean hook_esc_eq_end;
88
125
 
89
- /** Hookesc is same as hookend. Speeds up input processing. */
126
+ /** Lookup-table for the first chars of hooks. Speeds up input processing. */
90
127
  guchar hook_1st_chars[ 256 ];
91
128
 
92
- };
93
- typedef struct stackfile_s stackfile_t;
129
+ } stackfile_t;
94
130
 
95
131
 
96
132
 
@@ -102,10 +138,9 @@ typedef struct stackfile_s stackfile_t;
102
138
  * EOF is encountered, it is popped of the stack. This makes the
103
139
  * character flow continuous from the parser point of view.
104
140
  */
105
- struct filestack_s {
141
+ typedef struct filestack_s {
106
142
  GList* file; /**< Stack of files. */
107
- };
108
- typedef struct filestack_s filestack_t;
143
+ } filestack_t;
109
144
 
110
145
 
111
146
 
@@ -114,19 +149,19 @@ typedef struct filestack_s filestack_t;
114
149
  * from the default output to another output file temporarely. Note
115
150
  * that the diverted output stream has to be closed as well.
116
151
  */
117
- struct outfile_s {
118
- gchar* filename; /**< Filename. */
119
- FILE* fh; /**< Stream handle. */
120
- int lineno; /**< Line number (0->). */
121
- };
122
- typedef struct outfile_s outfile_t;
152
+ typedef struct outfile_s {
153
+ gchar* filename; /**< Filename. */
154
+ FILE* fh; /**< Stream handle. */
155
+ int lineno; /**< Line number (0->). */
156
+ gboolean blocked; /**< Blocked output for IO stream. */
157
+ } outfile_t;
123
158
 
124
159
 
125
160
 
126
161
  /**
127
162
  * Parser state for Mucgly.
128
163
  */
129
- struct pstate_s {
164
+ typedef struct pstate_s {
130
165
  filestack_t* fs; /**< Stack of input streams. */
131
166
 
132
167
  GString* check_buf; /**< Preview buffer. */
@@ -138,8 +173,11 @@ struct pstate_s {
138
173
  GList* output; /**< Stack of output streams. */
139
174
 
140
175
  gboolean flush; /**< Flush out-stream immediately. */
141
- };
142
- typedef struct pstate_s pstate_t;
176
+
177
+ gboolean post_push; /**< Move up in fs after macro processing. */
178
+ gboolean post_pop; /**< Move down in fs after macro processing. */
179
+
180
+ } pstate_t;
143
181
 
144
182
 
145
183
  /** Hook type enum. */
@@ -149,11 +187,9 @@ typedef enum hook_e { hook_none, hook_end, hook_beg, hook_esc } hook_t;
149
187
  /** Current filestack from pstate_t (for convenience). */
150
188
  #define ps_has_file(ps) ((stackfile_t*)(ps)->fs->file)
151
189
 
152
-
153
190
  /** Current stackfile from pstate_t (for convenience). */
154
191
  #define ps_topfile(ps) ((stackfile_t*)(ps)->fs->file->data)
155
192
 
156
-
157
193
  /** Current stackfile from filestack_t (for convenience). */
158
194
  #define fs_topfile(fs) ((stackfile_t*)(fs)->file->data)
159
195
 
@@ -166,7 +202,7 @@ typedef enum hook_e { hook_none, hook_end, hook_beg, hook_esc } hook_t;
166
202
  /** Default hook settings. */
167
203
  stackfile_t* stack_default = NULL;
168
204
 
169
- /** Pointer to current file. Usefull for debugging. */
205
+ /** Pointer to current file. Useful for debugging. */
170
206
  stackfile_t* csf;
171
207
 
172
208
  /** Ruby side reference to parser. */
@@ -178,7 +214,9 @@ pstate_t* ruby_ps;
178
214
  * Function declarations:
179
215
  * ------------------------------------------------------------ */
180
216
 
181
- void sf_set_hook( stackfile_t* sf, hook_t hook, char* value );
217
+ void sf_update_hook_cache( stackfile_t* sf );
218
+ void sf_multi_hook( stackfile_t* sf, const char* beg, const char* end );
219
+ gchar* ps_current_hookend( pstate_t* ps );
182
220
 
183
221
 
184
222
 
@@ -189,10 +227,10 @@ void sf_set_hook( stackfile_t* sf, hook_t hook, char* value );
189
227
 
190
228
  /**
191
229
  * Compare two strings upto length of str1.
192
- *
230
+ *
193
231
  * @param str1 String 1.
194
232
  * @param str2 String 2.
195
- *
233
+ *
196
234
  * @return Match length (0 for no match).
197
235
  */
198
236
  int len_str_cmp( char* str1, char* str2 )
@@ -208,7 +246,7 @@ int len_str_cmp( char* str1, char* str2 )
208
246
 
209
247
  /**
210
248
  * Common routine for user info message output.
211
- *
249
+ *
212
250
  * @param sf Currently processed file (or NULL).
213
251
  * @param infotype Severity type.
214
252
  * @param format Message (printf) formatter.
@@ -255,7 +293,7 @@ void mucgly_user_info( stackfile_t* sf, char* infotype, char* format, va_list ap
255
293
 
256
294
  /**
257
295
  * Report warning to user (no exit).
258
- *
296
+ *
259
297
  * @param sf Current input file.
260
298
  * @param format Message formatter.
261
299
  */
@@ -270,7 +308,7 @@ void mucgly_warn( stackfile_t* sf, char* format, ... )
270
308
 
271
309
  /**
272
310
  * Report error to user (and exit).
273
- *
311
+ *
274
312
  * @param sf Current input file.
275
313
  * @param format Message formatter.
276
314
  */
@@ -286,7 +324,7 @@ void mucgly_error( stackfile_t* sf, char* format, ... )
286
324
 
287
325
  /**
288
326
  * Report fatal error to user (and exit).
289
- *
327
+ *
290
328
  * @param sf Current input file.
291
329
  * @param format Message formatter.
292
330
  */
@@ -300,12 +338,40 @@ void mucgly_fatal( stackfile_t* sf, char* format, ... )
300
338
  }
301
339
 
302
340
 
341
+ /**
342
+ * Duplicate hookpair content.
343
+ *
344
+ * @param from Source.
345
+ * @param to Destination.
346
+ *
347
+ * @return Destination.
348
+ */
349
+ hookpair_t* hookpair_dup( hookpair_t* from, hookpair_t* to )
350
+ {
351
+ to->beg = g_strdup( from->beg );
352
+ to->end = g_strdup( from->end );
353
+ return to;
354
+ }
355
+
356
+
357
+ /**
358
+ * Free allocated hookpair memory.
359
+ *
360
+ * @param pair Pair to dealloc.
361
+ */
362
+ void hookpair_del( hookpair_t* pair )
363
+ {
364
+ g_free( pair->beg );
365
+ g_free( pair->end );
366
+ }
367
+
368
+
303
369
  /**
304
370
  * Create new Stackfile.
305
- *
371
+ *
306
372
  * @param filename Filename (NULL for stdin).
307
373
  * @param inherit Inherit hooks source (NULL for defaults).
308
- *
374
+ *
309
375
  * @return Stackfile.
310
376
  */
311
377
  stackfile_t* sf_new( gchar* filename, stackfile_t* inherit )
@@ -347,25 +413,41 @@ stackfile_t* sf_new( gchar* filename, stackfile_t* inherit )
347
413
  sf->macro = FALSE;
348
414
  sf->macro_line = 0;
349
415
  sf->macro_col = 0;
416
+ sf->eat_tail = FALSE;
417
+
418
+ sf->multi = NULL;
419
+ sf->multi_cnt = 0;
420
+
421
+ sf->curhook = NULL;
350
422
 
351
423
 
352
424
  if ( inherit )
353
425
  {
354
426
  /* Inherited hook values. */
355
- sf->hookbeg = g_strdup( inherit->hookbeg );
356
- sf->hookend = g_strdup( inherit->hookend );
427
+ hookpair_dup( &inherit->hook, &sf->hook );
357
428
  sf->hookesc = g_strdup( inherit->hookesc );
429
+
430
+ /* Setup fast-lookup caches. */
431
+ sf_update_hook_cache( sf );
432
+
433
+ if ( inherit->multi )
434
+ {
435
+ for ( int i = 0; i < inherit->multi_cnt; i++ )
436
+ {
437
+ sf_multi_hook( sf, inherit->multi[i].beg, inherit->multi[i].end );
438
+ }
439
+ }
358
440
  }
359
441
  else
360
442
  {
361
443
  /* Default hook values. */
362
- sf->hookbeg = g_strdup( HOOKBEG_DEFAULT );
363
- sf->hookend = g_strdup( HOOKEND_DEFAULT );
444
+ sf->hook.beg = g_strdup( HOOKBEG_DEFAULT );
445
+ sf->hook.end = g_strdup( HOOKEND_DEFAULT );
364
446
  sf->hookesc = g_strdup( HOOKESC_DEFAULT );
365
- }
366
447
 
367
- /* Setup fast-lookup caches. */
368
- sf_set_hook( sf, hook_none, NULL );
448
+ /* Setup fast-lookup caches. */
449
+ sf_update_hook_cache( sf );
450
+ }
369
451
 
370
452
  return sf;
371
453
  }
@@ -373,7 +455,7 @@ stackfile_t* sf_new( gchar* filename, stackfile_t* inherit )
373
455
 
374
456
  /**
375
457
  * Store macro start info.
376
- *
458
+ *
377
459
  * @param sf Stackfile.
378
460
  */
379
461
  void sf_mark_macro( stackfile_t* sf )
@@ -386,7 +468,7 @@ void sf_mark_macro( stackfile_t* sf )
386
468
 
387
469
  /**
388
470
  * Reset macro start info.
389
- *
471
+ *
390
472
  * @param sf Stackfile.
391
473
  */
392
474
  void sf_unmark_macro( stackfile_t* sf )
@@ -397,7 +479,7 @@ void sf_unmark_macro( stackfile_t* sf )
397
479
 
398
480
  /**
399
481
  * Free Stackfile and close the file stream.
400
- *
482
+ *
401
483
  * @param sf Stackfile.
402
484
  */
403
485
  void sf_rem( stackfile_t* sf )
@@ -411,42 +493,44 @@ void sf_rem( stackfile_t* sf )
411
493
 
412
494
  g_free( sf->filename );
413
495
 
414
- g_free( sf->hookbeg );
415
- g_free( sf->hookend );
496
+ hookpair_del( &sf->hook );
416
497
  g_free( sf->hookesc );
417
498
 
499
+ if ( sf->multi )
500
+ {
501
+ for ( int i = 0; i < sf->multi_cnt; i++ )
502
+ hookpair_del( &sf->multi[i] );
503
+ g_free( sf->multi );
504
+ }
505
+
418
506
  g_free( sf );
419
507
  }
420
508
 
421
509
 
422
510
  /**
423
511
  * Get char from Stackfile.
424
- *
512
+ *
425
513
  * @param sf Stackfile.
426
- *
514
+ *
427
515
  * @return Char or EOF.
428
516
  */
429
517
  int sf_get( stackfile_t* sf )
430
518
  {
431
519
  int ret;
432
-
520
+
521
+ re_read:
433
522
  if ( sf->buf->len > 0 )
434
523
  {
435
-
436
524
  /* Pending chars in buffer, don't need to read file (yet). */
437
-
438
525
  int pos = sf->buf->len-1;
439
526
 
440
527
  /* Use buffer as stack with oldest (first to use) on right. */
441
528
  ret = sf->buf->str[ pos ];
442
529
  g_string_truncate( sf->buf, pos );
443
-
444
530
  }
445
531
  else
446
532
  {
447
-
448
533
  /* Read from actual file stream. */
449
-
450
534
  #ifdef DEBUG_CHAR
451
535
  ret = fgetc( sf->fh );
452
536
  if ( DEBUG_CHAR == (char) ret )
@@ -475,16 +559,24 @@ int sf_get( stackfile_t* sf )
475
559
  }
476
560
  }
477
561
 
562
+ if ( sf->eat_tail == TRUE )
563
+ {
564
+ /* Eat tail if not in EOF. */
565
+ sf->eat_tail = FALSE;
566
+ if ( ret != EOF )
567
+ goto re_read;
568
+ }
569
+
478
570
  return ret;
479
571
  }
480
572
 
481
573
 
482
574
  /**
483
575
  * Put char back to Stackfile.
484
- *
576
+ *
485
577
  * @param sf Stackfile.
486
578
  * @param c char.
487
- *
579
+ *
488
580
  * @return TRUE.
489
581
  */
490
582
  gboolean sf_put( stackfile_t* sf, char c )
@@ -508,48 +600,133 @@ gboolean sf_put( stackfile_t* sf, char c )
508
600
 
509
601
 
510
602
  /**
511
- * Set hook value.
512
- *
603
+ * Update the hooks related cache/lookup entries.
604
+ *
513
605
  * @param sf Stackfile.
514
- * @param hook Hook type.
606
+ */
607
+ void sf_update_hook_cache( stackfile_t* sf )
608
+ {
609
+ if ( sf->multi )
610
+ {
611
+ /* Esc can never match multihooks. */
612
+ sf->hook_esc_eq_beg = FALSE;
613
+ sf->hook_esc_eq_end = FALSE;
614
+
615
+ /* Add the latest addition to 1st char lookup. */
616
+ sf->hook_1st_chars[ sf->multi[ sf->multi_cnt-1 ].beg[0] ] = 1;
617
+ sf->hook_1st_chars[ sf->multi[ sf->multi_cnt-1 ].end[0] ] = 1;
618
+
619
+ sf->hook_1st_chars[ sf->hookesc[0] ] = 1;
620
+ }
621
+ else
622
+ {
623
+ /* Store these equalities for speed-up. */
624
+ if ( !g_strcmp0( sf->hookesc, sf->hook.beg ) )
625
+ sf->hook_esc_eq_beg = TRUE;
626
+ else
627
+ sf->hook_esc_eq_beg = FALSE;
628
+
629
+ if ( !g_strcmp0( sf->hookesc, sf->hook.end ) )
630
+ sf->hook_esc_eq_end = TRUE;
631
+ else
632
+ sf->hook_esc_eq_end = FALSE;
633
+
634
+ /* Initialize 1st char lookup. */
635
+ memset( sf->hook_1st_chars, 0, 256 * sizeof( guchar ) );
636
+
637
+ /* Hook 1st char lookup. */
638
+ sf->hook_1st_chars[ sf->hook.beg[0] ] = 1;
639
+ sf->hook_1st_chars[ sf->hook.end[0] ] = 1;
640
+ sf->hook_1st_chars[ sf->hookesc[0] ] = 1;
641
+ }
642
+ }
643
+
644
+
645
+ /**
646
+ * Set hook value.
647
+ *
648
+ * @param sf Stackfile.
649
+ * @param hook Hook type.
515
650
  * @param value New hook value.
516
651
  */
517
652
  void sf_set_hook( stackfile_t* sf, hook_t hook, char* value )
518
653
  {
654
+ /* Check that we are not in multi-hook mode. */
655
+ if ( sf->multi && hook != hook_esc )
656
+ {
657
+ /* Disable multi-hook mode. */
658
+ for ( int i = 0; i < sf->multi_cnt; i++ )
659
+ hookpair_del( &sf->multi[i] );
660
+ g_free( sf->multi );
661
+ sf->multi_cnt = 0;
662
+ }
519
663
 
520
664
  /* Set values. */
521
665
  switch ( hook )
522
666
  {
523
- case hook_beg: g_free( sf->hookbeg ); sf->hookbeg = g_strdup( value ); break;
524
- case hook_end: g_free( sf->hookend ); sf->hookend = g_strdup( value ); break;
525
- case hook_esc: g_free( sf->hookesc ); sf->hookesc = g_strdup( value ); break;
667
+ case hook_beg: g_free( sf->hook.beg ); sf->hook.beg = g_strdup( value ); break;
668
+ case hook_end: g_free( sf->hook.end ); sf->hook.end = g_strdup( value ); break;
669
+ case hook_esc:
670
+ {
671
+ /* Remove old esc from 1st char lookup. */
672
+ sf->hook_1st_chars[ sf->hookesc[0] ] = 0;
673
+ g_free( sf->hookesc );
674
+ sf->hookesc = g_strdup( value );
675
+ break;
676
+ }
526
677
  default: break;
527
678
  }
528
679
 
680
+ sf_update_hook_cache( sf );
681
+ }
529
682
 
530
- /* Store these equalities for speed-up. */
531
683
 
532
- if ( !g_strcmp0( sf->hookesc, sf->hookbeg ) )
533
- sf->hook_esc_eq_beg = TRUE;
534
- else
535
- sf->hook_esc_eq_beg = FALSE;
684
+ /**
685
+ * Add multi-hook pair.
686
+ *
687
+ * @param sf Stackfile.
688
+ * @param beg Hookbeg of pair.
689
+ * @param end Hookend of pair.
690
+ */
691
+ void sf_multi_hook( stackfile_t* sf, const char* beg, const char* end )
692
+ {
693
+ /* Check that hooks don't match escape. */
694
+ if ( !g_strcmp0( sf->hookesc, beg )
695
+ || !g_strcmp0( sf->hookesc, end ) )
696
+ {
697
+ g_print( "mucgly: Esc hook is not allowed to match multihooks\n" );
698
+ exit( EXIT_FAILURE );
699
+ }
536
700
 
537
- if ( !g_strcmp0( sf->hookesc, sf->hookend ) )
538
- sf->hook_esc_eq_end = TRUE;
539
- else
540
- sf->hook_esc_eq_end = FALSE;
701
+ if ( sf->multi == NULL )
702
+ {
703
+ /* Allocate enough pairs. */
704
+ sf->multi = g_new0( hookpair_t, MULTI_LIMIT );
705
+ sf->multi[0] = (hookpair_t) {0,0};
706
+ sf->multi_cnt = 0;
707
+
708
+ /* Clear non-multi-mode lookups. */
709
+ memset( sf->hook_1st_chars, 0, 256 * sizeof( guchar ) );
710
+ }
541
711
 
542
- /* Hook 1st char lookup. */
543
- memset( sf->hook_1st_chars, 0, 256 * sizeof( guchar ) );
544
- sf->hook_1st_chars[ sf->hookbeg[0] ] = 1;
545
- sf->hook_1st_chars[ sf->hookend[0] ] = 1;
546
- sf->hook_1st_chars[ sf->hookesc[0] ] = 1;
712
+ if ( sf->multi_cnt >= (MULTI_LIMIT-1) )
713
+ {
714
+ g_print( "mucgly: Too many multihooks, 127 allowed!\n" );
715
+ exit( EXIT_FAILURE );
716
+ }
717
+
718
+ sf->multi[sf->multi_cnt].beg = g_strdup( beg );
719
+ sf->multi[sf->multi_cnt].end = g_strdup( end );
720
+ sf->multi_cnt++;
721
+ sf->multi[sf->multi_cnt] = (hookpair_t) {0,0};
722
+
723
+ sf_update_hook_cache( sf );
547
724
  }
548
725
 
549
726
 
550
727
  /**
551
728
  * Create new Filestack.
552
- *
729
+ *
553
730
  * @return Filestack.
554
731
  */
555
732
  filestack_t* fs_new( void )
@@ -564,9 +741,9 @@ filestack_t* fs_new( void )
564
741
 
565
742
  /**
566
743
  * Free Filestack and close all Stackfiles.
567
- *
744
+ *
568
745
  * @param fs Filestack.
569
- *
746
+ *
570
747
  * @return NULL.
571
748
  */
572
749
  filestack_t* fs_rem( filestack_t* fs )
@@ -587,7 +764,7 @@ filestack_t* fs_rem( filestack_t* fs )
587
764
 
588
765
  /**
589
766
  * Push file on top of Filestack.
590
- *
767
+ *
591
768
  * @param fs Filestack.
592
769
  * @param filename New top file.
593
770
  */
@@ -596,7 +773,7 @@ void fs_push_file( filestack_t* fs, gchar* filename )
596
773
  stackfile_t* sf;
597
774
 
598
775
  if ( fs->file )
599
- /* Additinal file. */
776
+ /* Additional file. */
600
777
  sf = sf_new( filename, fs_topfile(fs) );
601
778
  else
602
779
  /* First file, i.e. inherit stack_default hooks. */
@@ -610,9 +787,23 @@ void fs_push_file( filestack_t* fs, gchar* filename )
610
787
  }
611
788
 
612
789
 
790
+ /**
791
+ * Push file on top of Filestack for later use (i.e. current macro is
792
+ * completely processed).
793
+ *
794
+ * @param fs Filestack.
795
+ * @param filename New top file.
796
+ */
797
+ void fs_push_file_delayed( filestack_t* fs, gchar* filename )
798
+ {
799
+ fs_push_file( fs, filename );
800
+ fs->file = fs->file->next;
801
+ }
802
+
803
+
613
804
  /**
614
805
  * Pop file from top of Filestack. File is closed.
615
- *
806
+ *
616
807
  * @param fs Filestack.
617
808
  */
618
809
  void fs_pop_file( filestack_t* fs )
@@ -632,13 +823,31 @@ void fs_pop_file( filestack_t* fs )
632
823
 
633
824
 
634
825
  /**
635
- * Get char from (through) Filestack (i.e. top file).
636
- *
826
+ * Get char from (through) Filestack (i.e. top file). Stop at EOF, and
827
+ * don't pop the file in order to allow putback to the file.
828
+ *
637
829
  * @param fs Filestack.
638
- *
830
+ *
639
831
  * @return Char or EOF.
640
832
  */
641
833
  int fs_get( filestack_t* fs )
834
+ {
835
+ if ( fs->file != NULL )
836
+ return sf_get( fs_topfile(fs) );
837
+ else
838
+ return EOF;
839
+ }
840
+
841
+
842
+ /**
843
+ * Get char from (through) Filestack (i.e. top file). If EOF is
844
+ * encountered, continue with lower file if possible.
845
+ *
846
+ * @param fs Filestack.
847
+ *
848
+ * @return Char or EOF.
849
+ */
850
+ int fs_get_one( filestack_t* fs )
642
851
  {
643
852
  int ret;
644
853
 
@@ -666,10 +875,10 @@ int fs_get( filestack_t* fs )
666
875
 
667
876
  /**
668
877
  * Put back char.
669
- *
878
+ *
670
879
  * @param fs Filestack.
671
880
  * @param c Char.
672
- *
881
+ *
673
882
  * @return TRUE.
674
883
  */
675
884
  gboolean fs_put( filestack_t* fs, char c )
@@ -682,11 +891,11 @@ gboolean fs_put( filestack_t* fs, char c )
682
891
  /**
683
892
  * Get n chars from Filestack. If ret is non-null, use that for
684
893
  * storage.
685
- *
894
+ *
686
895
  * @param fs Filestack.
687
896
  * @param n Number of chars to get.
688
897
  * @param ret Storage for data.
689
- *
898
+ *
690
899
  * @return Return data.
691
900
  */
692
901
  GString* fs_get_n( filestack_t* fs, int n, GString* ret )
@@ -732,11 +941,11 @@ GString* fs_get_n( filestack_t* fs, int n, GString* ret )
732
941
  /**
733
942
  * Put chars in str back to Filestack. If n is 0, then use strlen to
734
943
  * get the count.
735
- *
944
+ *
736
945
  * @param fs Filestack.
737
946
  * @param str Content to add.
738
947
  * @param n Number of chars from content.
739
- *
948
+ *
740
949
  * @return True if success.
741
950
  */
742
951
  gboolean fs_put_n( filestack_t* fs, gchar* str, int n )
@@ -757,9 +966,9 @@ gboolean fs_put_n( filestack_t* fs, gchar* str, int n )
757
966
 
758
967
  /**
759
968
  * Create new Outfile. If filename is NULL, then stream is stdout.
760
- *
969
+ *
761
970
  * @param filename File name (or NULL for stdout).
762
- *
971
+ *
763
972
  * @return Outfile.
764
973
  */
765
974
  outfile_t* outfile_new( gchar* filename )
@@ -768,10 +977,10 @@ outfile_t* outfile_new( gchar* filename )
768
977
 
769
978
  of = g_new0( outfile_t, 1 );
770
979
  of->lineno = 0;
980
+ of->blocked = FALSE;
771
981
 
772
982
  if ( filename )
773
983
  {
774
-
775
984
  /* Disk file output. */
776
985
 
777
986
  of->filename = g_strdup( filename );
@@ -787,15 +996,12 @@ outfile_t* outfile_new( gchar* filename )
787
996
 
788
997
  mucgly_fatal( err_sf, "Can't open \"%s\"", err_sf->filename );
789
998
  }
790
-
791
999
  }
792
1000
  else
793
1001
  {
794
-
795
1002
  /* STDOUT output. */
796
1003
  of->filename = g_strdup( "<STDOUT>" );
797
1004
  of->fh = stdout;
798
-
799
1005
  }
800
1006
 
801
1007
  return of;
@@ -804,7 +1010,7 @@ outfile_t* outfile_new( gchar* filename )
804
1010
 
805
1011
  /**
806
1012
  * Free Outfile and close output stream if non-stdout.
807
- *
1013
+ *
808
1014
  * @param of Outfile.
809
1015
  */
810
1016
  void outfile_rem( outfile_t* of )
@@ -820,9 +1026,9 @@ void outfile_rem( outfile_t* of )
820
1026
  /**
821
1027
  * Create Pstate. Create input file stack and output file
822
1028
  * stack. Initialize parsing state.
823
- *
1029
+ *
824
1030
  * @param outfile Initial input file name.
825
- *
1031
+ *
826
1032
  * @return Pstate.
827
1033
  */
828
1034
  pstate_t* ps_new( gchar* outfile )
@@ -844,17 +1050,19 @@ pstate_t* ps_new( gchar* outfile )
844
1050
  /* Match string buffer. */
845
1051
  ps->match_buf = g_string_sized_new( 0 );
846
1052
 
847
-
848
1053
  ps->output = g_list_prepend( ps->output, outfile_new( outfile ) );
849
1054
  ps->flush = FALSE;
850
1055
 
1056
+ ps->post_push = FALSE;
1057
+ ps->post_pop = FALSE;
1058
+
851
1059
  return ps;
852
1060
  }
853
1061
 
854
1062
 
855
1063
  /**
856
1064
  * Free Pstate. Close all input and output files.
857
- *
1065
+ *
858
1066
  * @param ps Pstate.
859
1067
  */
860
1068
  void ps_rem( pstate_t* ps )
@@ -876,9 +1084,10 @@ void ps_rem( pstate_t* ps )
876
1084
  /**
877
1085
  * Fast check for current input char being first char of any of the
878
1086
  * hooks.
879
- *
1087
+ *
880
1088
  * @param ps Pstate.
881
- *
1089
+ * @param c Char to check.
1090
+ *
882
1091
  * @return TRUE if next char matches 1st char of any hook.
883
1092
  */
884
1093
  gboolean ps_check_hook( pstate_t* ps, int c )
@@ -900,11 +1109,11 @@ gboolean ps_check_hook( pstate_t* ps, int c )
900
1109
  /**
901
1110
  * Check if input has the match string coming next. Erase the matched
902
1111
  * string if erase is true and input is matched.
903
- *
1112
+ *
904
1113
  * @param ps Pstate.
905
1114
  * @param match Match string.
906
1115
  * @param erase Erase matched string (but only if matched).
907
- *
1116
+ *
908
1117
  * @return True if match.
909
1118
  */
910
1119
  gboolean ps_check( pstate_t* ps, gchar* match, gboolean erase )
@@ -920,21 +1129,34 @@ gboolean ps_check( pstate_t* ps, gchar* match, gboolean erase )
920
1129
 
921
1130
  len = strlen( match );
922
1131
  input = fs_get_n( ps->fs, len, ps->check_buf );
923
- ret = !g_strcmp0( input->str, ps->match_buf->str );
924
1132
 
925
- if ( !ret || !erase )
926
- /* Put back checked chars. */
927
- fs_put_n( ps->fs, input->str, input->len );
1133
+ if ( input->len == 0 )
1134
+ {
1135
+ /* We are at end-of-file. Let's give up and pop the file from
1136
+ filestack. */
1137
+ fs_pop_file( ps->fs );
1138
+ return FALSE;
1139
+ }
1140
+ else
1141
+ {
1142
+ /* At least partial match string was received. */
928
1143
 
929
- return ret;
1144
+ ret = !g_strcmp0( input->str, ps->match_buf->str );
1145
+
1146
+ if ( !ret || !erase )
1147
+ /* Put back checked chars. */
1148
+ fs_put_n( ps->fs, input->str, input->len );
1149
+
1150
+ return ret;
1151
+ }
930
1152
  }
931
1153
 
932
1154
 
933
1155
  /**
934
1156
  * Check input for hookesc.
935
- *
1157
+ *
936
1158
  * @param ps Pstate.
937
- *
1159
+ *
938
1160
  * @return TRUE on match.
939
1161
  */
940
1162
  gboolean ps_check_hookesc( pstate_t* ps )
@@ -946,11 +1168,37 @@ gboolean ps_check_hookesc( pstate_t* ps )
946
1168
  }
947
1169
 
948
1170
 
1171
+ /**
1172
+ * Save hookpair on top of stack.
1173
+ *
1174
+ * @param sf Stackfile (as context).
1175
+ * @param pair Hookpair to store.
1176
+ */
1177
+ void ps_push_curhook( stackfile_t* sf, hookpair_t* pair )
1178
+ {
1179
+ sf->curhook = g_list_prepend(
1180
+ sf->curhook,
1181
+ hookpair_dup( pair, g_new0( hookpair_t, 1 ) ) );
1182
+ }
1183
+
1184
+
1185
+ /**
1186
+ * Pop of top of stack hookpair.
1187
+ *
1188
+ * @param sf Stackfile (as context).
1189
+ */
1190
+ void ps_pop_curhook( stackfile_t* sf )
1191
+ {
1192
+ hookpair_del( (hookpair_t*) sf->curhook->data );
1193
+ sf->curhook = g_list_delete_link( sf->curhook, sf->curhook );
1194
+ }
1195
+
1196
+
949
1197
  /**
950
1198
  * Check input for hookbeg.
951
- *
1199
+ *
952
1200
  * @param ps Pstate.
953
- *
1201
+ *
954
1202
  * @return TRUE on match.
955
1203
  */
956
1204
  gboolean ps_check_hookbeg( pstate_t* ps )
@@ -958,42 +1206,99 @@ gboolean ps_check_hookbeg( pstate_t* ps )
958
1206
  if ( !ps_has_file(ps) )
959
1207
  return FALSE;
960
1208
 
961
- return ps_check( ps, ps_topfile(ps)->hookbeg, TRUE );
1209
+ gboolean ret;
1210
+ stackfile_t* sf = ps_topfile(ps);
1211
+
1212
+ if ( sf->multi )
1213
+ {
1214
+ ret = FALSE;
1215
+
1216
+ /* Check all hookbegs for match. */
1217
+ for ( int i = 0; i < sf->multi_cnt; i++ )
1218
+ {
1219
+ ret = ps_check( ps, sf->multi[i].beg, TRUE );
1220
+ if ( ret )
1221
+ {
1222
+ ps_push_curhook( sf, &sf->multi[i] );
1223
+ break;
1224
+ }
1225
+ }
1226
+ }
1227
+ else
1228
+ {
1229
+ ret = ps_check( ps, sf->hook.beg, TRUE );
1230
+
1231
+ if ( ret )
1232
+ ps_push_curhook( sf, &sf->hook );
1233
+ }
1234
+
1235
+ return ret;
962
1236
  }
963
1237
 
964
1238
 
965
1239
  /**
966
1240
  * Check input for hookend.
967
- *
1241
+ *
968
1242
  * @param ps Pstate.
969
- *
1243
+ *
970
1244
  * @return TRUE on match.
971
1245
  */
972
1246
  gboolean ps_check_hookend( pstate_t* ps )
973
1247
  {
974
1248
  if ( !ps_has_file(ps) )
975
1249
  return FALSE;
1250
+ else
1251
+ return ps_check( ps, ps_current_hookend(ps), TRUE );
1252
+ }
976
1253
 
977
- return ps_check( ps, ps_topfile(ps)->hookend, TRUE );
1254
+
1255
+ /**
1256
+ * Return hookbeg string that opened the current macro.
1257
+ *
1258
+ * @param ps PState.
1259
+ *
1260
+ * @return
1261
+ */
1262
+ gchar* ps_current_hookbeg( pstate_t* ps )
1263
+ {
1264
+ hookpair_t* h;
1265
+ h = ps_topfile(ps)->curhook->data;
1266
+ return h->beg;
1267
+ }
1268
+
1269
+
1270
+ /**
1271
+ * Return hookend string that opened the current macro.
1272
+ *
1273
+ * @param ps PState.
1274
+ *
1275
+ * @return
1276
+ */
1277
+ gchar* ps_current_hookend( pstate_t* ps )
1278
+ {
1279
+ hookpair_t* h;
1280
+ h = ps_topfile(ps)->curhook->data;
1281
+ return h->end;
978
1282
  }
979
1283
 
980
1284
 
1285
+
981
1286
  /**
982
1287
  * Get char (through Filestack).
983
- *
1288
+ *
984
1289
  * @param ps Pstate.
985
- *
1290
+ *
986
1291
  * @return Char or EOF.
987
1292
  */
988
1293
  int ps_in( pstate_t* ps )
989
1294
  {
990
- return fs_get( ps->fs );
1295
+ return fs_get_one( ps->fs );
991
1296
  }
992
1297
 
993
1298
 
994
1299
  /**
995
1300
  * Output char to current output stream.
996
- *
1301
+ *
997
1302
  * @param ps Pstate.
998
1303
  * @param c Char.
999
1304
  */
@@ -1001,18 +1306,21 @@ void ps_out( pstate_t* ps, int c )
1001
1306
  {
1002
1307
  outfile_t* of = ps->output->data;
1003
1308
 
1004
- if ( c == '\n' )
1005
- of->lineno++;
1006
-
1007
- fputc( c, of->fh );
1008
- if ( ps->flush )
1009
- fflush( of->fh );
1309
+ if ( of->blocked == FALSE )
1310
+ {
1311
+ if ( c == '\n' )
1312
+ of->lineno++;
1313
+
1314
+ fputc( c, of->fh );
1315
+ if ( ps->flush )
1316
+ fflush( of->fh );
1317
+ }
1010
1318
  }
1011
1319
 
1012
1320
 
1013
1321
  /**
1014
1322
  * Output chars with ps_out.
1015
- *
1323
+ *
1016
1324
  * @param ps Pstate.
1017
1325
  * @param str Output string (or NULL for no output).
1018
1326
  */
@@ -1030,9 +1338,33 @@ void ps_out_str( pstate_t* ps, gchar* str )
1030
1338
  }
1031
1339
 
1032
1340
 
1341
+ /**
1342
+ * Block output stream.
1343
+ *
1344
+ * @param ps Pstate.
1345
+ */
1346
+ void ps_block_output( pstate_t* ps )
1347
+ {
1348
+ outfile_t* of = ps->output->data;
1349
+ of->blocked = TRUE;
1350
+ }
1351
+
1352
+
1353
+ /**
1354
+ * Unblock output stream.
1355
+ *
1356
+ * @param ps Pstate.
1357
+ */
1358
+ void ps_unblock_output( pstate_t* ps )
1359
+ {
1360
+ outfile_t* of = ps->output->data;
1361
+ of->blocked = FALSE;
1362
+ }
1363
+
1364
+
1033
1365
  /**
1034
1366
  * Push new output stream on top of output file stack.
1035
- *
1367
+ *
1036
1368
  * @param ps Pstate.
1037
1369
  * @param filename File name.
1038
1370
  */
@@ -1045,7 +1377,7 @@ void ps_push_file( pstate_t* ps, gchar* filename )
1045
1377
 
1046
1378
  /**
1047
1379
  * Pop file from output file stack. Close the stream.
1048
- *
1380
+ *
1049
1381
  * @param ps Pstate.
1050
1382
  */
1051
1383
  void ps_pop_file( pstate_t* ps )
@@ -1060,9 +1392,9 @@ void ps_pop_file( pstate_t* ps )
1060
1392
 
1061
1393
  /**
1062
1394
  * Return current Stackfile (input stream).
1063
- *
1395
+ *
1064
1396
  * @param ps Pstate.
1065
- *
1397
+ *
1066
1398
  * @return Stackfile (or NULL if none).
1067
1399
  */
1068
1400
  stackfile_t* ps_current_file( pstate_t* ps )
@@ -1076,9 +1408,10 @@ stackfile_t* ps_current_file( pstate_t* ps )
1076
1408
  }
1077
1409
 
1078
1410
 
1411
+
1079
1412
  /**
1080
1413
  * Initialize macro content collection.
1081
- *
1414
+ *
1082
1415
  * @param ps Pstate.
1083
1416
  */
1084
1417
  void ps_start_collect( pstate_t* ps )
@@ -1089,7 +1422,7 @@ void ps_start_collect( pstate_t* ps )
1089
1422
 
1090
1423
  /**
1091
1424
  * Add content to macro.
1092
- *
1425
+ *
1093
1426
  * @param ps Pstate.
1094
1427
  * @param c Char to add.
1095
1428
  */
@@ -1099,16 +1432,42 @@ void ps_collect( pstate_t* ps, int c )
1099
1432
  }
1100
1433
 
1101
1434
 
1435
+ /**
1436
+ * Enter first level macro and setup state accordingly.
1437
+ *
1438
+ * @param ps Pstate.
1439
+ */
1440
+ void ps_enter_macro( pstate_t* ps )
1441
+ {
1442
+ /* New macro starting. */
1443
+ ps->in_macro++;
1444
+
1445
+ /* Store macro start info. */
1446
+ sf_mark_macro( ps_topfile( ps ) );
1447
+
1448
+ ps_start_collect( ps );
1449
+ }
1450
+
1451
+
1102
1452
  /**
1103
1453
  * Get current macro content.
1104
- *
1454
+ *
1105
1455
  * @param ps Pstate.
1106
- *
1456
+ *
1107
1457
  * @return Macro content.
1108
1458
  */
1109
1459
  char* ps_get_macro( pstate_t* ps )
1110
1460
  {
1111
- return ps->macro_buf->str;
1461
+ if ( ps->macro_buf->str[0] == '+' )
1462
+ {
1463
+ /* Eat the next char after macro. */
1464
+ ps_topfile(ps)->eat_tail = TRUE;
1465
+ return &ps->macro_buf->str[1];
1466
+ }
1467
+ else
1468
+ {
1469
+ return ps->macro_buf->str;
1470
+ }
1112
1471
  }
1113
1472
 
1114
1473
 
@@ -1116,12 +1475,12 @@ char* ps_get_macro( pstate_t* ps )
1116
1475
  * Evaluate str as Ruby code and convert result to string. Conversion
1117
1476
  * is performed if execution was successful and conversion was
1118
1477
  * requested.
1119
- *
1120
- * @param ps Pstate.
1121
- * @param str Ruby code string.
1478
+ *
1479
+ * @param ps Pstate.
1480
+ * @param str Ruby code string.
1122
1481
  * @param to_str Result convert enable.
1123
- * @param ctxt Context (name) for execution.
1124
- *
1482
+ * @param ctxt Context (name) for execution.
1483
+ *
1125
1484
  * @return Ruby code execution as string (or NULL).
1126
1485
  */
1127
1486
  gchar* ps_eval_ruby_str( pstate_t* ps, gchar* str, gboolean to_str, char* ctxt )
@@ -1168,8 +1527,9 @@ gchar* ps_eval_ruby_str( pstate_t* ps, gchar* str, gboolean to_str, char* ctxt )
1168
1527
 
1169
1528
 
1170
1529
  /**
1171
- * Load file Ruby intepreter.
1172
- *
1530
+ * Load file with Ruby intepreter.
1531
+ *
1532
+ * @param ps Pstate.
1173
1533
  * @param filename Source file.
1174
1534
  */
1175
1535
  void ps_load_ruby_file( pstate_t* ps, gchar* filename )
@@ -1185,9 +1545,9 @@ void ps_load_ruby_file( pstate_t* ps, gchar* filename )
1185
1545
 
1186
1546
  /**
1187
1547
  * Execute Mucgly command/macro.
1188
- *
1548
+ *
1189
1549
  * @param ps Pstate.
1190
- *
1550
+ *
1191
1551
  * @return TRUE if input processing should be aborted.
1192
1552
  */
1193
1553
  gboolean ps_eval_cmd( pstate_t* ps )
@@ -1239,11 +1599,20 @@ gboolean ps_eval_cmd( pstate_t* ps )
1239
1599
  }
1240
1600
 
1241
1601
  else if ( ( len = len_str_cmp( ":include", cmd ) ) )
1242
- fs_push_file( ps->fs, &cmd[ len+1 ] );
1602
+ {
1603
+ fs_push_file_delayed( ps->fs, &cmd[ len+1 ] );
1604
+ ps->post_push = TRUE;
1605
+ }
1243
1606
 
1244
1607
  else if ( ( len = len_str_cmp( ":source", cmd ) ) )
1245
1608
  ps_load_ruby_file( ps, &cmd[ len+1 ] );
1246
1609
 
1610
+ else if ( ( len = len_str_cmp( ":block", cmd ) ) )
1611
+ ps_block_output( ps );
1612
+
1613
+ else if ( ( len = len_str_cmp( ":unblock", cmd ) ) )
1614
+ ps_unblock_output( ps );
1615
+
1247
1616
  else if ( ( len = len_str_cmp( ":comment", cmd ) ) )
1248
1617
  {
1249
1618
  /* Do nothing. */
@@ -1276,12 +1645,12 @@ gboolean ps_eval_cmd( pstate_t* ps )
1276
1645
  {
1277
1646
  /* Postpone evaluation (to next round). */
1278
1647
 
1279
- ps_out_str( ps, ps_topfile(ps)->hookbeg );
1648
+ ps_out_str( ps, ps_current_hookbeg( ps ) );
1280
1649
 
1281
1650
  /* Remove one # sign. */
1282
1651
  ps_out_str( ps, &cmd[1] );
1283
1652
 
1284
- ps_out_str( ps, ps_topfile(ps)->hookend );
1653
+ ps_out_str( ps, ps_current_hookend( ps ) );
1285
1654
  }
1286
1655
 
1287
1656
  else
@@ -1294,51 +1663,90 @@ gboolean ps_eval_cmd( pstate_t* ps )
1294
1663
  }
1295
1664
 
1296
1665
 
1297
- /** Convenience macro for hookend. */
1298
- #define HOOK_END_SEQ { \
1299
- ps->in_macro--; \
1300
- if ( ps->in_macro ) \
1301
- { \
1302
- /* Multilevel macro decrement. */ \
1303
- /* Output the consumed hookend. */ \
1304
- ps_out_str( ps, ps_topfile(ps)->hookend ); \
1305
- } \
1306
- else \
1307
- { \
1308
- /* Back to base level from macro, eval the macro. */ \
1309
- gboolean do_break; \
1310
- do_break = ps_eval_cmd( ps ); \
1311
- sf_unmark_macro( ps_topfile( ps ) ); \
1312
- if ( do_break ) \
1313
- break; \
1314
- } \
1315
- }
1316
-
1317
-
1318
- /** Convenience macro for non-hook processing. */
1319
- #define NON_HOOK_SEQ { \
1320
- /* Non-hook input. */ \
1321
- if ( ps->in_macro ) \
1322
- { \
1323
- if ( c == EOF ) \
1324
- mucgly_error( ps_current_file(ps), "Got EOF within macro!" ); \
1325
- /* Add content to macro. */ \
1326
- ps_collect( ps, c ); \
1327
- } \
1328
- else \
1329
- { \
1330
- if ( c == EOF ) \
1331
- break; \
1332
- else \
1333
- /* Output to current output file. */ \
1334
- ps_out( ps, c ); \
1335
- } \
1336
- }
1666
+ /**
1667
+ * Processing routine for hookend phase.
1668
+ *
1669
+ * @param ps Pstate.
1670
+ * @param do_break True if current processing phase is over.
1671
+ */
1672
+ void ps_process_hook_end_seq( pstate_t* ps, gboolean* do_break )
1673
+ {
1674
+ ps->in_macro--;
1675
+
1676
+ if ( ps->in_macro < 0 )
1677
+ {
1678
+ mucgly_fatal( ps_topfile(ps), "Internal error in macro status..." );
1679
+ }
1680
+ else if ( ps->in_macro )
1681
+ {
1682
+ /* Multilevel macro decrement. */
1683
+ /* Output the consumed hookend. */
1684
+ ps_out_str( ps, ps_current_hookend( ps ) );
1685
+ ps_pop_curhook( ps_topfile(ps) );
1686
+ *do_break = FALSE;
1687
+ }
1688
+ else
1689
+ {
1690
+ /* Back to base level from macro, eval the macro. */
1691
+ *do_break = ps_eval_cmd( ps );
1692
+ sf_unmark_macro( ps_topfile( ps ) );
1693
+ ps_pop_curhook( ps_topfile(ps) );
1694
+
1695
+ if ( ps->post_push == TRUE )
1696
+ {
1697
+ ps->post_push = FALSE;
1698
+ ps->fs->file = ps->fs->file->prev;
1699
+ }
1700
+
1701
+ if ( ps->post_pop )
1702
+ {
1703
+ ps->post_pop = FALSE;
1704
+ fs_pop_file( ps->fs );
1705
+ }
1706
+ }
1707
+ }
1708
+
1709
+
1710
+ /**
1711
+ * Processing routine for non-hook chars.
1712
+ *
1713
+ * @param ps Pstate.
1714
+ * @param c Current char input.
1715
+ * @param do_break True if should break from current phase.
1716
+ */
1717
+ void ps_process_non_hook_seq( pstate_t* ps, int c, gboolean* do_break )
1718
+ {
1719
+ /* Non-hook input. */
1720
+ if ( ps->in_macro )
1721
+ {
1722
+ *do_break = FALSE;
1723
+
1724
+ if ( c == EOF )
1725
+ mucgly_error( ps_current_file(ps), "Got EOF within macro!" );
1726
+
1727
+ /* Add content to macro. */
1728
+ ps_collect( ps, c );
1729
+ }
1730
+ else
1731
+ {
1732
+ if ( c == EOF )
1733
+ {
1734
+ *do_break = TRUE;
1735
+ }
1736
+ else
1737
+ {
1738
+ *do_break = FALSE;
1739
+
1740
+ /* Output to current output file. */
1741
+ ps_out( ps, c );
1742
+ }
1743
+ }
1744
+ }
1337
1745
 
1338
1746
 
1339
1747
  /**
1340
1748
  * Process input file (or stack of files).
1341
- *
1749
+ *
1342
1750
  * @param ps Pstate.
1343
1751
  * @param infile Input file name.
1344
1752
  * @param outfile Output file name (or NULL). Outfile string is freed.
@@ -1346,6 +1754,7 @@ gboolean ps_eval_cmd( pstate_t* ps )
1346
1754
  void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
1347
1755
  {
1348
1756
  int c;
1757
+ gboolean do_break = FALSE;
1349
1758
 
1350
1759
  fs_push_file( ps->fs, infile );
1351
1760
 
@@ -1399,24 +1808,10 @@ void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
1399
1808
  if ( ( c == ' ' || c == '\n' ) &&
1400
1809
  ps_topfile(ps)->hook_esc_eq_end )
1401
1810
  {
1402
-
1403
1811
  /* Space/newline and hookesc is same as hookbeg. */
1404
-
1405
- ps->in_macro--;
1406
-
1407
- if ( ps->in_macro )
1408
- {
1409
- /* Multilevel macro decrement. */
1410
-
1411
- /* Output the consumed hookend. */
1412
- ps_out_str( ps, ps_topfile(ps)->hookend );
1413
- }
1414
- else
1415
- {
1416
- /* Back to base level from macro, eval the macro. */
1417
- if ( ps_eval_cmd( ps ) )
1418
- break;
1419
- }
1812
+ ps_process_hook_end_seq( ps, &do_break );
1813
+ if ( do_break )
1814
+ break;
1420
1815
  }
1421
1816
  else
1422
1817
  {
@@ -1467,13 +1862,17 @@ void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
1467
1862
  {
1468
1863
  /* Escape is same as hookbeg and escape is
1469
1864
  not used to eat out spaces. */
1470
- ps->in_macro++;
1471
1865
 
1472
1866
  /* Put back the extra char (for safety/clarity). */
1473
1867
  fs_put( ps->fs, c );
1474
1868
 
1869
+ /* Push hook here. For non-esc hooks
1870
+ this is done while matching for
1871
+ hook. */
1872
+ ps_push_curhook( ps_topfile(ps), &ps_topfile(ps)->hook );
1873
+
1475
1874
  /* Start collect the macro content. */
1476
- ps_start_collect( ps );
1875
+ ps_enter_macro( ps );
1477
1876
  }
1478
1877
  }
1479
1878
  else
@@ -1488,8 +1887,11 @@ void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
1488
1887
  }
1489
1888
  else if ( ps->in_macro && ps_check_hookend( ps ) )
1490
1889
  {
1491
- /* Hookend has priority over hookbeg if we are in macro. */
1492
- HOOK_END_SEQ;
1890
+ /* Hookend has priority over hookbeg if we are in
1891
+ macro. Also hookend is ignored when outside macro. */
1892
+ ps_process_hook_end_seq( ps, &do_break );
1893
+ if ( do_break )
1894
+ break;
1493
1895
  }
1494
1896
  else if ( ps_check_hookbeg( ps ) )
1495
1897
  {
@@ -1502,33 +1904,26 @@ void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
1502
1904
  ps->in_macro++;
1503
1905
 
1504
1906
  /* Output the consumed hookbeg. */
1505
- ps_out_str( ps, ps_topfile(ps)->hookbeg );
1907
+ ps_out_str( ps, ps_current_hookbeg( ps ) );
1506
1908
  }
1507
1909
  else
1508
1910
  {
1509
- /* New macro starting. */
1510
- ps->in_macro++;
1511
-
1512
- /* Store macro start info. */
1513
- sf_mark_macro( ps_topfile( ps ) );
1514
-
1515
- ps_start_collect( ps );
1911
+ ps_enter_macro( ps );
1516
1912
  }
1517
-
1518
- }
1519
- else if ( ps_check_hookend( ps ) )
1520
- {
1521
- HOOK_END_SEQ;
1522
1913
  }
1523
1914
  else
1524
1915
  {
1525
1916
  c = ps_in( ps );
1526
- NON_HOOK_SEQ;
1917
+ ps_process_non_hook_seq( ps, c, &do_break );
1918
+ if ( do_break )
1919
+ break;
1527
1920
  }
1528
1921
  }
1529
1922
  else
1530
1923
  {
1531
- NON_HOOK_SEQ;
1924
+ ps_process_non_hook_seq( ps, c, &do_break );
1925
+ if ( do_break )
1926
+ break;
1532
1927
  }
1533
1928
  }
1534
1929
 
@@ -1547,10 +1942,10 @@ void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
1547
1942
 
1548
1943
  /**
1549
1944
  * Mucgly.write method. Write output current output without NL.
1550
- *
1945
+ *
1551
1946
  * @param obj Not used.
1552
1947
  * @param rstr Written string (Ruby String).
1553
- *
1948
+ *
1554
1949
  * @return Qnil.
1555
1950
  */
1556
1951
  VALUE mucgly_write( VALUE obj, VALUE rstr )
@@ -1572,10 +1967,10 @@ VALUE mucgly_write( VALUE obj, VALUE rstr )
1572
1967
 
1573
1968
  /**
1574
1969
  * Mucgly.puts method. Write output current output with NL.
1575
- *
1970
+ *
1576
1971
  * @param obj Not used.
1577
1972
  * @param rstr Written string (Ruby String).
1578
- *
1973
+ *
1579
1974
  * @return Qnil.
1580
1975
  */
1581
1976
  VALUE mucgly_puts( VALUE obj, VALUE rstr )
@@ -1599,35 +1994,35 @@ VALUE mucgly_puts( VALUE obj, VALUE rstr )
1599
1994
 
1600
1995
  /**
1601
1996
  * Mucgly.hookbeg method. Get hookbeg.
1602
- *
1997
+ *
1603
1998
  * @param obj Not used.
1604
- *
1999
+ *
1605
2000
  * @return Hookbeg as Ruby String.
1606
2001
  */
1607
2002
  VALUE mucgly_hookbeg( VALUE obj )
1608
2003
  {
1609
- return rb_str_new_cstr( ps_current_file( ruby_ps )->hookbeg );
2004
+ return rb_str_new_cstr( ps_current_file( ruby_ps )->hook.beg );
1610
2005
  }
1611
2006
 
1612
2007
 
1613
2008
  /**
1614
2009
  * Mucgly.hookend method. Get hookend.
1615
- *
2010
+ *
1616
2011
  * @param obj Not used.
1617
- *
2012
+ *
1618
2013
  * @return Hookend as Ruby String.
1619
2014
  */
1620
2015
  VALUE mucgly_hookend( VALUE obj )
1621
2016
  {
1622
- return rb_str_new_cstr( ps_current_file( ruby_ps )->hookend );
2017
+ return rb_str_new_cstr( ps_current_file( ruby_ps )->hook.end );
1623
2018
  }
1624
2019
 
1625
2020
 
1626
2021
  /**
1627
2022
  * Mucgly.hookesc method. Get hookesc.
1628
- *
2023
+ *
1629
2024
  * @param obj Not used.
1630
- *
2025
+ *
1631
2026
  * @return Hookesc as Ruby String.
1632
2027
  */
1633
2028
  VALUE mucgly_hookesc( VALUE obj )
@@ -1638,10 +2033,10 @@ VALUE mucgly_hookesc( VALUE obj )
1638
2033
 
1639
2034
  /**
1640
2035
  * Mucgly.sethookbeg method. Set hookbeg.
1641
- *
2036
+ *
1642
2037
  * @param obj Not used.
1643
2038
  * @param rstr Hookbeg string (Ruby String).
1644
- *
2039
+ *
1645
2040
  * @return Qnil.
1646
2041
  */
1647
2042
  VALUE mucgly_sethookbeg( VALUE obj, VALUE rstr )
@@ -1655,10 +2050,10 @@ VALUE mucgly_sethookbeg( VALUE obj, VALUE rstr )
1655
2050
 
1656
2051
  /**
1657
2052
  * Mucgly.sethookend method. Set hookend.
1658
- *
2053
+ *
1659
2054
  * @param obj Not used.
1660
2055
  * @param rstr Hookend string (Ruby String).
1661
- *
2056
+ *
1662
2057
  * @return Qnil.
1663
2058
  */
1664
2059
  VALUE mucgly_sethookend( VALUE obj, VALUE rstr )
@@ -1672,10 +2067,10 @@ VALUE mucgly_sethookend( VALUE obj, VALUE rstr )
1672
2067
 
1673
2068
  /**
1674
2069
  * Mucgly.sethookesc method. Set hookesc.
1675
- *
2070
+ *
1676
2071
  * @param obj Not used.
1677
2072
  * @param rstr Hookesc string (Ruby String).
1678
- *
2073
+ *
1679
2074
  * @return Qnil.
1680
2075
  */
1681
2076
  VALUE mucgly_sethookesc( VALUE obj, VALUE rstr )
@@ -1687,11 +2082,38 @@ VALUE mucgly_sethookesc( VALUE obj, VALUE rstr )
1687
2082
  }
1688
2083
 
1689
2084
 
2085
+ /**
2086
+ * Mucgly.multihook method. Add multihook pairs.
2087
+ *
2088
+ * @param obj Not used.
2089
+ * @param ary Array of hookpairs.
2090
+ *
2091
+ * @return Qnil.
2092
+ */
2093
+ VALUE mucgly_multihook( VALUE obj, VALUE ary )
2094
+ {
2095
+ if ( ( RARRAY_LEN(ary) % 2 ) != 0 )
2096
+ rb_raise( rb_eRuntimeError,
2097
+ "mucgly: Must have even number of multi hook args, had %d!",
2098
+ ((int) RARRAY_LEN(ary)) );
2099
+
2100
+ char* beg, *end;
2101
+ for ( int i = 0; i < RARRAY_LEN(ary); i += 2 )
2102
+ {
2103
+ beg = RSTRING_PTR( RARRAY_PTR(ary)[i] );
2104
+ end = RSTRING_PTR( RARRAY_PTR(ary)[i+1] );
2105
+ sf_multi_hook( ps_topfile( ruby_ps ), beg, end );
2106
+ }
2107
+
2108
+ return Qnil;
2109
+ }
2110
+
2111
+
1690
2112
  /**
1691
2113
  * Mucgly.ifilename method. Gets current input file name.
1692
- *
2114
+ *
1693
2115
  * @param obj Not used.
1694
- *
2116
+ *
1695
2117
  * @return Input file name (Ruby String).
1696
2118
  */
1697
2119
  VALUE mucgly_ifilename( VALUE obj )
@@ -1702,9 +2124,9 @@ VALUE mucgly_ifilename( VALUE obj )
1702
2124
 
1703
2125
  /**
1704
2126
  * Mucgly.ilinenumber method. Gets current input file line.
1705
- *
2127
+ *
1706
2128
  * @param obj Not used.
1707
- *
2129
+ *
1708
2130
  * @return Input file line number (Ruby Fixnum).
1709
2131
  */
1710
2132
  VALUE mucgly_ilinenumber( VALUE obj )
@@ -1715,9 +2137,9 @@ VALUE mucgly_ilinenumber( VALUE obj )
1715
2137
 
1716
2138
  /**
1717
2139
  * Mucgly.ofilename method. Gets current output file name.
1718
- *
2140
+ *
1719
2141
  * @param obj Not used.
1720
- *
2142
+ *
1721
2143
  * @return Input file name (Ruby String).
1722
2144
  */
1723
2145
  VALUE mucgly_ofilename( VALUE obj )
@@ -1730,9 +2152,9 @@ VALUE mucgly_ofilename( VALUE obj )
1730
2152
 
1731
2153
  /**
1732
2154
  * Mucgly.olinenumber method. Gets current output file line.
1733
- *
2155
+ *
1734
2156
  * @param obj Not used.
1735
- *
2157
+ *
1736
2158
  * @return Output file line number (Ruby Fixnum).
1737
2159
  */
1738
2160
  VALUE mucgly_olinenumber( VALUE obj )
@@ -1745,10 +2167,10 @@ VALUE mucgly_olinenumber( VALUE obj )
1745
2167
 
1746
2168
  /**
1747
2169
  * Mucgly.pushinput method. Push new input stream.
1748
- *
2170
+ *
1749
2171
  * @param obj Not used.
1750
2172
  * @param rstr Input file name (Ruby String).
1751
- *
2173
+ *
1752
2174
  * @return Qnil.
1753
2175
  */
1754
2176
  VALUE mucgly_pushinput( VALUE obj, VALUE rstr )
@@ -1756,31 +2178,32 @@ VALUE mucgly_pushinput( VALUE obj, VALUE rstr )
1756
2178
  char* str;
1757
2179
 
1758
2180
  str = RSTRING_PTR( rstr );
1759
- fs_push_file( ruby_ps->fs, str );
2181
+ fs_push_file_delayed( ruby_ps->fs, str );
2182
+ ruby_ps->post_push = TRUE;
1760
2183
  return Qnil;
1761
2184
  }
1762
2185
 
1763
2186
 
1764
2187
  /**
1765
2188
  * Mucgly.closeinput method. Pop input stream and close file stream.
1766
- *
2189
+ *
1767
2190
  * @param obj Not used.
1768
- *
2191
+ *
1769
2192
  * @return Qnil.
1770
2193
  */
1771
2194
  VALUE mucgly_closeinput( VALUE obj )
1772
2195
  {
1773
- fs_pop_file( ruby_ps->fs );
2196
+ ruby_ps->post_pop = TRUE;
1774
2197
  return Qnil;
1775
2198
  }
1776
2199
 
1777
2200
 
1778
2201
  /**
1779
2202
  * Mucgly.pushoutput method. Push new output stream.
1780
- *
2203
+ *
1781
2204
  * @param obj Not used.
1782
2205
  * @param rstr Output file name (Ruby String).
1783
- *
2206
+ *
1784
2207
  * @return Qnil.
1785
2208
  */
1786
2209
  VALUE mucgly_pushoutput( VALUE obj, VALUE rstr )
@@ -1795,9 +2218,9 @@ VALUE mucgly_pushoutput( VALUE obj, VALUE rstr )
1795
2218
 
1796
2219
  /**
1797
2220
  * Mucgly.closeoutput method. Pop output stream and close file stream.
1798
- *
2221
+ *
1799
2222
  * @param obj Not used.
1800
- *
2223
+ *
1801
2224
  * @return Qnil.
1802
2225
  */
1803
2226
  VALUE mucgly_closeoutput( VALUE obj )
@@ -1807,14 +2230,42 @@ VALUE mucgly_closeoutput( VALUE obj )
1807
2230
  }
1808
2231
 
1809
2232
 
2233
+ /**
2234
+ * Mucgly.block method. Block output.
2235
+ *
2236
+ * @param obj Not used.
2237
+ *
2238
+ * @return Qnil.
2239
+ */
2240
+ VALUE mucgly_block( VALUE obj )
2241
+ {
2242
+ ps_block_output( ruby_ps );
2243
+ return Qnil;
2244
+ }
2245
+
2246
+
2247
+ /**
2248
+ * Mucgly.block method. Block output.
2249
+ *
2250
+ * @param obj Not used.
2251
+ *
2252
+ * @return Qnil.
2253
+ */
2254
+ VALUE mucgly_unblock( VALUE obj )
2255
+ {
2256
+ ps_unblock_output( ruby_ps );
2257
+ return Qnil;
2258
+ }
2259
+
2260
+
1810
2261
  #ifdef MUCGLY_LIB
1811
2262
  /**
1812
2263
  * Call mucgly C-side entry point. Convert Ruby ARGV array to C-array
1813
2264
  * for C-side command line arg parsing.
1814
- *
2265
+ *
1815
2266
  * @param obj Not used.
1816
2267
  * @param cli Ruby side ARGV.
1817
- *
2268
+ *
1818
2269
  * @return Qnil.
1819
2270
  */
1820
2271
  VALUE mucgly_run_mucgly( VALUE obj, VALUE cli )
@@ -1839,7 +2290,7 @@ VALUE mucgly_run_mucgly( VALUE obj, VALUE cli )
1839
2290
 
1840
2291
  /**
1841
2292
  * Create Mucgly Ruby module and register all methods to it.
1842
- *
2293
+ *
1843
2294
  */
1844
2295
  void Init_mucgly( void )
1845
2296
  {
@@ -1859,6 +2310,7 @@ void Init_mucgly( void )
1859
2310
  rb_define_module_function( mMucgly, "sethookbeg", mucgly_sethookbeg, 1 );
1860
2311
  rb_define_module_function( mMucgly, "sethookend", mucgly_sethookend, 1 );
1861
2312
  rb_define_module_function( mMucgly, "sethookesc", mucgly_sethookesc, 1 );
2313
+ rb_define_module_function( mMucgly, "multihook", mucgly_multihook, 1 );
1862
2314
  rb_define_module_function( mMucgly, "ifilename", mucgly_ifilename, 0 );
1863
2315
  rb_define_module_function( mMucgly, "ilinenumber", mucgly_ilinenumber, 0 );
1864
2316
  rb_define_module_function( mMucgly, "ofilename", mucgly_ofilename, 0 );
@@ -1867,6 +2319,8 @@ void Init_mucgly( void )
1867
2319
  rb_define_module_function( mMucgly, "closeinput", mucgly_closeinput, 0 );
1868
2320
  rb_define_module_function( mMucgly, "pushoutput", mucgly_pushoutput, 1 );
1869
2321
  rb_define_module_function( mMucgly, "closeoutput", mucgly_closeoutput, 0 );
2322
+ rb_define_module_function( mMucgly, "block", mucgly_block, 0 );
2323
+ rb_define_module_function( mMucgly, "unblock", mucgly_unblock, 0 );
1870
2324
 
1871
2325
  }
1872
2326
 
@@ -1874,10 +2328,10 @@ void Init_mucgly( void )
1874
2328
  /**
1875
2329
  * Generate outfile name from infile by removing the outfile_tag from
1876
2330
  * infile name.
1877
- *
2331
+ *
1878
2332
  * @param infile Input file name.
1879
2333
  * @param outfile_tag Tag to remove from infile to get outfile.
1880
- *
2334
+ *
1881
2335
  * @return Outfile name.
1882
2336
  */
1883
2337
  gchar* infile_to_outfile( gchar* infile, gchar* outfile_tag )
@@ -1907,21 +2361,18 @@ gchar* infile_to_outfile( gchar* infile, gchar* outfile_tag )
1907
2361
  * Main program:
1908
2362
  * ------------------------------------------------------------ */
1909
2363
 
2364
+ /**
2365
+ * Mucgly entrypoint.
2366
+ *
2367
+ * @param argc Arg count.
2368
+ * @param argv Arg values.
2369
+ */
1910
2370
  void mucgly_main( int argc, char** argv )
1911
2371
  {
1912
2372
 
1913
2373
  /* Setup default hooks etc. */
1914
2374
  stack_default = sf_new( NULL, NULL );
1915
2375
 
1916
- /* ------------------------------------------------------------
1917
- * Setup Ruby features:
1918
- * ------------------------------------------------------------ */
1919
-
1920
- #ifndef MUCGLY_LIB
1921
- ruby_init();
1922
- Init_mucgly();
1923
- #endif
1924
-
1925
2376
 
1926
2377
  /* ------------------------------------------------------------
1927
2378
  * Command line parsing:
@@ -1936,6 +2387,7 @@ void mucgly_main( int argc, char** argv )
1936
2387
  gchar* opt_end_sep = NULL;
1937
2388
  gchar* opt_esc_sep = NULL;
1938
2389
  gchar* opt_all_sep = NULL;
2390
+ gchar** opt_multi_sep = NULL;
1939
2391
  gboolean opt_module = FALSE;
1940
2392
  gboolean opt_interact = FALSE;
1941
2393
  gboolean opt_no_init = FALSE;
@@ -1945,28 +2397,43 @@ void mucgly_main( int argc, char** argv )
1945
2397
  {
1946
2398
  { "files", 'f', 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_files,
1947
2399
  "Input file(s) (default: stdin).", NULL },
2400
+
1948
2401
  { "configs", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_configs,
1949
2402
  "Ruby configuration file(s).", NULL },
2403
+
1950
2404
  { "output", 'o', 0, G_OPTION_ARG_FILENAME, &opt_output,
1951
2405
  "Output file(s) (default: stdout).", NULL },
2406
+
1952
2407
  { "genout", 'g', 0, G_OPTION_ARG_STRING, &opt_genout,
1953
2408
  "Generate output file names (remove arg from input filename).", NULL },
2409
+
1954
2410
  { "cli_cmds", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &opt_cli_cmds,
1955
2411
  "Command line configuration(s).", NULL },
2412
+
1956
2413
  { "beg_sep", 'b', 0, G_OPTION_ARG_STRING, &opt_beg_sep,
1957
2414
  "Set hookbeg separator (default: '-<').", NULL },
2415
+
1958
2416
  { "end_sep", 'e', 0, G_OPTION_ARG_STRING, &opt_end_sep,
1959
2417
  "Set hookend separator (default: '>-').", NULL },
2418
+
1960
2419
  { "esc_sep", 's', 0, G_OPTION_ARG_STRING, &opt_esc_sep,
1961
2420
  "Set hookesc separator (default: '\\').", NULL },
2421
+
1962
2422
  { "all_sep", 'a', 0, G_OPTION_ARG_STRING, &opt_all_sep,
1963
2423
  "Set all separators.", NULL },
2424
+
2425
+ { "multi_sep", 'u', 0, G_OPTION_ARG_STRING_ARRAY, &opt_multi_sep,
2426
+ "Set multiple beg/end hook pairs.", NULL },
2427
+
1964
2428
  { "module", 'm', 0, G_OPTION_ARG_NONE, &opt_module,
1965
2429
  "Include Mucgly module at startup.", NULL },
2430
+
1966
2431
  { "interact", 'i', 0, G_OPTION_ARG_NONE, &opt_interact,
1967
2432
  "Flush output for convenient interaction.", NULL },
2433
+
1968
2434
  { "no_init", 'n', 0, G_OPTION_ARG_NONE, &opt_no_init,
1969
2435
  "No init file (skip MUCGLY env var).", NULL },
2436
+
1970
2437
  { NULL }
1971
2438
  };
1972
2439
 
@@ -2049,6 +2516,30 @@ void mucgly_main( int argc, char** argv )
2049
2516
  sf_set_hook( stack_default, hook_esc, opt_all_sep );
2050
2517
  }
2051
2518
 
2519
+ /* Set multi-hooks. */
2520
+ if ( opt_multi_sep )
2521
+ {
2522
+ int cnt = 0;
2523
+
2524
+ /* First check that we have even number of hooks. */
2525
+ while ( opt_multi_sep[ cnt ] ) cnt++;
2526
+
2527
+ if ( ( cnt % 2 ) != 0 )
2528
+ {
2529
+ g_print( "mucgly: Must have even number of multi hook args, had %d!\n", cnt );
2530
+ exit( EXIT_FAILURE );
2531
+ }
2532
+
2533
+ cnt = 0;
2534
+ while ( opt_multi_sep[ cnt ] )
2535
+ {
2536
+ sf_multi_hook( stack_default,
2537
+ opt_multi_sep[ cnt ],
2538
+ opt_multi_sep[ cnt+1 ] );
2539
+ cnt += 2;
2540
+ }
2541
+ }
2542
+
2052
2543
 
2053
2544
  if ( opt_interact )
2054
2545
  /* Flush output immediately in interactive mode. */
@@ -2084,14 +2575,20 @@ void mucgly_main( int argc, char** argv )
2084
2575
  #ifndef MUCGLY_LIB
2085
2576
  /**
2086
2577
  * C-top entry point.
2087
- *
2578
+ *
2088
2579
  * @param argc Cli arg count.
2089
2580
  * @param argv Cli arg values.
2090
- *
2581
+ *
2091
2582
  * @return Not used.
2092
2583
  */
2093
2584
  int main( int argc, char** argv )
2094
2585
  {
2586
+
2587
+ /* Setup Ruby and mucgly module. */
2588
+ ruby_init();
2589
+ Init_mucgly();
2590
+
2591
+ /* Enter mucgly. */
2095
2592
  mucgly_main( argc, argv );
2096
2593
  }
2097
2594
  #endif