mucgly 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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