mucgly 0.0.2 → 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +8 -0
- data/LICENSE +1 -1
- data/README.rdoc +160 -130
- data/bin/mucgly +4 -217
- data/doc/Mucgly.html +24 -82
- data/doc/_index.html +5 -153
- data/doc/class_list.html +7 -2
- data/doc/css/style.css +2 -1
- data/doc/file.CHANGELOG.html +16 -8
- data/doc/file.README.html +210 -187
- data/doc/file_list.html +6 -1
- data/doc/frames.html +5 -7
- data/doc/index.html +210 -187
- data/doc/js/app.js +7 -2
- data/doc/js/full_list.js +9 -6
- data/doc/method_list.html +7 -686
- data/doc/top-level-namespace.html +5 -5
- data/ext/mucgly/extconf.rb +11 -0
- data/ext/mucgly/mucgly.c +2095 -0
- data/lib/version.rb +6 -0
- data/test/golden/test_basic.txt +18 -0
- data/test/golden/test_specials_cli.txt +11 -0
- data/test/golden/test_specials_cmd.txt +36 -0
- data/test/result/test_basic.txt +18 -0
- data/test/result/test_specials_cli.txt +11 -0
- data/test/result/test_specials_cmd.txt +36 -0
- data/test/result/test_specials_cmd2.txt +1 -0
- data/test/test_basic.rx.txt +10 -6
- data/test/test_mucgly.rb +4 -6
- data/test/test_specials_cli.rx.txt +4 -4
- data/test/test_specials_cmd.rx.txt +5 -5
- metadata +45 -63
- data/Rakefile +0 -29
- data/doc/EasyFile/InOut.html +0 -2097
- data/doc/EasyFile/Read.html +0 -1334
- data/doc/EasyFile/ReadStack.html +0 -461
- data/doc/EasyFile/Stacked.html +0 -411
- data/doc/EasyFile/String.html +0 -570
- data/doc/EasyFile/Write.html +0 -1084
- data/doc/EasyFile/WriteStack.html +0 -305
- data/doc/EasyFile.html +0 -155
- data/doc/Mucgly/Env.html +0 -1675
- data/doc/Mucgly/MucglyFile/ParseState.html +0 -1662
- data/doc/Mucgly/MucglyFile/Token.html +0 -529
- data/doc/Mucgly/MucglyFile.html +0 -545
- data/doc/Mucgly/Separators.html +0 -521
- data/lib/easyfile.rb +0 -720
- data/lib/mucgly.rb +0 -627
- data/test/test_multi.rx.txt +0 -4
data/ext/mucgly/mucgly.c
ADDED
@@ -0,0 +1,2095 @@
|
|
1
|
+
#include <glib.h>
|
2
|
+
#include <glib/gstdio.h>
|
3
|
+
#include <ruby.h>
|
4
|
+
|
5
|
+
|
6
|
+
/*
|
7
|
+
* If MUCGLY_LIB is defined, mucgly is compiled in Ruby-top (e.g. for
|
8
|
+
* gems) mode. Otherwise mucgly is in C-top (main) mode,
|
9
|
+
* i.e. standalone binary.
|
10
|
+
*/
|
11
|
+
#ifdef MUCGLY_LIB
|
12
|
+
void mucgly_main( int argc, char** argv );
|
13
|
+
#endif
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
/* ------------------------------------------------------------
|
18
|
+
* Debugging:
|
19
|
+
* ------------------------------------------------------------ */
|
20
|
+
|
21
|
+
/* Enable the define is input char based debugging is required. Set
|
22
|
+
GDB breakpoint at mucgly_debug. */
|
23
|
+
void mucgly_debug( void ) {}
|
24
|
+
#if 1
|
25
|
+
# define DEBUG_CHAR '~'
|
26
|
+
#endif
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
/* ------------------------------------------------------------
|
31
|
+
* Data models:
|
32
|
+
* ------------------------------------------------------------ */
|
33
|
+
|
34
|
+
/*
|
35
|
+
* Mucgly uses Pstate to store the parsing state of the input
|
36
|
+
* files. The parser has to know whether we are in or out of macro
|
37
|
+
* definition. The "hooks" in the input stream change the state within
|
38
|
+
* parsing.
|
39
|
+
*
|
40
|
+
* Pstate contains Filestack. Filestack is a stack of input files. The
|
41
|
+
* input file stack grows when the "lower" level files performs
|
42
|
+
* include command.
|
43
|
+
*
|
44
|
+
* Filestack contains Stackfiles. Stackfile presents the read status
|
45
|
+
* of an input file. It contains character position information for
|
46
|
+
* error reporting.
|
47
|
+
*
|
48
|
+
* Outfile is part of the Pstate output file stack. Each Outfile
|
49
|
+
* represents the output file state. Outfile stack is needed to
|
50
|
+
* implement redirection of output stream to different files. Output
|
51
|
+
* files has to be controlled explicitly from Mucgly files.
|
52
|
+
*/
|
53
|
+
|
54
|
+
|
55
|
+
#define HOOKBEG_DEFAULT "-<"
|
56
|
+
#define HOOKEND_DEFAULT ">-"
|
57
|
+
#define HOOKESC_DEFAULT "\\"
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Stackfile is an entry in the Filestack. Stackfile is the input file
|
63
|
+
* for Mucgly.
|
64
|
+
*/
|
65
|
+
struct stackfile_s {
|
66
|
+
|
67
|
+
gchar* filename; /**< Filename. */
|
68
|
+
FILE* fh; /**< Stream handle. */
|
69
|
+
GString* buf; /**< Put-back buffer (oldest-is-last). */
|
70
|
+
|
71
|
+
int lineno; /**< Line number (0->). */
|
72
|
+
int column; /**< Line column (0->). */
|
73
|
+
int old_column; /**< Prev column (used with putback). */
|
74
|
+
|
75
|
+
gboolean macro; /**< Macro active. */
|
76
|
+
int macro_line; /**< Macro start line. */
|
77
|
+
int macro_col; /**< Macro start column. */
|
78
|
+
|
79
|
+
gchar* hookbeg; /**< Hookbeg for input file. */
|
80
|
+
gchar* hookend; /**< Hookend for input file. */
|
81
|
+
gchar* hookesc; /**< Hookesc for input file. */
|
82
|
+
|
83
|
+
/** Hookesc is same as hookbeg. Speeds up input processing. */
|
84
|
+
gboolean hook_esc_eq_beg;
|
85
|
+
|
86
|
+
/** Hookesc is same as hookend. Speeds up input processing. */
|
87
|
+
gboolean hook_esc_eq_end;
|
88
|
+
|
89
|
+
/** Hookesc is same as hookend. Speeds up input processing. */
|
90
|
+
guchar hook_1st_chars[ 256 ];
|
91
|
+
|
92
|
+
};
|
93
|
+
typedef struct stackfile_s stackfile_t;
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Filestack is a stack of files (stackfile_t). Macro processing
|
99
|
+
* starts at base file. The base file is allowed to include other
|
100
|
+
* files (as input with ":include" command). The included file is
|
101
|
+
* added to the top of stack as current file at inclusion. When file
|
102
|
+
* EOF is encountered, it is popped of the stack. This makes the
|
103
|
+
* character flow continuous from the parser point of view.
|
104
|
+
*/
|
105
|
+
struct filestack_s {
|
106
|
+
GList* file; /**< Stack of files. */
|
107
|
+
};
|
108
|
+
typedef struct filestack_s filestack_t;
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Outfile is output file stream descriptor. Output can be diverted
|
114
|
+
* from the default output to another output file temporarely. Note
|
115
|
+
* that the diverted output stream has to be closed as well.
|
116
|
+
*/
|
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;
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Parser state for Mucgly.
|
128
|
+
*/
|
129
|
+
struct pstate_s {
|
130
|
+
filestack_t* fs; /**< Stack of input streams. */
|
131
|
+
|
132
|
+
GString* check_buf; /**< Preview buffer. */
|
133
|
+
GString* macro_buf; /**< Macro content buffer. */
|
134
|
+
GString* match_buf; /**< Match str buffer. */
|
135
|
+
|
136
|
+
int in_macro; /**< Processing within macro. */
|
137
|
+
|
138
|
+
GList* output; /**< Stack of output streams. */
|
139
|
+
|
140
|
+
gboolean flush;
|
141
|
+
};
|
142
|
+
typedef struct pstate_s pstate_t;
|
143
|
+
|
144
|
+
|
145
|
+
/** Hook type enum. */
|
146
|
+
typedef enum hook_e { hook_none, hook_end, hook_beg, hook_esc } hook_t;
|
147
|
+
|
148
|
+
|
149
|
+
/** Current filestack from pstate_t (for convenience). */
|
150
|
+
#define ps_has_file(ps) ((stackfile_t*)(ps)->fs->file)
|
151
|
+
|
152
|
+
/** Current stackfile from pstate_t (for convenience). */
|
153
|
+
#define ps_topfile(ps) ((stackfile_t*)(ps)->fs->file->data)
|
154
|
+
|
155
|
+
/** Current stackfile from filestack_t (for convenience). */
|
156
|
+
#define fs_topfile(fs) ((stackfile_t*)(fs)->file->data)
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
/* ------------------------------------------------------------
|
161
|
+
* Global variables:
|
162
|
+
* ------------------------------------------------------------ */
|
163
|
+
|
164
|
+
/** Default hook settings. */
|
165
|
+
stackfile_t* stack_default = NULL;
|
166
|
+
|
167
|
+
/** Pointer to current file. Usefull for debugging. */
|
168
|
+
stackfile_t* csf;
|
169
|
+
|
170
|
+
/** Ruby side reference to parser. */
|
171
|
+
pstate_t* ruby_ps;
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
/* ------------------------------------------------------------
|
176
|
+
* Function declarations:
|
177
|
+
* ------------------------------------------------------------ */
|
178
|
+
|
179
|
+
void sf_set_hook( stackfile_t* sf, hook_t hook, char* value );
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
/* ------------------------------------------------------------
|
184
|
+
* Mucgly C-functions:
|
185
|
+
* ------------------------------------------------------------ */
|
186
|
+
|
187
|
+
|
188
|
+
/**
|
189
|
+
* Compare two strings upto length of str1.
|
190
|
+
*
|
191
|
+
* @param str1 String 1.
|
192
|
+
* @param str2 String 2.
|
193
|
+
*
|
194
|
+
* @return Match length (0 for no match).
|
195
|
+
*/
|
196
|
+
int len_str_cmp( char* str1, char* str2 )
|
197
|
+
{
|
198
|
+
int len = strlen( str1 );
|
199
|
+
|
200
|
+
if ( !strncmp( str1, str2, len ) )
|
201
|
+
return len;
|
202
|
+
else
|
203
|
+
return 0;
|
204
|
+
}
|
205
|
+
|
206
|
+
|
207
|
+
/**
|
208
|
+
* Common utility function to output user info messages.
|
209
|
+
*
|
210
|
+
* @param sf Currently processed file (or NULL).
|
211
|
+
* @param infotype Severity type.
|
212
|
+
* @param format Message (printf) formatter.
|
213
|
+
* @param ap Message args.
|
214
|
+
*/
|
215
|
+
void mucgly_user_info( stackfile_t* sf, char* infotype, char* format, va_list ap )
|
216
|
+
{
|
217
|
+
GString* out = g_string_sized_new( 0 );
|
218
|
+
|
219
|
+
if ( sf )
|
220
|
+
{
|
221
|
+
int lineno, column;
|
222
|
+
|
223
|
+
if ( sf->macro )
|
224
|
+
{
|
225
|
+
lineno = sf->macro_line;
|
226
|
+
column = sf->macro_col;
|
227
|
+
}
|
228
|
+
else
|
229
|
+
{
|
230
|
+
lineno = sf->lineno;
|
231
|
+
column = sf->column;
|
232
|
+
}
|
233
|
+
|
234
|
+
g_string_append_printf( out, "mucgly %s in \"%s:%d:%d\": ",
|
235
|
+
infotype,
|
236
|
+
sf->filename,
|
237
|
+
lineno+1,
|
238
|
+
column+1
|
239
|
+
);
|
240
|
+
}
|
241
|
+
else
|
242
|
+
{
|
243
|
+
g_string_append_printf( out, "mucgly %s: ", infotype );
|
244
|
+
}
|
245
|
+
|
246
|
+
g_string_append_vprintf( out, format, ap );
|
247
|
+
fputs( out->str, stderr );
|
248
|
+
fputc( '\n', stderr );
|
249
|
+
fflush( stderr );
|
250
|
+
g_string_free( out, TRUE );
|
251
|
+
}
|
252
|
+
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Report warning to user (no exit).
|
256
|
+
*
|
257
|
+
* @param sf Current input file.
|
258
|
+
* @param format Message formatter.
|
259
|
+
*/
|
260
|
+
void mucgly_warn( stackfile_t* sf, char* format, ... )
|
261
|
+
{
|
262
|
+
va_list ap;
|
263
|
+
va_start( ap, format );
|
264
|
+
mucgly_user_info( sf, "warning", format, ap );
|
265
|
+
va_end( ap );
|
266
|
+
}
|
267
|
+
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Report error to user (and exit).
|
271
|
+
*
|
272
|
+
* @param sf Current input file.
|
273
|
+
* @param format Message formatter.
|
274
|
+
*/
|
275
|
+
void mucgly_error( stackfile_t* sf, char* format, ... )
|
276
|
+
{
|
277
|
+
va_list ap;
|
278
|
+
va_start( ap, format );
|
279
|
+
mucgly_user_info( sf, "error", format, ap );
|
280
|
+
va_end( ap );
|
281
|
+
exit( EXIT_FAILURE );
|
282
|
+
}
|
283
|
+
|
284
|
+
|
285
|
+
/**
|
286
|
+
* Report fatal error to user (and exit).
|
287
|
+
*
|
288
|
+
* @param sf Current input file.
|
289
|
+
* @param format Message formatter.
|
290
|
+
*/
|
291
|
+
void mucgly_fatal( stackfile_t* sf, char* format, ... )
|
292
|
+
{
|
293
|
+
va_list ap;
|
294
|
+
va_start( ap, format );
|
295
|
+
mucgly_user_info( sf, "fatal error", format, ap );
|
296
|
+
va_end( ap );
|
297
|
+
exit( EXIT_FAILURE );
|
298
|
+
}
|
299
|
+
|
300
|
+
|
301
|
+
/**
|
302
|
+
* Create new Stackfile.
|
303
|
+
*
|
304
|
+
* @param filename Filename (NULL for stdin).
|
305
|
+
* @param inherit Inherit hooks source (NULL for defaults).
|
306
|
+
*
|
307
|
+
* @return Stackfile.
|
308
|
+
*/
|
309
|
+
stackfile_t* sf_new( gchar* filename, stackfile_t* inherit )
|
310
|
+
{
|
311
|
+
stackfile_t* sf;
|
312
|
+
|
313
|
+
sf = g_new0( stackfile_t, 1 );
|
314
|
+
|
315
|
+
if ( filename )
|
316
|
+
{
|
317
|
+
/* Real file. */
|
318
|
+
|
319
|
+
sf->fh = g_fopen( filename, (gchar*) "r" );
|
320
|
+
sf->filename = g_strdup( filename );
|
321
|
+
|
322
|
+
if ( sf->fh == NULL )
|
323
|
+
{
|
324
|
+
stackfile_t* err_sf;
|
325
|
+
|
326
|
+
/* Get the includer. */
|
327
|
+
err_sf = ruby_ps->fs->file->data;
|
328
|
+
|
329
|
+
mucgly_fatal( err_sf, "Can't open \"%s\"", filename );
|
330
|
+
}
|
331
|
+
|
332
|
+
}
|
333
|
+
else
|
334
|
+
{
|
335
|
+
/* Use stdin. */
|
336
|
+
sf->fh = stdin;
|
337
|
+
sf->filename = g_strdup( "<STDIN>" );
|
338
|
+
}
|
339
|
+
|
340
|
+
sf->buf = g_string_sized_new( 0 );
|
341
|
+
sf->lineno = 0;
|
342
|
+
sf->column = 0;
|
343
|
+
sf->old_column = 0;
|
344
|
+
|
345
|
+
sf->macro = FALSE;
|
346
|
+
sf->macro_line = 0;
|
347
|
+
sf->macro_col = 0;
|
348
|
+
|
349
|
+
|
350
|
+
if ( inherit )
|
351
|
+
{
|
352
|
+
/* Inherited hook values. */
|
353
|
+
sf->hookbeg = g_strdup( inherit->hookbeg );
|
354
|
+
sf->hookend = g_strdup( inherit->hookend );
|
355
|
+
sf->hookesc = g_strdup( inherit->hookesc );
|
356
|
+
}
|
357
|
+
else
|
358
|
+
{
|
359
|
+
/* Default hook values. */
|
360
|
+
sf->hookbeg = g_strdup( HOOKBEG_DEFAULT );
|
361
|
+
sf->hookend = g_strdup( HOOKEND_DEFAULT );
|
362
|
+
sf->hookesc = g_strdup( HOOKESC_DEFAULT );
|
363
|
+
}
|
364
|
+
|
365
|
+
/* Setup fast-lookup caches. */
|
366
|
+
sf_set_hook( sf, hook_none, NULL );
|
367
|
+
|
368
|
+
return sf;
|
369
|
+
}
|
370
|
+
|
371
|
+
|
372
|
+
/**
|
373
|
+
* Store macro start info.
|
374
|
+
*
|
375
|
+
* @param sf Stackfile.
|
376
|
+
*/
|
377
|
+
void sf_mark_macro( stackfile_t* sf )
|
378
|
+
{
|
379
|
+
sf->macro = TRUE;
|
380
|
+
sf->macro_line = sf->lineno;
|
381
|
+
sf->macro_col = sf->column;
|
382
|
+
}
|
383
|
+
|
384
|
+
|
385
|
+
/**
|
386
|
+
* Reset macro start info.
|
387
|
+
*
|
388
|
+
* @param sf Stackfile.
|
389
|
+
*/
|
390
|
+
void sf_unmark_macro( stackfile_t* sf )
|
391
|
+
{
|
392
|
+
sf->macro = FALSE;
|
393
|
+
}
|
394
|
+
|
395
|
+
|
396
|
+
/**
|
397
|
+
* Free Stackfile and close the file stream.
|
398
|
+
*
|
399
|
+
* @param sf Stackfile.
|
400
|
+
*/
|
401
|
+
void sf_rem( stackfile_t* sf )
|
402
|
+
{
|
403
|
+
g_string_free( sf->buf, TRUE );
|
404
|
+
|
405
|
+
if ( sf->fh != stdin )
|
406
|
+
{
|
407
|
+
fclose( sf->fh );
|
408
|
+
}
|
409
|
+
|
410
|
+
g_free( sf->filename );
|
411
|
+
|
412
|
+
g_free( sf->hookbeg );
|
413
|
+
g_free( sf->hookend );
|
414
|
+
g_free( sf->hookesc );
|
415
|
+
|
416
|
+
g_free( sf );
|
417
|
+
}
|
418
|
+
|
419
|
+
|
420
|
+
/**
|
421
|
+
* Get char from Stackfile.
|
422
|
+
*
|
423
|
+
* @param sf Stackfile.
|
424
|
+
*
|
425
|
+
* @return Char or EOF.
|
426
|
+
*/
|
427
|
+
int sf_get( stackfile_t* sf )
|
428
|
+
{
|
429
|
+
int ret;
|
430
|
+
|
431
|
+
if ( sf->buf->len > 0 )
|
432
|
+
{
|
433
|
+
|
434
|
+
/* Pending chars in buffer, don't need to read file (yet). */
|
435
|
+
|
436
|
+
int pos = sf->buf->len-1;
|
437
|
+
|
438
|
+
/* Use buffer as stack with oldest (first to use) on right. */
|
439
|
+
ret = sf->buf->str[ pos ];
|
440
|
+
g_string_truncate( sf->buf, pos );
|
441
|
+
|
442
|
+
}
|
443
|
+
else
|
444
|
+
{
|
445
|
+
|
446
|
+
/* Read from actual file stream. */
|
447
|
+
|
448
|
+
#ifdef DEBUG_CHAR
|
449
|
+
ret = fgetc( sf->fh );
|
450
|
+
if ( DEBUG_CHAR == (char) ret )
|
451
|
+
{
|
452
|
+
mucgly_debug();
|
453
|
+
ret = fgetc( sf->fh );
|
454
|
+
}
|
455
|
+
#else
|
456
|
+
ret = fgetc( sf->fh );
|
457
|
+
#endif
|
458
|
+
|
459
|
+
}
|
460
|
+
|
461
|
+
/* Update file point info. */
|
462
|
+
if ( ret != EOF )
|
463
|
+
{
|
464
|
+
if ( ret == '\n' )
|
465
|
+
{
|
466
|
+
sf->old_column = sf->column;
|
467
|
+
sf->lineno++;
|
468
|
+
sf->column = 0;
|
469
|
+
}
|
470
|
+
else
|
471
|
+
{
|
472
|
+
sf->column++;
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
return ret;
|
477
|
+
}
|
478
|
+
|
479
|
+
|
480
|
+
/**
|
481
|
+
* Put char back to Stackfile.
|
482
|
+
*
|
483
|
+
* @param sf Stackfile.
|
484
|
+
* @param c char.
|
485
|
+
*
|
486
|
+
* @return TRUE.
|
487
|
+
*/
|
488
|
+
gboolean sf_put( stackfile_t* sf, char c )
|
489
|
+
{
|
490
|
+
if ( c == '\n' )
|
491
|
+
{
|
492
|
+
sf->lineno--;
|
493
|
+
sf->column = sf->old_column;
|
494
|
+
sf->old_column = 0;
|
495
|
+
}
|
496
|
+
else
|
497
|
+
{
|
498
|
+
sf->column--;
|
499
|
+
}
|
500
|
+
|
501
|
+
/* Use buffer as stack with oldest (first to use) on right. */
|
502
|
+
g_string_append_c( sf->buf, c );
|
503
|
+
|
504
|
+
return TRUE;
|
505
|
+
}
|
506
|
+
|
507
|
+
|
508
|
+
/**
|
509
|
+
* Set hook value.
|
510
|
+
*
|
511
|
+
* @param sf Stackfile.
|
512
|
+
* @param hook Hook type.
|
513
|
+
* @param value New hook value.
|
514
|
+
*/
|
515
|
+
void sf_set_hook( stackfile_t* sf, hook_t hook, char* value )
|
516
|
+
{
|
517
|
+
|
518
|
+
/* Set values. */
|
519
|
+
switch ( hook )
|
520
|
+
{
|
521
|
+
case hook_beg: g_free( sf->hookbeg ); sf->hookbeg = g_strdup( value ); break;
|
522
|
+
case hook_end: g_free( sf->hookend ); sf->hookend = g_strdup( value ); break;
|
523
|
+
case hook_esc: g_free( sf->hookesc ); sf->hookesc = g_strdup( value ); break;
|
524
|
+
default: break;
|
525
|
+
}
|
526
|
+
|
527
|
+
|
528
|
+
/* Store these equalities for speed-up. */
|
529
|
+
|
530
|
+
if ( !g_strcmp0( sf->hookesc, sf->hookbeg ) )
|
531
|
+
sf->hook_esc_eq_beg = TRUE;
|
532
|
+
else
|
533
|
+
sf->hook_esc_eq_beg = FALSE;
|
534
|
+
|
535
|
+
if ( !g_strcmp0( sf->hookesc, sf->hookend ) )
|
536
|
+
sf->hook_esc_eq_end = TRUE;
|
537
|
+
else
|
538
|
+
sf->hook_esc_eq_end = FALSE;
|
539
|
+
|
540
|
+
/* Hook 1st char lookup. */
|
541
|
+
memset( sf->hook_1st_chars, 0, 256 * sizeof( guchar ) );
|
542
|
+
sf->hook_1st_chars[ sf->hookbeg[0] ] = 1;
|
543
|
+
sf->hook_1st_chars[ sf->hookend[0] ] = 1;
|
544
|
+
sf->hook_1st_chars[ sf->hookesc[0] ] = 1;
|
545
|
+
}
|
546
|
+
|
547
|
+
|
548
|
+
/**
|
549
|
+
* Create new Filestack.
|
550
|
+
*
|
551
|
+
* @return Filestack.
|
552
|
+
*/
|
553
|
+
filestack_t* fs_new( void )
|
554
|
+
{
|
555
|
+
filestack_t* fs;
|
556
|
+
|
557
|
+
fs = g_new0( filestack_t, 1 );
|
558
|
+
fs->file = NULL;
|
559
|
+
return fs;
|
560
|
+
}
|
561
|
+
|
562
|
+
|
563
|
+
/**
|
564
|
+
* Free Filestack and close all Stackfiles.
|
565
|
+
*
|
566
|
+
* @param fs Filestack.
|
567
|
+
*
|
568
|
+
* @return NULL.
|
569
|
+
*/
|
570
|
+
filestack_t* fs_rem( filestack_t* fs )
|
571
|
+
{
|
572
|
+
if ( fs )
|
573
|
+
{
|
574
|
+
for ( GList* p = fs->file; p; p = p->next )
|
575
|
+
{
|
576
|
+
sf_rem( (stackfile_t*) p->data );
|
577
|
+
}
|
578
|
+
|
579
|
+
g_free( fs );
|
580
|
+
}
|
581
|
+
|
582
|
+
return NULL;
|
583
|
+
}
|
584
|
+
|
585
|
+
|
586
|
+
/**
|
587
|
+
* Push file on top of Filestack.
|
588
|
+
*
|
589
|
+
* @param fs Filestack.
|
590
|
+
* @param filename New top file.
|
591
|
+
*/
|
592
|
+
void fs_push_file( filestack_t* fs, gchar* filename )
|
593
|
+
{
|
594
|
+
stackfile_t* sf;
|
595
|
+
|
596
|
+
if ( fs->file )
|
597
|
+
/* Additinal file. */
|
598
|
+
sf = sf_new( filename, fs_topfile(fs) );
|
599
|
+
else
|
600
|
+
/* First file, i.e. inherit stack_default hooks. */
|
601
|
+
sf = sf_new( filename, stack_default );
|
602
|
+
|
603
|
+
/* Push file. */
|
604
|
+
fs->file = g_list_prepend( fs->file, sf );
|
605
|
+
|
606
|
+
/* Set global CurrentStackfile handle. */
|
607
|
+
csf = (stackfile_t*) fs->file->data;
|
608
|
+
}
|
609
|
+
|
610
|
+
|
611
|
+
/**
|
612
|
+
* Pop file from top of Filestack. File is closed.
|
613
|
+
*
|
614
|
+
* @param fs Filestack.
|
615
|
+
*/
|
616
|
+
void fs_pop_file( filestack_t* fs )
|
617
|
+
{
|
618
|
+
stackfile_t* sf;
|
619
|
+
|
620
|
+
sf = fs->file->data;
|
621
|
+
sf_rem( sf );
|
622
|
+
fs->file = g_list_delete_link( fs->file, fs->file );
|
623
|
+
|
624
|
+
/* Set global CurrentStackfile handle. */
|
625
|
+
if ( fs->file )
|
626
|
+
csf = (stackfile_t*) fs->file->data;
|
627
|
+
else
|
628
|
+
csf = NULL;
|
629
|
+
}
|
630
|
+
|
631
|
+
|
632
|
+
/**
|
633
|
+
* Get char from (through) Filestack (i.e. top file).
|
634
|
+
*
|
635
|
+
* @param fs Filestack.
|
636
|
+
*
|
637
|
+
* @return Char or EOF.
|
638
|
+
*/
|
639
|
+
int fs_get( filestack_t* fs )
|
640
|
+
{
|
641
|
+
int ret;
|
642
|
+
|
643
|
+
if ( fs->file != NULL )
|
644
|
+
{
|
645
|
+
ret = sf_get( fs_topfile(fs) );
|
646
|
+
|
647
|
+
/* Pop Stackfiles until no files or non-EOF char is received. */
|
648
|
+
while ( ret == EOF )
|
649
|
+
{
|
650
|
+
fs_pop_file( fs );
|
651
|
+
if ( fs->file == NULL )
|
652
|
+
return EOF;
|
653
|
+
ret = sf_get( fs_topfile(fs) );
|
654
|
+
}
|
655
|
+
|
656
|
+
return ret;
|
657
|
+
}
|
658
|
+
else
|
659
|
+
{
|
660
|
+
return EOF;
|
661
|
+
}
|
662
|
+
}
|
663
|
+
|
664
|
+
|
665
|
+
/**
|
666
|
+
* Put back char.
|
667
|
+
*
|
668
|
+
* @param fs Filestack.
|
669
|
+
* @param c Char.
|
670
|
+
*
|
671
|
+
* @return TRUE.
|
672
|
+
*/
|
673
|
+
gboolean fs_put( filestack_t* fs, char c )
|
674
|
+
{
|
675
|
+
sf_put( fs_topfile(fs), c );
|
676
|
+
return TRUE;
|
677
|
+
}
|
678
|
+
|
679
|
+
|
680
|
+
/**
|
681
|
+
* Get n chars from Filestack. If ret is non-null, use that for
|
682
|
+
* storage.
|
683
|
+
*
|
684
|
+
* @param fs Filestack.
|
685
|
+
* @param n Number of chars to get.
|
686
|
+
* @param ret Storage for data.
|
687
|
+
*
|
688
|
+
* @return Return data.
|
689
|
+
*/
|
690
|
+
GString* fs_get_n( filestack_t* fs, int n, GString* ret )
|
691
|
+
{
|
692
|
+
|
693
|
+
if ( ret == NULL )
|
694
|
+
{
|
695
|
+
/* New GString is needed for storage. */
|
696
|
+
ret = g_string_sized_new( 0 );
|
697
|
+
}
|
698
|
+
else
|
699
|
+
{
|
700
|
+
/* Reset size of the existing storage. */
|
701
|
+
g_string_set_size( ret, 0 );
|
702
|
+
}
|
703
|
+
|
704
|
+
|
705
|
+
/* Get n chars from current file. If EOF is encountered, reading is
|
706
|
+
terminated. */
|
707
|
+
|
708
|
+
for ( int i = 0, c; ; )
|
709
|
+
{
|
710
|
+
if ( i >= n )
|
711
|
+
break;
|
712
|
+
|
713
|
+
c = fs_get( fs );
|
714
|
+
|
715
|
+
if ( c != EOF )
|
716
|
+
{
|
717
|
+
g_string_append_c( ret, (char) c );
|
718
|
+
i++;
|
719
|
+
}
|
720
|
+
else
|
721
|
+
{
|
722
|
+
break;
|
723
|
+
}
|
724
|
+
}
|
725
|
+
|
726
|
+
return ret;
|
727
|
+
}
|
728
|
+
|
729
|
+
|
730
|
+
/**
|
731
|
+
* Put chars in str back to Filestack. If n is 0, then use strlen to
|
732
|
+
* get the count.
|
733
|
+
*
|
734
|
+
* @param fs Filestack.
|
735
|
+
* @param str Content to add.
|
736
|
+
* @param n Number of chars from content.
|
737
|
+
*
|
738
|
+
* @return True if success.
|
739
|
+
*/
|
740
|
+
gboolean fs_put_n( filestack_t* fs, gchar* str, int n )
|
741
|
+
{
|
742
|
+
if ( n == 0 )
|
743
|
+
n = strlen( str );
|
744
|
+
|
745
|
+
/* Put chars with newest first (i.e. reverse order), so that oldest
|
746
|
+
comes out first. */
|
747
|
+
for ( int i = n-1; i >= 0; i-- )
|
748
|
+
{
|
749
|
+
fs_put( fs, str[ i ] );
|
750
|
+
}
|
751
|
+
|
752
|
+
return TRUE;
|
753
|
+
}
|
754
|
+
|
755
|
+
|
756
|
+
/**
|
757
|
+
* Create new Outfile. If filename is NULL, then stream is stdout.
|
758
|
+
*
|
759
|
+
* @param filename File name (or NULL for stdout).
|
760
|
+
*
|
761
|
+
* @return Outfile.
|
762
|
+
*/
|
763
|
+
outfile_t* outfile_new( gchar* filename )
|
764
|
+
{
|
765
|
+
outfile_t* of;
|
766
|
+
|
767
|
+
of = g_new0( outfile_t, 1 );
|
768
|
+
of->lineno = 0;
|
769
|
+
|
770
|
+
if ( filename )
|
771
|
+
{
|
772
|
+
|
773
|
+
/* Disk file output. */
|
774
|
+
|
775
|
+
of->filename = g_strdup( filename );
|
776
|
+
|
777
|
+
of->fh = g_fopen( filename, (gchar*) "w" );
|
778
|
+
|
779
|
+
if ( of->fh == NULL )
|
780
|
+
{
|
781
|
+
stackfile_t* err_sf;
|
782
|
+
|
783
|
+
/* Get the includer. */
|
784
|
+
err_sf = ruby_ps->fs->file->data;
|
785
|
+
|
786
|
+
mucgly_fatal( err_sf, "Can't open \"%s\"", err_sf->filename );
|
787
|
+
}
|
788
|
+
|
789
|
+
}
|
790
|
+
else
|
791
|
+
{
|
792
|
+
|
793
|
+
/* STDOUT output. */
|
794
|
+
of->filename = g_strdup( "<STDOUT>" );
|
795
|
+
of->fh = stdout;
|
796
|
+
|
797
|
+
}
|
798
|
+
|
799
|
+
return of;
|
800
|
+
}
|
801
|
+
|
802
|
+
|
803
|
+
/**
|
804
|
+
* Free Outfile and close output stream if non-stdout.
|
805
|
+
*
|
806
|
+
* @param of Outfile.
|
807
|
+
*/
|
808
|
+
void outfile_rem( outfile_t* of )
|
809
|
+
{
|
810
|
+
if ( of->fh != stdout )
|
811
|
+
fclose( of->fh );
|
812
|
+
|
813
|
+
g_free( of->filename );
|
814
|
+
g_free( of );
|
815
|
+
}
|
816
|
+
|
817
|
+
|
818
|
+
/**
|
819
|
+
* Create Pstate. Create input file stack and output file
|
820
|
+
* stack. Initialize parsing state.
|
821
|
+
*
|
822
|
+
* @param outfile Initial input file name.
|
823
|
+
*
|
824
|
+
* @return Pstate.
|
825
|
+
*/
|
826
|
+
pstate_t* ps_new( gchar* outfile )
|
827
|
+
{
|
828
|
+
pstate_t* ps;
|
829
|
+
|
830
|
+
ps = g_new0( pstate_t, 1 );
|
831
|
+
|
832
|
+
ps->fs = fs_new();
|
833
|
+
|
834
|
+
ps->in_macro = 0;
|
835
|
+
|
836
|
+
/* Preview input buffer. */
|
837
|
+
ps->check_buf = g_string_sized_new( 0 );
|
838
|
+
|
839
|
+
/* Top level input buffer. */
|
840
|
+
ps->macro_buf = g_string_sized_new( 0 );
|
841
|
+
|
842
|
+
/* Match string buffer. */
|
843
|
+
ps->match_buf = g_string_sized_new( 0 );
|
844
|
+
|
845
|
+
|
846
|
+
ps->output = g_list_prepend( ps->output, outfile_new( outfile ) );
|
847
|
+
ps->flush = FALSE;
|
848
|
+
|
849
|
+
return ps;
|
850
|
+
}
|
851
|
+
|
852
|
+
|
853
|
+
/**
|
854
|
+
* Free Pstate. Close all input and output files.
|
855
|
+
*
|
856
|
+
* @param ps Pstate.
|
857
|
+
*/
|
858
|
+
void ps_rem( pstate_t* ps )
|
859
|
+
{
|
860
|
+
fs_rem( ps->fs );
|
861
|
+
|
862
|
+
g_string_free( ps->check_buf, TRUE );
|
863
|
+
g_string_free( ps->macro_buf, TRUE );
|
864
|
+
|
865
|
+
for ( GList* of = ps->output; of; of = of->next )
|
866
|
+
{
|
867
|
+
outfile_rem( (outfile_t*) of->data );
|
868
|
+
}
|
869
|
+
|
870
|
+
g_free( ps );
|
871
|
+
}
|
872
|
+
|
873
|
+
|
874
|
+
/**
|
875
|
+
* Fast check for current input char being first char of any of the
|
876
|
+
* hooks.
|
877
|
+
*
|
878
|
+
* @param ps Pstate.
|
879
|
+
*
|
880
|
+
* @return TRUE if next char matches 1st char of any hook.
|
881
|
+
*/
|
882
|
+
gboolean ps_check_hook( pstate_t* ps, int c )
|
883
|
+
{
|
884
|
+
stackfile_t* sf;
|
885
|
+
|
886
|
+
if ( c == EOF )
|
887
|
+
return FALSE;
|
888
|
+
|
889
|
+
sf = ps_topfile(ps);
|
890
|
+
|
891
|
+
if ( sf->hook_1st_chars[ c ] )
|
892
|
+
return TRUE;
|
893
|
+
else
|
894
|
+
return FALSE;
|
895
|
+
}
|
896
|
+
|
897
|
+
|
898
|
+
/**
|
899
|
+
* Check if input has the match string coming next. Erase the matched
|
900
|
+
* string if erase is true and input is matched.
|
901
|
+
*
|
902
|
+
* @param ps Pstate.
|
903
|
+
* @param match Match string.
|
904
|
+
* @param erase Erase matched string (but only if matched).
|
905
|
+
*
|
906
|
+
* @return True if match.
|
907
|
+
*/
|
908
|
+
gboolean ps_check( pstate_t* ps, gchar* match, gboolean erase )
|
909
|
+
{
|
910
|
+
GString* input;
|
911
|
+
int len;
|
912
|
+
gboolean ret;
|
913
|
+
|
914
|
+
/* Dup match string since it might disappear if Filestack stack is
|
915
|
+
popped. */
|
916
|
+
|
917
|
+
g_string_assign( ps->match_buf, match );
|
918
|
+
|
919
|
+
len = strlen( match );
|
920
|
+
input = fs_get_n( ps->fs, len, ps->check_buf );
|
921
|
+
ret = !g_strcmp0( input->str, ps->match_buf->str );
|
922
|
+
|
923
|
+
if ( !ret || !erase )
|
924
|
+
/* Put back checked chars. */
|
925
|
+
fs_put_n( ps->fs, input->str, input->len );
|
926
|
+
|
927
|
+
return ret;
|
928
|
+
}
|
929
|
+
|
930
|
+
|
931
|
+
/**
|
932
|
+
* Check input for hookesc.
|
933
|
+
*
|
934
|
+
* @param ps Pstate.
|
935
|
+
*
|
936
|
+
* @return TRUE on match.
|
937
|
+
*/
|
938
|
+
gboolean ps_check_hookesc( pstate_t* ps )
|
939
|
+
{
|
940
|
+
if ( !ps_has_file(ps) )
|
941
|
+
return FALSE;
|
942
|
+
|
943
|
+
return ps_check( ps, ps_topfile(ps)->hookesc, TRUE );
|
944
|
+
}
|
945
|
+
|
946
|
+
|
947
|
+
/**
|
948
|
+
* Check input for hookbeg.
|
949
|
+
*
|
950
|
+
* @param ps Pstate.
|
951
|
+
*
|
952
|
+
* @return TRUE on match.
|
953
|
+
*/
|
954
|
+
gboolean ps_check_hookbeg( pstate_t* ps )
|
955
|
+
{
|
956
|
+
if ( !ps_has_file(ps) )
|
957
|
+
return FALSE;
|
958
|
+
|
959
|
+
return ps_check( ps, ps_topfile(ps)->hookbeg, TRUE );
|
960
|
+
}
|
961
|
+
|
962
|
+
|
963
|
+
/**
|
964
|
+
* Check input for hookend.
|
965
|
+
*
|
966
|
+
* @param ps Pstate.
|
967
|
+
*
|
968
|
+
* @return TRUE on match.
|
969
|
+
*/
|
970
|
+
gboolean ps_check_hookend( pstate_t* ps )
|
971
|
+
{
|
972
|
+
if ( !ps_has_file(ps) )
|
973
|
+
return FALSE;
|
974
|
+
|
975
|
+
return ps_check( ps, ps_topfile(ps)->hookend, TRUE );
|
976
|
+
}
|
977
|
+
|
978
|
+
|
979
|
+
/**
|
980
|
+
* Get char (through Filestack).
|
981
|
+
*
|
982
|
+
* @param ps Pstate.
|
983
|
+
*
|
984
|
+
* @return Char or EOF.
|
985
|
+
*/
|
986
|
+
int ps_in( pstate_t* ps )
|
987
|
+
{
|
988
|
+
return fs_get( ps->fs );
|
989
|
+
}
|
990
|
+
|
991
|
+
|
992
|
+
/**
|
993
|
+
* Output char to current output stream.
|
994
|
+
*
|
995
|
+
* @param ps Pstate.
|
996
|
+
* @param c Char.
|
997
|
+
*/
|
998
|
+
void ps_out( pstate_t* ps, int c )
|
999
|
+
{
|
1000
|
+
outfile_t* of = ps->output->data;
|
1001
|
+
|
1002
|
+
if ( c == '\n' )
|
1003
|
+
of->lineno++;
|
1004
|
+
|
1005
|
+
fputc( c, of->fh );
|
1006
|
+
if ( ps->flush )
|
1007
|
+
fflush( of->fh );
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
|
1011
|
+
/**
|
1012
|
+
* Output chars with ps_out.
|
1013
|
+
*
|
1014
|
+
* @param ps Pstate.
|
1015
|
+
* @param str Output string (or NULL for no output).
|
1016
|
+
*/
|
1017
|
+
void ps_out_str( pstate_t* ps, gchar* str )
|
1018
|
+
{
|
1019
|
+
if ( str )
|
1020
|
+
{
|
1021
|
+
int len = strlen( str );
|
1022
|
+
|
1023
|
+
for ( int i = 0; i < len; i++ )
|
1024
|
+
{
|
1025
|
+
ps_out( ps, str[ i ] );
|
1026
|
+
}
|
1027
|
+
}
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
|
1031
|
+
/**
|
1032
|
+
* Push new output stream on top of output file stack.
|
1033
|
+
*
|
1034
|
+
* @param ps Pstate.
|
1035
|
+
* @param filename File name.
|
1036
|
+
*/
|
1037
|
+
void ps_push_file( pstate_t* ps, gchar* filename )
|
1038
|
+
{
|
1039
|
+
ps->output = g_list_prepend( ps->output,
|
1040
|
+
outfile_new( filename ) );
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
|
1044
|
+
/**
|
1045
|
+
* Pop file from output file stack. Close the stream.
|
1046
|
+
*
|
1047
|
+
* @param ps Pstate.
|
1048
|
+
*/
|
1049
|
+
void ps_pop_file( pstate_t* ps )
|
1050
|
+
{
|
1051
|
+
outfile_t* of;
|
1052
|
+
|
1053
|
+
of = ps->output->data;
|
1054
|
+
outfile_rem( of );
|
1055
|
+
ps->output = g_list_delete_link( ps->output, ps->output );
|
1056
|
+
}
|
1057
|
+
|
1058
|
+
|
1059
|
+
/**
|
1060
|
+
* Return current Stackfile (input stream).
|
1061
|
+
*
|
1062
|
+
* @param ps Pstate.
|
1063
|
+
*
|
1064
|
+
* @return Stackfile (or NULL if none).
|
1065
|
+
*/
|
1066
|
+
stackfile_t* ps_current_file( pstate_t* ps )
|
1067
|
+
{
|
1068
|
+
if ( ps == NULL )
|
1069
|
+
return NULL;
|
1070
|
+
else if ( !ps_has_file(ps) )
|
1071
|
+
return NULL;
|
1072
|
+
else
|
1073
|
+
return ps_topfile(ps);
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
|
1077
|
+
/**
|
1078
|
+
* Initialize macro content collection.
|
1079
|
+
*
|
1080
|
+
* @param ps Pstate.
|
1081
|
+
*/
|
1082
|
+
void ps_start_collect( pstate_t* ps )
|
1083
|
+
{
|
1084
|
+
g_string_set_size( ps->macro_buf, 0 );
|
1085
|
+
}
|
1086
|
+
|
1087
|
+
|
1088
|
+
/**
|
1089
|
+
* Add content to macro.
|
1090
|
+
*
|
1091
|
+
* @param ps Pstate.
|
1092
|
+
* @param c Char to add.
|
1093
|
+
*/
|
1094
|
+
void ps_collect( pstate_t* ps, int c )
|
1095
|
+
{
|
1096
|
+
g_string_append_c( ps->macro_buf, c );
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
|
1100
|
+
/**
|
1101
|
+
* Get current macro content.
|
1102
|
+
*
|
1103
|
+
* @param ps Pstate.
|
1104
|
+
*
|
1105
|
+
* @return Macro content.
|
1106
|
+
*/
|
1107
|
+
char* ps_get_macro( pstate_t* ps )
|
1108
|
+
{
|
1109
|
+
return ps->macro_buf->str;
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
|
1113
|
+
/**
|
1114
|
+
* Evaluate str as Ruby code and convert result to string. Conversion
|
1115
|
+
* is performed if execution was successful and conversion was
|
1116
|
+
* requested.
|
1117
|
+
*
|
1118
|
+
* @param ps Pstate.
|
1119
|
+
* @param str Ruby code string.
|
1120
|
+
* @param to_str Result convert enable.
|
1121
|
+
* @param ctxt Context (name) for execution.
|
1122
|
+
*
|
1123
|
+
* @return Ruby code execution as string (or NULL).
|
1124
|
+
*/
|
1125
|
+
gchar* ps_eval_ruby_str( pstate_t* ps, gchar* str, gboolean to_str, char* ctxt )
|
1126
|
+
{
|
1127
|
+
int status = 0;
|
1128
|
+
VALUE ret;
|
1129
|
+
|
1130
|
+
if ( !ctxt )
|
1131
|
+
/* Used default context. */
|
1132
|
+
ctxt = "macro";
|
1133
|
+
|
1134
|
+
ret = rb_eval_string_protect( (char*) str, &status );
|
1135
|
+
|
1136
|
+
if ( status == 0 )
|
1137
|
+
{
|
1138
|
+
/* Successfull execution. */
|
1139
|
+
if ( to_str )
|
1140
|
+
{
|
1141
|
+
if ( TYPE( ret ) == T_STRING )
|
1142
|
+
{
|
1143
|
+
return ( (gchar*) StringValueCStr( ret ) );
|
1144
|
+
}
|
1145
|
+
else
|
1146
|
+
{
|
1147
|
+
/* Convert to Ruby String. */
|
1148
|
+
ret = rb_inspect( ret );
|
1149
|
+
return ( (gchar*) StringValueCStr( ret ) );
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
else
|
1153
|
+
{
|
1154
|
+
return NULL;
|
1155
|
+
}
|
1156
|
+
}
|
1157
|
+
else
|
1158
|
+
{
|
1159
|
+
mucgly_error( ps_current_file( ps ), "Error in %s: \n in ruby (%s): %s",
|
1160
|
+
ctxt,
|
1161
|
+
RSTRING_PTR(rb_inspect(RARRAY_AREF(rb_gv_get("$@"),0))),
|
1162
|
+
RSTRING_PTR(rb_inspect(rb_gv_get("$!"))) );
|
1163
|
+
return NULL;
|
1164
|
+
}
|
1165
|
+
}
|
1166
|
+
|
1167
|
+
|
1168
|
+
/**
|
1169
|
+
* Load file Ruby intepreter.
|
1170
|
+
*
|
1171
|
+
* @param filename Source file.
|
1172
|
+
*/
|
1173
|
+
void ps_load_ruby_file( pstate_t* ps, gchar* filename )
|
1174
|
+
{
|
1175
|
+
GString* cmd;
|
1176
|
+
|
1177
|
+
cmd = g_string_sized_new( 0 );
|
1178
|
+
g_string_printf( cmd, "load \"%s\"", filename );
|
1179
|
+
ps_eval_ruby_str( ps, cmd->str, FALSE, "load" );
|
1180
|
+
g_string_free( cmd, TRUE );
|
1181
|
+
}
|
1182
|
+
|
1183
|
+
|
1184
|
+
/**
|
1185
|
+
* Execute Mucgly command/macro.
|
1186
|
+
*
|
1187
|
+
* @param ps Pstate.
|
1188
|
+
*
|
1189
|
+
* @return TRUE if input processing should be aborted.
|
1190
|
+
*/
|
1191
|
+
gboolean ps_eval_cmd( pstate_t* ps )
|
1192
|
+
{
|
1193
|
+
char* cmd;
|
1194
|
+
|
1195
|
+
cmd = ps_get_macro( ps );
|
1196
|
+
|
1197
|
+
if ( cmd[0] == ':' )
|
1198
|
+
{
|
1199
|
+
|
1200
|
+
/* Mucgly internal command. */
|
1201
|
+
|
1202
|
+
int len;
|
1203
|
+
|
1204
|
+
if ( 0 ) {}
|
1205
|
+
else if ( ( len = len_str_cmp( ":hookbeg", cmd ) ) )
|
1206
|
+
sf_set_hook( ps_topfile(ps), hook_beg, &cmd[ len+1 ] );
|
1207
|
+
else if ( ( len = len_str_cmp( ":hookend", cmd ) ) )
|
1208
|
+
sf_set_hook( ps_topfile(ps), hook_end, &cmd[ len+1 ] );
|
1209
|
+
else if ( ( len = len_str_cmp( ":hookesc", cmd ) ) )
|
1210
|
+
sf_set_hook( ps_topfile(ps), hook_esc, &cmd[ len+1 ] );
|
1211
|
+
|
1212
|
+
else if ( ( len = len_str_cmp( ":hookall", cmd ) ) )
|
1213
|
+
{
|
1214
|
+
sf_set_hook( ps_topfile(ps), hook_beg, &cmd[ len+1 ] );
|
1215
|
+
sf_set_hook( ps_topfile(ps), hook_end, &cmd[ len+1 ] );
|
1216
|
+
sf_set_hook( ps_topfile(ps), hook_esc, &cmd[ len+1 ] );
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
else if ( ( len = len_str_cmp( ":hook", cmd ) ) )
|
1220
|
+
{
|
1221
|
+
gchar** pieces;
|
1222
|
+
|
1223
|
+
pieces = g_strsplit( &cmd[ len+1 ], " ", 2 );
|
1224
|
+
if ( g_strv_length( pieces ) == 2 )
|
1225
|
+
{
|
1226
|
+
/* Two hooks separated by space. */
|
1227
|
+
sf_set_hook( ps_topfile(ps), hook_beg, pieces[0] );
|
1228
|
+
sf_set_hook( ps_topfile(ps), hook_end, pieces[1] );
|
1229
|
+
}
|
1230
|
+
else
|
1231
|
+
{
|
1232
|
+
/* Only one hook specified. */
|
1233
|
+
sf_set_hook( ps_topfile(ps), hook_beg, pieces[0] );
|
1234
|
+
sf_set_hook( ps_topfile(ps), hook_end, pieces[0] );
|
1235
|
+
}
|
1236
|
+
g_strfreev( pieces );
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
else if ( ( len = len_str_cmp( ":include", cmd ) ) )
|
1240
|
+
fs_push_file( ps->fs, &cmd[ len+1 ] );
|
1241
|
+
|
1242
|
+
else if ( ( len = len_str_cmp( ":source", cmd ) ) )
|
1243
|
+
ps_load_ruby_file( ps, &cmd[ len+1 ] );
|
1244
|
+
|
1245
|
+
else if ( ( len = len_str_cmp( ":comment", cmd ) ) )
|
1246
|
+
{
|
1247
|
+
/* Do nothing. */
|
1248
|
+
}
|
1249
|
+
|
1250
|
+
else if ( ( len = len_str_cmp( ":exit", cmd ) ) )
|
1251
|
+
{
|
1252
|
+
/* Exit processing. */
|
1253
|
+
return TRUE;
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
else
|
1257
|
+
{
|
1258
|
+
mucgly_error( ps_topfile(ps), "Unknown internal command: \"%s\"", &cmd[len+1] );
|
1259
|
+
}
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
else if ( cmd[0] == '.' )
|
1263
|
+
{
|
1264
|
+
/* Mucgly variable output. */
|
1265
|
+
ps_out_str( ps, ps_eval_ruby_str( ps, &cmd[1], TRUE, NULL ) );
|
1266
|
+
}
|
1267
|
+
|
1268
|
+
else if ( cmd[0] == '/' )
|
1269
|
+
{
|
1270
|
+
/* Mucgly comment, do nothing. */
|
1271
|
+
}
|
1272
|
+
|
1273
|
+
else if ( cmd[0] == '#' )
|
1274
|
+
{
|
1275
|
+
/* Postpone evaluation (to next round). */
|
1276
|
+
|
1277
|
+
ps_out_str( ps, ps_topfile(ps)->hookbeg );
|
1278
|
+
|
1279
|
+
/* Remove one # sign. */
|
1280
|
+
ps_out_str( ps, &cmd[1] );
|
1281
|
+
|
1282
|
+
ps_out_str( ps, ps_topfile(ps)->hookend );
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
else
|
1286
|
+
{
|
1287
|
+
/* Ruby code execution. */
|
1288
|
+
ps_eval_ruby_str( ps, cmd, FALSE, NULL );
|
1289
|
+
}
|
1290
|
+
|
1291
|
+
return FALSE;
|
1292
|
+
}
|
1293
|
+
|
1294
|
+
|
1295
|
+
/** Convenience macro for hookend. */
|
1296
|
+
#define HOOK_END_SEQ { \
|
1297
|
+
ps->in_macro--; \
|
1298
|
+
if ( ps->in_macro ) \
|
1299
|
+
{ \
|
1300
|
+
/* Multilevel macro decrement. */ \
|
1301
|
+
/* Output the consumed hookend. */ \
|
1302
|
+
ps_out_str( ps, ps_topfile(ps)->hookend ); \
|
1303
|
+
} \
|
1304
|
+
else \
|
1305
|
+
{ \
|
1306
|
+
/* Back to base level from macro, eval the macro. */ \
|
1307
|
+
gboolean do_break; \
|
1308
|
+
do_break = ps_eval_cmd( ps ); \
|
1309
|
+
sf_unmark_macro( ps_topfile( ps ) ); \
|
1310
|
+
if ( do_break ) \
|
1311
|
+
break; \
|
1312
|
+
} \
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
|
1316
|
+
/* Convenience macro for non-hook processing. */
|
1317
|
+
#define NON_HOOK_SEQ { \
|
1318
|
+
/* Non-hook input. */ \
|
1319
|
+
if ( ps->in_macro ) \
|
1320
|
+
{ \
|
1321
|
+
if ( c == EOF ) \
|
1322
|
+
mucgly_error( ps_current_file(ps), "Got EOF within macro!" ); \
|
1323
|
+
/* Add content to macro. */ \
|
1324
|
+
ps_collect( ps, c ); \
|
1325
|
+
} \
|
1326
|
+
else \
|
1327
|
+
{ \
|
1328
|
+
if ( c == EOF ) \
|
1329
|
+
break; \
|
1330
|
+
else \
|
1331
|
+
/* Output to current output file. */ \
|
1332
|
+
ps_out( ps, c ); \
|
1333
|
+
} \
|
1334
|
+
}
|
1335
|
+
|
1336
|
+
|
1337
|
+
/**
|
1338
|
+
* Process input file (or stack of files).
|
1339
|
+
*
|
1340
|
+
* @param ps Pstate.
|
1341
|
+
* @param infile Input file name.
|
1342
|
+
* @param outfile Output file name (or NULL). Outfile string is freed.
|
1343
|
+
*/
|
1344
|
+
void ps_process_file( pstate_t* ps, gchar* infile, gchar* outfile )
|
1345
|
+
{
|
1346
|
+
int c;
|
1347
|
+
|
1348
|
+
fs_push_file( ps->fs, infile );
|
1349
|
+
|
1350
|
+
if ( outfile )
|
1351
|
+
{
|
1352
|
+
ps_push_file( ps, outfile );
|
1353
|
+
g_free( outfile );
|
1354
|
+
}
|
1355
|
+
|
1356
|
+
/* ------------------------------------------------------------
|
1357
|
+
* Process input:
|
1358
|
+
* ------------------------------------------------------------ */
|
1359
|
+
|
1360
|
+
for (;;)
|
1361
|
+
{
|
1362
|
+
|
1363
|
+
/* For each input char, we must explicitly read it since
|
1364
|
+
otherwise the Filestack does not operate correctly, i.e. we
|
1365
|
+
are not allowed to put back to stream after EOF has been
|
1366
|
+
encountered. Files are popped automatically after EOF. */
|
1367
|
+
|
1368
|
+
c = ps_in( ps );
|
1369
|
+
|
1370
|
+
/* Check if next char starts one of the hooks. */
|
1371
|
+
if ( ps_check_hook( ps, c ) )
|
1372
|
+
{
|
1373
|
+
|
1374
|
+
fs_put( ps->fs, c );
|
1375
|
+
|
1376
|
+
/* Escape is always checked before other hooks. */
|
1377
|
+
if ( ps_check_hookesc( ps ) )
|
1378
|
+
{
|
1379
|
+
/* Handle ESCAPEs depending whether in macro or not. */
|
1380
|
+
|
1381
|
+
if ( ps->in_macro )
|
1382
|
+
{
|
1383
|
+
|
1384
|
+
/* Escape in macro. */
|
1385
|
+
|
1386
|
+
/* Just transfer the following char to output. */
|
1387
|
+
c = ps_in( ps );
|
1388
|
+
|
1389
|
+
/* Error, if EOF in macro. */
|
1390
|
+
if ( c == EOF )
|
1391
|
+
{
|
1392
|
+
mucgly_error( ps_current_file(ps), "Got EOF within macro!" );
|
1393
|
+
}
|
1394
|
+
else
|
1395
|
+
{
|
1396
|
+
|
1397
|
+
if ( ( c == ' ' || c == '\n' ) &&
|
1398
|
+
ps_topfile(ps)->hook_esc_eq_end )
|
1399
|
+
{
|
1400
|
+
|
1401
|
+
/* Space/newline and hookesc is same as hookbeg. */
|
1402
|
+
|
1403
|
+
ps->in_macro--;
|
1404
|
+
|
1405
|
+
if ( ps->in_macro )
|
1406
|
+
{
|
1407
|
+
/* Multilevel macro decrement. */
|
1408
|
+
|
1409
|
+
/* Output the consumed hookend. */
|
1410
|
+
ps_out_str( ps, ps_topfile(ps)->hookend );
|
1411
|
+
}
|
1412
|
+
else
|
1413
|
+
{
|
1414
|
+
/* Back to base level from macro, eval the macro. */
|
1415
|
+
if ( ps_eval_cmd( ps ) )
|
1416
|
+
break;
|
1417
|
+
}
|
1418
|
+
}
|
1419
|
+
else
|
1420
|
+
{
|
1421
|
+
ps_collect( ps, c );
|
1422
|
+
}
|
1423
|
+
}
|
1424
|
+
}
|
1425
|
+
else
|
1426
|
+
{
|
1427
|
+
|
1428
|
+
/* Escape outside macro. */
|
1429
|
+
|
1430
|
+
/* Map next char according to escape rules. */
|
1431
|
+
c = ps_in( ps );
|
1432
|
+
|
1433
|
+
if ( c == EOF )
|
1434
|
+
{
|
1435
|
+
/* EOF */
|
1436
|
+
break;
|
1437
|
+
}
|
1438
|
+
else
|
1439
|
+
{
|
1440
|
+
|
1441
|
+
switch ( (char)c )
|
1442
|
+
{
|
1443
|
+
|
1444
|
+
case '\n':
|
1445
|
+
/* Eat newlines. */
|
1446
|
+
break;
|
1447
|
+
|
1448
|
+
case ' ':
|
1449
|
+
/* Eat spaces. */
|
1450
|
+
break;
|
1451
|
+
|
1452
|
+
default:
|
1453
|
+
|
1454
|
+
if ( ps_topfile(ps)->hook_esc_eq_beg )
|
1455
|
+
{
|
1456
|
+
|
1457
|
+
if ( ps_topfile(ps)->hookesc[1] == 0
|
1458
|
+
&& c == ps_topfile(ps)->hookesc[0] )
|
1459
|
+
{
|
1460
|
+
/* Escape is one char long and following
|
1461
|
+
char was ESC (i.e. escaped escape). */
|
1462
|
+
ps_out( ps, c );
|
1463
|
+
}
|
1464
|
+
else
|
1465
|
+
{
|
1466
|
+
/* Escape is same as hookbeg and escape is
|
1467
|
+
not used to eat out spaces. */
|
1468
|
+
ps->in_macro++;
|
1469
|
+
|
1470
|
+
/* Put back the extra char (for safety/clarity). */
|
1471
|
+
fs_put( ps->fs, c );
|
1472
|
+
|
1473
|
+
/* Start collect the macro content. */
|
1474
|
+
ps_start_collect( ps );
|
1475
|
+
}
|
1476
|
+
}
|
1477
|
+
else
|
1478
|
+
{
|
1479
|
+
/* Literal output. */
|
1480
|
+
ps_out( ps, c );
|
1481
|
+
}
|
1482
|
+
}
|
1483
|
+
}
|
1484
|
+
|
1485
|
+
}
|
1486
|
+
}
|
1487
|
+
else if ( ps->in_macro && ps_check_hookend( ps ) )
|
1488
|
+
{
|
1489
|
+
/* Hookend has priority over hookbeg if we are in macro. */
|
1490
|
+
HOOK_END_SEQ;
|
1491
|
+
}
|
1492
|
+
else if ( ps_check_hookbeg( ps ) )
|
1493
|
+
{
|
1494
|
+
|
1495
|
+
if ( ps->in_macro )
|
1496
|
+
{
|
1497
|
+
/* Macro in macro. */
|
1498
|
+
|
1499
|
+
/* Increase macro level. */
|
1500
|
+
ps->in_macro++;
|
1501
|
+
|
1502
|
+
/* Output the consumed hookbeg. */
|
1503
|
+
ps_out_str( ps, ps_topfile(ps)->hookbeg );
|
1504
|
+
}
|
1505
|
+
else
|
1506
|
+
{
|
1507
|
+
/* New macro starting. */
|
1508
|
+
ps->in_macro++;
|
1509
|
+
|
1510
|
+
/* Store macro start info. */
|
1511
|
+
sf_mark_macro( ps_topfile( ps ) );
|
1512
|
+
|
1513
|
+
ps_start_collect( ps );
|
1514
|
+
}
|
1515
|
+
|
1516
|
+
}
|
1517
|
+
else if ( ps_check_hookend( ps ) )
|
1518
|
+
{
|
1519
|
+
HOOK_END_SEQ;
|
1520
|
+
}
|
1521
|
+
else
|
1522
|
+
{
|
1523
|
+
c = ps_in( ps );
|
1524
|
+
NON_HOOK_SEQ;
|
1525
|
+
}
|
1526
|
+
}
|
1527
|
+
else
|
1528
|
+
{
|
1529
|
+
NON_HOOK_SEQ;
|
1530
|
+
}
|
1531
|
+
}
|
1532
|
+
|
1533
|
+
if ( outfile )
|
1534
|
+
ps_pop_file( ps );
|
1535
|
+
|
1536
|
+
}
|
1537
|
+
|
1538
|
+
|
1539
|
+
|
1540
|
+
|
1541
|
+
/* ------------------------------------------------------------
|
1542
|
+
* Mucgly Ruby-functions:
|
1543
|
+
* ------------------------------------------------------------ */
|
1544
|
+
|
1545
|
+
|
1546
|
+
/**
|
1547
|
+
* Mucgly.write method. Write output current output without NL.
|
1548
|
+
*
|
1549
|
+
* @param obj Not used.
|
1550
|
+
* @param rstr Written string (Ruby String).
|
1551
|
+
*
|
1552
|
+
* @return Qnil.
|
1553
|
+
*/
|
1554
|
+
VALUE mucgly_write( VALUE obj, VALUE rstr )
|
1555
|
+
{
|
1556
|
+
char* str;
|
1557
|
+
VALUE tmp;
|
1558
|
+
|
1559
|
+
if ( TYPE( rstr ) == T_STRING )
|
1560
|
+
tmp = rstr;
|
1561
|
+
else
|
1562
|
+
/* Convert to Ruby String. */
|
1563
|
+
tmp = rb_inspect( rstr );
|
1564
|
+
|
1565
|
+
str = RSTRING_PTR( tmp );
|
1566
|
+
ps_out_str( ruby_ps, str );
|
1567
|
+
return Qnil;
|
1568
|
+
}
|
1569
|
+
|
1570
|
+
|
1571
|
+
/**
|
1572
|
+
* Mucgly.puts method. Write output current output with NL.
|
1573
|
+
*
|
1574
|
+
* @param obj Not used.
|
1575
|
+
* @param rstr Written string (Ruby String).
|
1576
|
+
*
|
1577
|
+
* @return Qnil.
|
1578
|
+
*/
|
1579
|
+
VALUE mucgly_puts( VALUE obj, VALUE rstr )
|
1580
|
+
{
|
1581
|
+
char* str;
|
1582
|
+
VALUE tmp;
|
1583
|
+
|
1584
|
+
if ( TYPE( rstr ) == T_STRING )
|
1585
|
+
tmp = rstr;
|
1586
|
+
else
|
1587
|
+
/* Convert to Ruby String. */
|
1588
|
+
tmp = rb_inspect( rstr );
|
1589
|
+
|
1590
|
+
str = RSTRING_PTR( tmp );
|
1591
|
+
|
1592
|
+
ps_out_str( ruby_ps, str );
|
1593
|
+
ps_out( ruby_ps, '\n' );
|
1594
|
+
return Qnil;
|
1595
|
+
}
|
1596
|
+
|
1597
|
+
|
1598
|
+
/**
|
1599
|
+
* Mucgly.hookbeg method. Get hookbeg.
|
1600
|
+
*
|
1601
|
+
* @param obj Not used.
|
1602
|
+
*
|
1603
|
+
* @return Hookbeg as Ruby String.
|
1604
|
+
*/
|
1605
|
+
VALUE mucgly_hookbeg( VALUE obj )
|
1606
|
+
{
|
1607
|
+
return rb_str_new_cstr( ps_current_file( ruby_ps )->hookbeg );
|
1608
|
+
}
|
1609
|
+
|
1610
|
+
|
1611
|
+
/**
|
1612
|
+
* Mucgly.hookend method. Get hookend.
|
1613
|
+
*
|
1614
|
+
* @param obj Not used.
|
1615
|
+
*
|
1616
|
+
* @return Hookend as Ruby String.
|
1617
|
+
*/
|
1618
|
+
VALUE mucgly_hookend( VALUE obj )
|
1619
|
+
{
|
1620
|
+
return rb_str_new_cstr( ps_current_file( ruby_ps )->hookend );
|
1621
|
+
}
|
1622
|
+
|
1623
|
+
|
1624
|
+
/**
|
1625
|
+
* Mucgly.hookesc method. Get hookesc.
|
1626
|
+
*
|
1627
|
+
* @param obj Not used.
|
1628
|
+
*
|
1629
|
+
* @return Hookesc as Ruby String.
|
1630
|
+
*/
|
1631
|
+
VALUE mucgly_hookesc( VALUE obj )
|
1632
|
+
{
|
1633
|
+
return rb_str_new_cstr( ps_current_file( ruby_ps )->hookesc );
|
1634
|
+
}
|
1635
|
+
|
1636
|
+
|
1637
|
+
/**
|
1638
|
+
* Mucgly.sethookbeg method. Set hookbeg.
|
1639
|
+
*
|
1640
|
+
* @param obj Not used.
|
1641
|
+
* @param rstr Hookbeg string (Ruby String).
|
1642
|
+
*
|
1643
|
+
* @return Qnil.
|
1644
|
+
*/
|
1645
|
+
VALUE mucgly_sethookbeg( VALUE obj, VALUE rstr )
|
1646
|
+
{
|
1647
|
+
char* str;
|
1648
|
+
str = RSTRING_PTR( rstr );
|
1649
|
+
sf_set_hook( ps_topfile( ruby_ps ), hook_beg, str );
|
1650
|
+
return Qnil;
|
1651
|
+
}
|
1652
|
+
|
1653
|
+
|
1654
|
+
/**
|
1655
|
+
* Mucgly.sethookend method. Set hookend.
|
1656
|
+
*
|
1657
|
+
* @param obj Not used.
|
1658
|
+
* @param rstr Hookend string (Ruby String).
|
1659
|
+
*
|
1660
|
+
* @return Qnil.
|
1661
|
+
*/
|
1662
|
+
VALUE mucgly_sethookend( VALUE obj, VALUE rstr )
|
1663
|
+
{
|
1664
|
+
char* str;
|
1665
|
+
str = RSTRING_PTR( rstr );
|
1666
|
+
sf_set_hook( ps_topfile( ruby_ps ), hook_end, str );
|
1667
|
+
return Qnil;
|
1668
|
+
}
|
1669
|
+
|
1670
|
+
|
1671
|
+
/**
|
1672
|
+
* Mucgly.sethookesc method. Set hookesc.
|
1673
|
+
*
|
1674
|
+
* @param obj Not used.
|
1675
|
+
* @param rstr Hookesc string (Ruby String).
|
1676
|
+
*
|
1677
|
+
* @return Qnil.
|
1678
|
+
*/
|
1679
|
+
VALUE mucgly_sethookesc( VALUE obj, VALUE rstr )
|
1680
|
+
{
|
1681
|
+
char* str;
|
1682
|
+
str = RSTRING_PTR( rstr );
|
1683
|
+
sf_set_hook( ps_topfile( ruby_ps ), hook_esc, str );
|
1684
|
+
return Qnil;
|
1685
|
+
}
|
1686
|
+
|
1687
|
+
|
1688
|
+
/**
|
1689
|
+
* Mucgly.ifilename method. Gets current input file name.
|
1690
|
+
*
|
1691
|
+
* @param obj Not used.
|
1692
|
+
*
|
1693
|
+
* @return Input file name (Ruby String).
|
1694
|
+
*/
|
1695
|
+
VALUE mucgly_ifilename( VALUE obj )
|
1696
|
+
{
|
1697
|
+
return rb_str_new_cstr( ps_topfile(ruby_ps)->filename );
|
1698
|
+
}
|
1699
|
+
|
1700
|
+
|
1701
|
+
/**
|
1702
|
+
* Mucgly.ilinenumber method. Gets current input file line.
|
1703
|
+
*
|
1704
|
+
* @param obj Not used.
|
1705
|
+
*
|
1706
|
+
* @return Input file line number (Ruby Fixnum).
|
1707
|
+
*/
|
1708
|
+
VALUE mucgly_ilinenumber( VALUE obj )
|
1709
|
+
{
|
1710
|
+
return INT2FIX( ps_topfile(ruby_ps)->lineno+1 );
|
1711
|
+
}
|
1712
|
+
|
1713
|
+
|
1714
|
+
/**
|
1715
|
+
* Mucgly.ofilename method. Gets current output file name.
|
1716
|
+
*
|
1717
|
+
* @param obj Not used.
|
1718
|
+
*
|
1719
|
+
* @return Input file name (Ruby String).
|
1720
|
+
*/
|
1721
|
+
VALUE mucgly_ofilename( VALUE obj )
|
1722
|
+
{
|
1723
|
+
outfile_t* of;
|
1724
|
+
of = ruby_ps->output->data;
|
1725
|
+
return rb_str_new_cstr( of->filename );
|
1726
|
+
}
|
1727
|
+
|
1728
|
+
|
1729
|
+
/**
|
1730
|
+
* Mucgly.olinenumber method. Gets current output file line.
|
1731
|
+
*
|
1732
|
+
* @param obj Not used.
|
1733
|
+
*
|
1734
|
+
* @return Output file line number (Ruby Fixnum).
|
1735
|
+
*/
|
1736
|
+
VALUE mucgly_olinenumber( VALUE obj )
|
1737
|
+
{
|
1738
|
+
outfile_t* of;
|
1739
|
+
of = ruby_ps->output->data;
|
1740
|
+
return INT2FIX( (of->lineno+1) );
|
1741
|
+
}
|
1742
|
+
|
1743
|
+
|
1744
|
+
/**
|
1745
|
+
* Mucgly.pushinput method. Push new input stream.
|
1746
|
+
*
|
1747
|
+
* @param obj Not used.
|
1748
|
+
* @param rstr Input file name (Ruby String).
|
1749
|
+
*
|
1750
|
+
* @return Qnil.
|
1751
|
+
*/
|
1752
|
+
VALUE mucgly_pushinput( VALUE obj, VALUE rstr )
|
1753
|
+
{
|
1754
|
+
char* str;
|
1755
|
+
|
1756
|
+
str = RSTRING_PTR( rstr );
|
1757
|
+
fs_push_file( ruby_ps->fs, str );
|
1758
|
+
return Qnil;
|
1759
|
+
}
|
1760
|
+
|
1761
|
+
|
1762
|
+
/**
|
1763
|
+
* Mucgly.closeinput method. Pop input stream and close file stream.
|
1764
|
+
*
|
1765
|
+
* @param obj Not used.
|
1766
|
+
*
|
1767
|
+
* @return Qnil.
|
1768
|
+
*/
|
1769
|
+
VALUE mucgly_closeinput( VALUE obj )
|
1770
|
+
{
|
1771
|
+
fs_pop_file( ruby_ps->fs );
|
1772
|
+
return Qnil;
|
1773
|
+
}
|
1774
|
+
|
1775
|
+
|
1776
|
+
/**
|
1777
|
+
* Mucgly.pushoutput method. Push new output stream.
|
1778
|
+
*
|
1779
|
+
* @param obj Not used.
|
1780
|
+
* @param rstr Output file name (Ruby String).
|
1781
|
+
*
|
1782
|
+
* @return Qnil.
|
1783
|
+
*/
|
1784
|
+
VALUE mucgly_pushoutput( VALUE obj, VALUE rstr )
|
1785
|
+
{
|
1786
|
+
char* str;
|
1787
|
+
|
1788
|
+
str = RSTRING_PTR( rstr );
|
1789
|
+
ps_push_file( ruby_ps, str );
|
1790
|
+
return Qnil;
|
1791
|
+
}
|
1792
|
+
|
1793
|
+
|
1794
|
+
/**
|
1795
|
+
* Mucgly.closeoutput method. Pop output stream and close file stream.
|
1796
|
+
*
|
1797
|
+
* @param obj Not used.
|
1798
|
+
*
|
1799
|
+
* @return Qnil.
|
1800
|
+
*/
|
1801
|
+
VALUE mucgly_closeoutput( VALUE obj )
|
1802
|
+
{
|
1803
|
+
ps_pop_file( ruby_ps );
|
1804
|
+
return Qnil;
|
1805
|
+
}
|
1806
|
+
|
1807
|
+
|
1808
|
+
#ifdef MUCGLY_LIB
|
1809
|
+
/**
|
1810
|
+
* Call mucgly C-side entry point. Convert Ruby ARGV array to C-array
|
1811
|
+
* for C-side command line arg parsing.
|
1812
|
+
*
|
1813
|
+
* @param obj Not used.
|
1814
|
+
* @param cli Ruby side ARGV.
|
1815
|
+
*
|
1816
|
+
* @return Qnil.
|
1817
|
+
*/
|
1818
|
+
VALUE mucgly_run_mucgly( VALUE obj, VALUE cli )
|
1819
|
+
{
|
1820
|
+
int argc;
|
1821
|
+
char** argv;
|
1822
|
+
|
1823
|
+
argc = RARRAY_LEN( cli );
|
1824
|
+
argv = (char**) g_new0( gpointer, argc );
|
1825
|
+
|
1826
|
+
for ( int i = 0; i < argc; i++ )
|
1827
|
+
{
|
1828
|
+
argv[ i ] = RSTRING_PTR( RARRAY_AREF(cli,i) );
|
1829
|
+
}
|
1830
|
+
|
1831
|
+
mucgly_main( argc, argv );
|
1832
|
+
|
1833
|
+
return Qnil;
|
1834
|
+
}
|
1835
|
+
#endif
|
1836
|
+
|
1837
|
+
|
1838
|
+
/**
|
1839
|
+
* Create Mucgly Ruby module and register all methods to it.
|
1840
|
+
*
|
1841
|
+
*/
|
1842
|
+
void Init_mucgly( void )
|
1843
|
+
{
|
1844
|
+
VALUE mMucgly;
|
1845
|
+
|
1846
|
+
mMucgly = rb_define_module( "Mucgly" );
|
1847
|
+
|
1848
|
+
#ifdef MUCGLY_LIB
|
1849
|
+
rb_define_module_function( mMucgly, "run_mucgly", mucgly_run_mucgly, 1 );
|
1850
|
+
#endif
|
1851
|
+
|
1852
|
+
rb_define_module_function( mMucgly, "write", mucgly_write, 1 );
|
1853
|
+
rb_define_module_function( mMucgly, "puts", mucgly_puts, 1 );
|
1854
|
+
rb_define_module_function( mMucgly, "hookbeg", mucgly_hookbeg, 0 );
|
1855
|
+
rb_define_module_function( mMucgly, "hookend", mucgly_hookend, 0 );
|
1856
|
+
rb_define_module_function( mMucgly, "hookesc", mucgly_hookesc, 0 );
|
1857
|
+
rb_define_module_function( mMucgly, "sethookbeg", mucgly_sethookbeg, 1 );
|
1858
|
+
rb_define_module_function( mMucgly, "sethookend", mucgly_sethookend, 1 );
|
1859
|
+
rb_define_module_function( mMucgly, "sethookesc", mucgly_sethookesc, 1 );
|
1860
|
+
rb_define_module_function( mMucgly, "ifilename", mucgly_ifilename, 0 );
|
1861
|
+
rb_define_module_function( mMucgly, "ilinenumber", mucgly_ilinenumber, 0 );
|
1862
|
+
rb_define_module_function( mMucgly, "ofilename", mucgly_ofilename, 0 );
|
1863
|
+
rb_define_module_function( mMucgly, "olinenumber", mucgly_olinenumber, 0 );
|
1864
|
+
rb_define_module_function( mMucgly, "pushinput", mucgly_pushinput, 1 );
|
1865
|
+
rb_define_module_function( mMucgly, "closeinput", mucgly_closeinput, 0 );
|
1866
|
+
rb_define_module_function( mMucgly, "pushoutput", mucgly_pushoutput, 1 );
|
1867
|
+
rb_define_module_function( mMucgly, "closeoutput", mucgly_closeoutput, 0 );
|
1868
|
+
|
1869
|
+
}
|
1870
|
+
|
1871
|
+
|
1872
|
+
/**
|
1873
|
+
* Generate outfile name from infile by removing the outfile_tag from
|
1874
|
+
* infile name.
|
1875
|
+
*
|
1876
|
+
* @param infile Input file name.
|
1877
|
+
* @param outfile_tag Tag to remove from infile to get outfile.
|
1878
|
+
*
|
1879
|
+
* @return Outfile name.
|
1880
|
+
*/
|
1881
|
+
gchar* infile_to_outfile( gchar* infile, gchar* outfile_tag )
|
1882
|
+
{
|
1883
|
+
gchar* tag = NULL;
|
1884
|
+
gchar* ret;
|
1885
|
+
|
1886
|
+
if ( !outfile_tag )
|
1887
|
+
return NULL;
|
1888
|
+
|
1889
|
+
if ( g_strrstr( infile, outfile_tag ) )
|
1890
|
+
{
|
1891
|
+
ret = g_strdup( infile );
|
1892
|
+
tag = g_strrstr( ret, outfile_tag );
|
1893
|
+
g_stpcpy( tag, tag + strlen( outfile_tag ) );
|
1894
|
+
return ret;
|
1895
|
+
}
|
1896
|
+
else
|
1897
|
+
{
|
1898
|
+
return NULL;
|
1899
|
+
}
|
1900
|
+
}
|
1901
|
+
|
1902
|
+
|
1903
|
+
|
1904
|
+
/* ------------------------------------------------------------
|
1905
|
+
* Main program:
|
1906
|
+
* ------------------------------------------------------------ */
|
1907
|
+
|
1908
|
+
void mucgly_main( int argc, char** argv )
|
1909
|
+
{
|
1910
|
+
|
1911
|
+
/* Setup default hooks etc. */
|
1912
|
+
stack_default = sf_new( NULL, NULL );
|
1913
|
+
|
1914
|
+
/* ------------------------------------------------------------
|
1915
|
+
* Setup Ruby features:
|
1916
|
+
* ------------------------------------------------------------ */
|
1917
|
+
|
1918
|
+
#ifndef MUCGLY_LIB
|
1919
|
+
ruby_init();
|
1920
|
+
Init_mucgly();
|
1921
|
+
#endif
|
1922
|
+
|
1923
|
+
|
1924
|
+
/* ------------------------------------------------------------
|
1925
|
+
* Command line parsing:
|
1926
|
+
* ------------------------------------------------------------ */
|
1927
|
+
|
1928
|
+
gchar** opt_files = NULL;
|
1929
|
+
gchar** opt_configs = NULL;
|
1930
|
+
gchar* opt_output = NULL;
|
1931
|
+
gchar* opt_genout = NULL;
|
1932
|
+
gchar** opt_cli_cmds = NULL;
|
1933
|
+
gchar* opt_beg_sep = NULL;
|
1934
|
+
gchar* opt_end_sep = NULL;
|
1935
|
+
gchar* opt_esc_sep = NULL;
|
1936
|
+
gchar* opt_all_sep = NULL;
|
1937
|
+
gboolean opt_module = FALSE;
|
1938
|
+
gboolean opt_interact = FALSE;
|
1939
|
+
gboolean opt_no_init = FALSE;
|
1940
|
+
|
1941
|
+
|
1942
|
+
GOptionEntry opts_table[] =
|
1943
|
+
{
|
1944
|
+
{ "files", 'f', 0, G_OPTION_ARG_FILENAME_ARRAY,
|
1945
|
+
&opt_files, "Input file(s) (default: stdin).", NULL },
|
1946
|
+
{ "configs", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY,
|
1947
|
+
&opt_configs, "Ruby configuration file(s).", NULL },
|
1948
|
+
{ "output", 'o', 0, G_OPTION_ARG_FILENAME,
|
1949
|
+
&opt_output, "Output file(s) (default: stdout).", NULL },
|
1950
|
+
{ "genout", 'g', 0, G_OPTION_ARG_STRING,
|
1951
|
+
&opt_genout, "Generate output file names (remove arg from input filename).", NULL },
|
1952
|
+
{ "cli_cmds", 'l', 0, G_OPTION_ARG_STRING_ARRAY,
|
1953
|
+
&opt_cli_cmds, "Command line configuration(s).", NULL },
|
1954
|
+
{ "beg_sep", 'b', 0, G_OPTION_ARG_STRING,
|
1955
|
+
&opt_beg_sep, "Set hookbeg separator (default: '-<').", NULL },
|
1956
|
+
{ "end_sep", 'e', 0, G_OPTION_ARG_STRING,
|
1957
|
+
&opt_end_sep, "Set hookend separator (default: '>-').", NULL },
|
1958
|
+
{ "esc_sep", 's', 0, G_OPTION_ARG_STRING,
|
1959
|
+
&opt_esc_sep, "Set hookesc separator (default: '\\').", NULL },
|
1960
|
+
{ "all_sep", 'a', 0, G_OPTION_ARG_STRING,
|
1961
|
+
&opt_all_sep, "Set all separators.", NULL },
|
1962
|
+
{ "module", 'm', 0, G_OPTION_ARG_NONE,
|
1963
|
+
&opt_module, "Include Mucgly module at start.", NULL },
|
1964
|
+
{ "interact", 'i', 0, G_OPTION_ARG_NONE,
|
1965
|
+
&opt_interact, "Flush output for proper interaction.", NULL },
|
1966
|
+
{ "no_init", 'n', 0, G_OPTION_ARG_NONE,
|
1967
|
+
&opt_no_init, "No init file (MUCGLY env var).", NULL },
|
1968
|
+
{ NULL }
|
1969
|
+
};
|
1970
|
+
|
1971
|
+
|
1972
|
+
GError* opts_error = NULL;
|
1973
|
+
GOptionContext* opts;
|
1974
|
+
|
1975
|
+
opts = g_option_context_new( "- Inline Macro processor" );
|
1976
|
+
g_option_context_add_main_entries( opts, opts_table, NULL );
|
1977
|
+
if ( !g_option_context_parse( opts, &argc, &argv, &opts_error ) )
|
1978
|
+
{
|
1979
|
+
g_print( "mucgly: option parsing failed: %s\n", opts_error->message );
|
1980
|
+
exit( EXIT_FAILURE );
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
|
1984
|
+
/* Create initial input state. */
|
1985
|
+
pstate_t* ps = ps_new( opt_output );
|
1986
|
+
ruby_ps = ps;
|
1987
|
+
|
1988
|
+
|
1989
|
+
/* Read init file if existing. */
|
1990
|
+
if ( !opt_no_init )
|
1991
|
+
{
|
1992
|
+
gchar* init;
|
1993
|
+
if ( ( init = (gchar*) g_environ_getenv( g_get_environ(), "MUCGLY" ) ) )
|
1994
|
+
{
|
1995
|
+
if ( !g_file_test( init, G_FILE_TEST_EXISTS ) )
|
1996
|
+
mucgly_warn( ps_current_file(ps),
|
1997
|
+
"MUCGLY env set, but the file \"%s\" does not exist!",
|
1998
|
+
init );
|
1999
|
+
else
|
2000
|
+
{
|
2001
|
+
ps_load_ruby_file( ps, init );
|
2002
|
+
}
|
2003
|
+
}
|
2004
|
+
}
|
2005
|
+
|
2006
|
+
|
2007
|
+
/* Include Mucgly module to flatten the namespace. */
|
2008
|
+
if ( opt_module )
|
2009
|
+
rb_eval_string( "include Mucgly" );
|
2010
|
+
|
2011
|
+
|
2012
|
+
/* Read Ruby configuration files. */
|
2013
|
+
if ( opt_configs )
|
2014
|
+
{
|
2015
|
+
while ( *opt_configs )
|
2016
|
+
{
|
2017
|
+
ps_load_ruby_file( ps, *opt_configs );
|
2018
|
+
opt_configs++;
|
2019
|
+
}
|
2020
|
+
}
|
2021
|
+
|
2022
|
+
|
2023
|
+
/* Process Ruby command line options. */
|
2024
|
+
if ( opt_cli_cmds )
|
2025
|
+
{
|
2026
|
+
while ( *opt_cli_cmds )
|
2027
|
+
{
|
2028
|
+
ps_eval_ruby_str( ps, *opt_cli_cmds, FALSE, "cli" );
|
2029
|
+
opt_cli_cmds++;
|
2030
|
+
}
|
2031
|
+
}
|
2032
|
+
|
2033
|
+
|
2034
|
+
/* Set hooks. */
|
2035
|
+
|
2036
|
+
if ( opt_beg_sep )
|
2037
|
+
sf_set_hook( stack_default, hook_beg, opt_beg_sep );
|
2038
|
+
|
2039
|
+
if ( opt_end_sep )
|
2040
|
+
sf_set_hook( stack_default, hook_end, opt_end_sep );
|
2041
|
+
|
2042
|
+
if ( opt_esc_sep )
|
2043
|
+
sf_set_hook( stack_default, hook_esc, opt_esc_sep );
|
2044
|
+
|
2045
|
+
if ( opt_all_sep )
|
2046
|
+
{
|
2047
|
+
sf_set_hook( stack_default, hook_beg, opt_all_sep );
|
2048
|
+
sf_set_hook( stack_default, hook_end, opt_all_sep );
|
2049
|
+
sf_set_hook( stack_default, hook_esc, opt_all_sep );
|
2050
|
+
}
|
2051
|
+
|
2052
|
+
|
2053
|
+
if ( opt_interact )
|
2054
|
+
ps->flush = TRUE;
|
2055
|
+
|
2056
|
+
/* Process input files. */
|
2057
|
+
if ( opt_files )
|
2058
|
+
{
|
2059
|
+
/* Named files. */
|
2060
|
+
while ( *opt_files )
|
2061
|
+
{
|
2062
|
+
ps_process_file( ps,
|
2063
|
+
*opt_files,
|
2064
|
+
infile_to_outfile(
|
2065
|
+
*opt_files,
|
2066
|
+
opt_genout )
|
2067
|
+
);
|
2068
|
+
opt_files++;
|
2069
|
+
}
|
2070
|
+
}
|
2071
|
+
else
|
2072
|
+
{
|
2073
|
+
/* Process STDIN. */
|
2074
|
+
ps_process_file( ps, NULL, NULL );
|
2075
|
+
}
|
2076
|
+
|
2077
|
+
ps_rem( ps );
|
2078
|
+
|
2079
|
+
}
|
2080
|
+
|
2081
|
+
|
2082
|
+
#ifndef MUCGLY_LIB
|
2083
|
+
/**
|
2084
|
+
* C-top entry point.
|
2085
|
+
*
|
2086
|
+
* @param argc Cli arg count.
|
2087
|
+
* @param argv Cli arg values.
|
2088
|
+
*
|
2089
|
+
* @return Not used.
|
2090
|
+
*/
|
2091
|
+
int main( int argc, char** argv )
|
2092
|
+
{
|
2093
|
+
mucgly_main( argc, argv );
|
2094
|
+
}
|
2095
|
+
#endif
|