4
#elif defined( _HPUX_SOURCE )
14
#include "templates.h"
16
/* ------------------------------------------------------------------------- */
17
/* macros and constants */
18
/* ------------------------------------------------------------------------- */
20
/* we do this as a define since the replace tag and cyclical replace tag
21
* use the same header... in other words, the replace tag inherits from
22
* the standard tag, and the cyclical replace tag inherits from the replace
25
#define STANDARD_REPLACE_TAG_HDR \
29
#define DEFAULT_TAG_START "<!--%"
30
#define DEFAULT_TAG_END "%-->"
31
#define DEFAULT_DELIMITER "="
33
#define NEW( item_name ) (item_name*)malloc( sizeof( item_name ) )
34
#define NEWTAG( name, type ) (type*)ae_tag_new( name, sizeof( type ) )
35
#define DELETE( x ) free( x )
37
#define DECL_CAST( var, parm, type ) type* var = (type*)parm
38
#define MGR_CAST( var, parm ) DECL_CAST( var, parm, t_ae_mgr )
39
#define GENERIC_TAG( var, parm ) DECL_CAST( var, parm, t_ae_generic_tag )
41
#define COMP_TYPE_EQ ( 0 )
42
#define COMP_TYPE_NE ( 1 )
43
#define COMP_TYPE_LT ( 2 )
44
#define COMP_TYPE_LE ( 3 )
45
#define COMP_TYPE_GT ( 4 )
46
#define COMP_TYPE_GE ( 5 )
48
/* ------------------------------------------------------------------------- */
49
/* type implementations */
50
/* ------------------------------------------------------------------------- */
52
typedef t_ae_tag (*t_standard_tag_def)();
56
} t_ae_generic_stream;
71
STANDARD_REPLACE_TAG_HDR;
75
STANDARD_REPLACE_TAG_HDR;
78
} t_ae_cyclical_replace_tag;
90
} t_ae_comparison_tag;
92
typedef struct __ae_tag_list t_ae_tag_list;
93
struct __ae_tag_list {
96
t_ae_generic_tag* tag;
100
t_ae_tag_list* m_taglist_head;
101
t_ae_tag_list* m_taglist_tail;
104
char* m_tag_delimiter;
105
t_ae_preproc_fn preproc;
110
typedef struct __ae_cookie t_ae_cookie;
121
t_ae_cookie* cookies;
123
} t_ae_html_proc_data;
125
/* ------------------------------------------------------------------------- */
126
/* static function definitions */
127
/* ------------------------------------------------------------------------- */
129
static t_ae_file_stream* static_ae_file_stream_new( void );
130
static t_ae_buffer_stream* static_ae_buffer_stream_new( void );
132
static int static_ae_file_stream_get_length( t_ae_stream stream );
133
static int static_ae_file_stream_read( t_ae_stream stream, char* buffer, int length );
134
static int static_ae_file_stream_close( t_ae_stream stream );
136
static int static_ae_buffer_stream_get_length( t_ae_stream stream );
137
static int static_ae_buffer_stream_read( t_ae_stream stream, char* buffer, int length );
138
static int static_ae_buffer_stream_close( t_ae_stream stream );
140
static t_ae_tag static_ae_comparison_tag( CONST char* name,
143
static int static_ae_replace_tag_apply( t_ae_tag tag,
145
t_ae_template_mgr mgr,
147
static int static_ae_replace_tag_process( t_ae_tag tag,
149
t_ae_template_mgr mgr,
151
static int static_ae_replace_tag_cleanup( t_ae_tag tag );
153
static int static_ae_cyclical_replace_tag_process( t_ae_tag tag,
155
t_ae_template_mgr mgr,
157
static int static_ae_cyclical_replace_tag_cleanup( t_ae_tag tag );
158
static int static_ae_shared_fn_tag_cleanup( t_ae_tag tag );
160
static int static_ae_typed_tag_apply( t_ae_tag tag,
162
t_ae_template_mgr mgr,
165
static int static_ae_if_tag_process( t_ae_tag tag,
167
t_ae_template_mgr mgr,
169
static int static_ae_if_not_tag_process( t_ae_tag tag,
171
t_ae_template_mgr mgr,
173
static int static_ae_include_tag_process( t_ae_tag tag,
175
t_ae_template_mgr mgr,
177
static int static_ae_repeat_tag_process( t_ae_tag tag,
179
t_ae_template_mgr mgr,
181
static int static_ae_env_tag_process( t_ae_tag tag,
183
t_ae_template_mgr mgr,
185
static int static_ae_exec_tag_process( t_ae_tag tag,
187
t_ae_template_mgr mgr,
189
static int static_ae_comparison_tag_process( t_ae_tag tag,
191
t_ae_template_mgr mgr,
193
static int static_ae_include_tag_named_process( t_ae_tag tag,
195
t_ae_template_mgr mgr,
198
static int static_ae_shared_fn_apply( t_ae_tag tag,
200
t_ae_template_mgr mgr,
202
static int static_ae_shared_fn_process( t_ae_tag tag,
204
t_ae_template_mgr mgr,
207
static int static_html_preproc_fn( t_ae_template_mgr mgr, FILE* output );
209
static char* static_get_non_value( t_ae_tag tag );
210
static char* static_get_replace_tag_value( t_ae_tag tag );
212
/* ------------------------------------------------------------------------- */
214
/* ------------------------------------------------------------------------- */
216
static t_standard_tag_def static_standard_tags[] = {
232
/* ------------------------------------------------------------------------- */
233
/* stream function implementations */
234
/* ------------------------------------------------------------------------- */
236
t_ae_stream ae_stream_open_file( CONST char* file_name ) {
237
t_ae_file_stream* stream;
239
/* create a new file stream object and open the requested file */
240
stream = static_ae_file_stream_new();
241
stream->fptr = fopen( file_name, "r" );
242
if( stream->fptr == NULL ) {
243
stream->close( stream );
247
return (t_ae_stream)stream;
250
t_ae_stream ae_stream_wrap_file( FILE* fptr ) {
251
t_ae_file_stream* stream;
253
/* create a new file stream object and set the file pointer to be the one
256
stream = static_ae_file_stream_new();
260
return (t_ae_stream)stream;
263
t_ae_stream ae_stream_open_buffer( CONST char* buffer ) {
264
t_ae_buffer_stream* stream;
266
stream = static_ae_buffer_stream_new();
267
stream->buffer = strdup( buffer );
270
return (t_ae_stream)stream;
273
/* the following functions use the function pointers in the stream
274
* structure to implement "virtual" methods of the stream object. */
276
int ae_stream_get_length( t_ae_stream stream ) {
277
DECL_CAST( str, stream, t_ae_generic_stream );
278
return str->get_length( stream );
281
int ae_stream_read( t_ae_stream stream, char* buffer, int length ) {
282
DECL_CAST( str, stream, t_ae_generic_stream );
283
return str->read( stream, buffer, length );
286
int ae_stream_close( t_ae_stream stream ) {
287
DECL_CAST( str, stream, t_ae_generic_stream );
288
str->close( stream );
293
/* ------------------------------------------------------------------------- */
294
/* tag function implementations */
295
/* ------------------------------------------------------------------------- */
297
t_ae_tag ae_tag_new( CONST char* name, int size ) {
298
t_ae_generic_tag* tag;
300
/* create a new tag by allocating space for it, setting it's name and
301
* delimiter, and setting default values for it's methods. */
303
tag = (t_ae_generic_tag*)malloc( size );
304
tag->m_tag = strdup( name );
305
tag->m_delim = strdup( DEFAULT_DELIMITER );
309
tag->get_value = static_get_non_value;
311
/* setting the type to TAG_TYPE_NO_VALUE means that the tag is untyped --
312
* if you call it's get-value function, you will always get NULL back. */
314
tag->type = TAG_TYPE_NO_VALUE;
316
return (t_ae_tag)tag;
319
void ae_tag_destroy( t_ae_tag tag ) {
320
GENERIC_TAG( tag_data, tag );
322
/* if the tag has a cleanup function defined, call it */
324
if( tag_data->cleanup ) {
325
tag_data->cleanup( tag );
328
/* free the memory for the tag */
330
free( tag_data->m_tag );
331
free( tag_data->m_delim );
335
char* ae_get_tag_name( t_ae_tag tag ) {
336
GENERIC_TAG( tag_data, tag );
337
return tag_data->m_tag;
340
char* ae_get_tag_delim( t_ae_tag tag ) {
341
GENERIC_TAG( tag_data, tag );
342
return tag_data->m_delim;
345
char* ae_get_tag_value( t_ae_tag tag ) {
346
GENERIC_TAG( tag_data, tag );
347
return tag_data->get_value( tag );
351
t_ae_tag ae_replace_tag( CONST char* name, CONST char* data ) {
352
t_ae_replace_tag* tag;
354
/* a replace tag simply replaces itself with the specified data.
355
* If the data is NULL, the tag is replaced with the empty string. */
357
tag = NEWTAG( name, t_ae_replace_tag );
359
/* tag->m_data = strdup( data );*/
360
tag->m_data = (char*)malloc( strlen( data ) + 1 );
361
strcpy( tag->m_data, data );
365
tag->apply = static_ae_replace_tag_apply;
366
tag->process = static_ae_replace_tag_process;
367
tag->cleanup = static_ae_replace_tag_cleanup;
368
tag->get_value = static_get_replace_tag_value;
369
tag->type = TAG_TYPE_VALUE;
371
return (t_ae_tag)tag;
374
t_ae_tag ae_cyclical_replace_tag( CONST char* name, CONST char* data, CONST char* delim ) {
375
t_ae_cyclical_replace_tag* tag;
377
/* a cyclical replace tag replaces itself with the next value in the associated delimited
378
* list of values. Each time the cyclical tag's "process" function is called, it's
379
* m_next pointer is incremented so that on each subsequent call, the next value in the
380
* list is obtained. */
382
tag = NEWTAG( name, t_ae_cyclical_replace_tag );
383
tag->m_data = strdup( data );
384
tag->m_rpt_delim = strdup( delim );
385
tag->m_next = tag->m_data;
386
tag->apply = static_ae_replace_tag_apply;
387
tag->process = static_ae_cyclical_replace_tag_process;
388
tag->cleanup = static_ae_cyclical_replace_tag_cleanup;
389
tag->get_value = static_get_replace_tag_value;
390
tag->type = TAG_TYPE_VALUE;
392
return (t_ae_tag)tag;
395
t_ae_tag ae_typed_tag( CONST char* type, t_ae_tag_fn process, int size ) {
396
t_ae_generic_tag* tag;
398
tag = (t_ae_generic_tag*)ae_tag_new( type, size );
399
tag->apply = static_ae_typed_tag_apply;
400
tag->process = process;
402
return (t_ae_tag)tag;
405
t_ae_tag ae_if_tag( void ) {
406
return ae_typed_tag( "IF", static_ae_if_tag_process, sizeof( t_ae_generic_tag ) );
409
t_ae_tag ae_if_not_tag( void ) {
410
return ae_typed_tag( "IF_NOT", static_ae_if_not_tag_process, sizeof( t_ae_generic_tag ) );
413
t_ae_tag ae_if_eq_tag( void ) {
414
return static_ae_comparison_tag( "IF_EQ", COMP_TYPE_EQ );
417
t_ae_tag ae_if_not_eq_tag( void ) {
418
return static_ae_comparison_tag( "IF_NOT_EQ", COMP_TYPE_NE );
421
t_ae_tag ae_if_lt_tag( void ) {
422
return static_ae_comparison_tag( "IF_LT", COMP_TYPE_LT );
425
t_ae_tag ae_if_le_tag( void ) {
426
return static_ae_comparison_tag( "IF_LE", COMP_TYPE_LE );
429
t_ae_tag ae_if_gt_tag( void ) {
430
return static_ae_comparison_tag( "IF_GT", COMP_TYPE_GT );
433
t_ae_tag ae_if_ge_tag( void ) {
434
return static_ae_comparison_tag( "IF_GE", COMP_TYPE_GE );
437
t_ae_tag ae_include_tag( void ) {
438
return ae_typed_tag( "INCLUDE", static_ae_include_tag_process, sizeof( t_ae_generic_tag ) );
441
t_ae_tag ae_repeat_tag( void ) {
442
return ae_typed_tag( "REPEAT2", static_ae_repeat_tag_process, sizeof( t_ae_generic_tag ) );
445
t_ae_tag ae_env_tag( void ) {
446
return ae_typed_tag( "ENV", static_ae_env_tag_process, sizeof( t_ae_generic_tag ) );
449
t_ae_tag ae_exec_tag( void ) {
450
return ae_typed_tag( "EXEC", static_ae_exec_tag_process, sizeof( t_ae_generic_tag ) );
453
t_ae_tag ae_shared_fn_tag( CONST char* name, CONST char* lib, CONST char* func, void* cookie ) {
454
t_ae_shared_fn_tag* tag;
456
tag = NEWTAG( name, t_ae_shared_fn_tag );
457
tag->apply = static_ae_shared_fn_apply;
458
tag->process = static_ae_shared_fn_process;
459
tag->cleanup = static_ae_shared_fn_tag_cleanup;
460
tag->m_lib = strdup( lib );
461
tag->m_func = strdup( func );
462
tag->m_cookie = cookie;
464
return (t_ae_tag)tag;
467
t_ae_tag ae_shared_fn_tag_named( CONST char* name, CONST char* lib,
468
CONST char* func, void* cookie )
470
t_ae_shared_fn_tag* tag;
472
tag = (t_ae_shared_fn_tag*)ae_shared_fn_tag( name, lib, func, cookie );
473
tag->apply = static_ae_replace_tag_apply;
475
return (t_ae_tag)tag;
478
t_ae_tag ae_include_tag_named( CONST char* name, CONST char* file ) {
479
t_ae_replace_tag* tag;
481
tag = (t_ae_replace_tag*)ae_replace_tag( name, file );
482
tag->process = static_ae_include_tag_named_process;
484
return (t_ae_tag)tag;
487
/* ------------------------------------------------------------------------- */
488
/* template function implementations */
489
/* ------------------------------------------------------------------------- */
491
t_ae_template_mgr ae_template_mgr_new( void ) {
495
mgr_data = NEW( t_ae_mgr );
496
mgr_data->m_taglist_head = NULL;
497
mgr_data->m_taglist_tail = NULL;
498
mgr_data->m_tag_start = strdup( DEFAULT_TAG_START );
499
mgr_data->m_tag_end = strdup( DEFAULT_TAG_END );
500
mgr_data->m_tag_delimiter = strdup( DEFAULT_DELIMITER );
501
mgr_data->preproc = NULL;
502
mgr_data->cookie = NULL;
503
mgr_data->recursive_depth = 0;
505
/* add the standard tag types, defined in the static_standard_tags array */
506
for( i = 0; static_standard_tags[i] != NULL; i++ ) {
507
ae_add_tag_ex( (t_ae_template_mgr)mgr_data, static_standard_tags[i]() );
510
return (t_ae_template_mgr)mgr_data;
513
void ae_template_mgr_done( t_ae_template_mgr mgr ) {
514
MGR_CAST( mgr_data, mgr );
518
free( mgr_data->m_tag_start );
519
free( mgr_data->m_tag_end );
520
free( mgr_data->m_tag_delimiter );
522
/* destroy the tags associated with this manager */
523
curr = mgr_data->m_taglist_head;
524
while( curr != NULL ) {
525
ae_tag_destroy( (t_ae_tag)curr->tag );
531
mgr_data->m_taglist_head = NULL;
532
mgr_data->m_taglist_tail = NULL;
537
void ae_add_tag( t_ae_template_mgr mgr, CONST char* name, CONST char* value ) {
538
/* add the name/value pair as a replace tag */
539
ae_add_tag_ex( mgr, ae_replace_tag( name, value ) );
542
void ae_add_tag_i( t_ae_template_mgr mgr, CONST char* name, int value ) {
545
/* add the name/value pair as a replace tag */
546
snprintf( buffer, sizeof( buffer ), "%d", value );
547
ae_add_tag_ex( mgr, ae_replace_tag( name, buffer ) );
550
void ae_add_tags( t_ae_template_mgr mgr, char** names, char** values ) {
553
/* for each value in the given names/values arrays, add a tag. If the
554
* tag starts with a '-', then add the value as if it were a t_ae_tag
555
* pointer, otherwise add it as a generic replace tag */
557
if( names == 0 ) return;
558
for( i = 0; names[i] && *names[i]; i++ ) {
559
if( *names[i] == '-' ) {
560
ae_add_tag_ex( mgr, (t_ae_tag)values[i] );
562
ae_add_tag( mgr, names[ i ], values[ i ] );
567
void ae_add_tag_ex( t_ae_template_mgr mgr, t_ae_tag tag ) {
568
MGR_CAST( mgr_data, mgr );
569
GENERIC_TAG( tag_data, tag );
573
/* add the given tag to the manager's linked list of tags. The
574
* most recently added tag is added at the end of the list, and
575
* the m_taglist_head and m_taglist_tail variables keep track of
576
* (respectively) the head and tail of the list */
578
if( tag == NULL ) return;
579
ae_remove_tag( mgr, ae_get_tag_name( tag ) );
581
free( tag_data->m_delim );
582
tag_data->m_delim = strdup( mgr_data->m_tag_delimiter );
584
c = mgr_data->m_taglist_tail;
586
item = NEW( t_ae_tag_list );
587
item->next = item->prev = NULL;
588
item->tag = tag_data;
589
mgr_data->m_taglist_tail = mgr_data->m_taglist_head = item;
592
if( strcmp( c->tag->m_tag, tag_data->m_tag ) == 0 ) {
593
ae_tag_destroy( c->tag );
600
item = NEW( t_ae_tag_list );
602
item->prev = mgr_data->m_taglist_tail;
603
item->prev->next = item;
604
item->tag = tag_data;
605
mgr_data->m_taglist_tail = item;
610
void ae_remove_tag( t_ae_template_mgr mgr, CONST char* name ) {
611
MGR_CAST( mgr_data, mgr );
614
/* remove the first tag found with the given name. The tag
615
* will be destroyed. */
617
item = mgr_data->m_taglist_head;
618
while( item != NULL ) {
619
if( strcmp( item->tag->m_tag, name ) == 0 ) {
620
if( item->prev != NULL ) {
621
item->prev->next = item->next;
623
if( item->next != NULL ) {
624
item->next->prev = item->prev;
626
if( item == mgr_data->m_taglist_tail ) {
627
mgr_data->m_taglist_tail = item->prev;
629
if( item == mgr_data->m_taglist_head ) {
630
mgr_data->m_taglist_head = item->next;
632
ae_tag_destroy( item->tag );
640
void ae_remove_tag_ex( t_ae_template_mgr mgr, t_ae_tag tag ) {
641
/* remove the first tag answering to the same name as the given tag */
642
ae_remove_tag( mgr, ((t_ae_generic_tag*)tag)->m_tag );
645
int ae_tag_count( t_ae_template_mgr mgr ) {
646
MGR_CAST( mgr_data, mgr );
650
for( item = mgr_data->m_taglist_head; item != NULL; item = item->next ) {
657
void ae_set_start_end_delim( t_ae_template_mgr mgr,
661
MGR_CAST( mgr_data, mgr );
663
free( mgr_data->m_tag_start );
664
free( mgr_data->m_tag_end );
666
mgr_data->m_tag_start = strdup( start );
667
mgr_data->m_tag_end = strdup( end );
670
t_ae_tag ae_get_tag( t_ae_template_mgr mgr, CONST char* name ) {
671
MGR_CAST( mgr_data, mgr );
674
/* return the first tag answering to the given name */
675
item = mgr_data->m_taglist_head;
676
while( item != NULL ) {
677
if( strcmp( item->tag->m_tag, name ) == 0 ) {
678
return (t_ae_tag)item->tag;
686
char* ae_get_value( t_ae_template_mgr mgr, CONST char* name ) {
687
MGR_CAST( mgr_data, mgr );
690
/* return the value of the first tag answering to the given name */
691
item = mgr_data->m_taglist_head;
692
while( item != NULL ) {
693
if( strcmp( item->tag->m_tag, name ) == 0 ) {
694
if( item->tag->type != TAG_TYPE_VALUE ) return NULL;
695
return item->tag->get_value( (t_ae_tag)item->tag );
703
t_ae_tag ae_get_tag_at( t_ae_template_mgr mgr, int index ) {
704
MGR_CAST( mgr_data, mgr );
707
item = mgr_data->m_taglist_head;
708
while( index > 0 && item != NULL ) {
713
if( item != NULL ) return item->tag;
718
int ae_process_template( t_ae_template_mgr mgr, CONST char* file, FILE* output ) {
722
/* open a file stream for the given file-name, and process the stream */
724
stream = ae_stream_open_file( file );
725
if( stream == NULL ) {
728
rc = ae_process_stream( mgr, stream, output );
729
ae_stream_close( stream );
734
int ae_process_buffer( t_ae_template_mgr mgr, CONST char* buffer, FILE* output ) {
738
/* open a buffer stream for the given buffer, and process the stream */
740
stream = ae_stream_open_buffer( buffer );
741
if( stream == NULL ) {
744
rc = ae_process_stream( mgr, stream, output );
745
ae_stream_close( stream );
750
int ae_process_stream( t_ae_template_mgr mgr, t_ae_stream stream, FILE* output ) {
751
MGR_CAST( mgr_data, mgr );
762
int original_fd = -1;
764
/* if a preprocessing function has been specified, use it */
765
if( mgr_data->recursive_depth < 1 && mgr_data->preproc != NULL ) {
766
original_fd = ae_redirect_to( output, STDOUT_FILENO );
767
mgr_data->preproc( mgr, output );
770
/* increment the recursive depth */
771
mgr_data->recursive_depth++;
773
/* precompute the length of the start and end delimiters */
774
start_delim_len = strlen( mgr_data->m_tag_start );
775
end_delim_len = strlen( mgr_data->m_tag_end );
777
/* read the entire stream into a buffer */
778
size = ae_stream_get_length( stream );
779
data = (char*)malloc( size+1 );
780
ae_stream_read( stream, data, size+1 );
783
/* search through the text of the stream, replacing tags as they are encountered */
785
start = strstr( text, mgr_data->m_tag_start );
786
while( start != NULL ) {
788
fputs( text, output );
790
/* find the next end-token */
791
end = strstr( start + start_delim_len, mgr_data->m_tag_end );
793
fputs( "[unclosed tag]", output );
798
/* skip past nested tags, by looking for start-tags that begin after the current
799
* start position, but before the next end-token. That is to say, if the tag delimiters
805
* Here, start is the first start-token found, and end is the first end-token found.
806
* Nested tags are detected because last_start exists between start and end. */
808
last_start = strstr( start + start_delim_len, mgr_data->m_tag_start );
809
while( last_start != NULL && last_start < end ) {
810
/* We've found a nested token, so we skip it by looking for the next 'last_start' tag
811
* AND the next 'end' tag, and we continue the loop if the last_start tag is before the
812
* end tag. In other words:
813
* End of Iteration #1 <% <% <% %> %> %>
815
* End of Iteration #2 <% <% <% %> %> %>
817
* Thus, by the end of iteration #2, last_start is either NULL or after E, which
818
* means that the stretch of data from S to E completely contains all tags within
819
* it, with no tags overlapping the ends of S-E. */
821
end = strstr( end + end_delim_len, mgr_data->m_tag_end );
822
if( end == NULL ) break;
823
last_start = strstr( last_start + start_delim_len, mgr_data->m_tag_start );
826
/* if end is NULL, then the tag was not closed */
828
fputs( "[unclosed tag]", output );
833
/* skip past the starting delimiter */
834
start = start + start_delim_len;
837
/* look for the first tag that can apply the given tag text. Each tag contains
838
* the logic it needs to recognize itself at the head of a chunk of text ('start'). */
839
for( item = mgr_data->m_taglist_head; item != NULL; item = item->next ) {
840
if( item->tag->apply( item->tag, start, mgr, output ) ) {
845
/* start the next loop after the end of the ending delimiter */
846
text = end + end_delim_len;
848
/* look for the next starting delimiter */
849
start = strstr( text, mgr_data->m_tag_start );
852
/* write the remaining data */
853
fputs( text, output );
856
/* decrement the recursive depth, as we are now leaving this function */
857
mgr_data->recursive_depth--;
858
if( mgr_data->recursive_depth < 1 ) {
859
ae_restore_file( original_fd, stdout );
865
void ae_set_preprocessor_func( t_ae_template_mgr mgr, t_ae_preproc_fn func ) {
866
MGR_CAST( mgr_data, mgr );
867
mgr_data->preproc = func;
870
void ae_set_mgr_cookie( t_ae_template_mgr mgr, void* cookie ) {
871
MGR_CAST( mgr_data, mgr );
872
mgr_data->cookie = cookie;
875
void* ae_get_mgr_cookie( t_ae_template_mgr mgr ) {
876
MGR_CAST( mgr_data, mgr );
877
return mgr_data->cookie;
881
/* ------------------------------------------------------------------------- */
882
/* ToHTML Replacement Functions */
883
/* ------------------------------------------------------------------------- */
885
int ToHTML2( CONST char* tem_file, char** tokens, char** values, int mode ) {
886
return ae_done_html( ae_init_html( tokens, values, mode ), tem_file );
890
t_ae_template_mgr ae_init_html( char** tokens, char** values, int mode ) {
891
t_ae_template_mgr mgr;
892
t_ae_html_proc_data* data;
894
/* set up the HTML proc data structure, with fields' values depending on the value of mode */
895
data = NEW( t_ae_html_proc_data );
896
data->headers = ( mode & HTML_HEADER );
897
data->no_cache = ( mode & HTML_NO_CACHE );
898
data->cookies = NULL;
900
/* default the output to stdout, but see ae_set_html_output */
901
data->output = stdout;
903
/* create the template manager */
904
mgr = ae_template_mgr_new();
906
/* set the HTML proc data structure in the template manager */
907
ae_set_mgr_cookie( mgr, data );
909
/* set the HTML preprocessor function (to handle writing of headers */
910
ae_set_preprocessor_func( mgr, static_html_preproc_fn );
912
/* add the token/value pairs */
913
ae_add_tags( mgr, tokens, values );
919
int ae_done_html( t_ae_template_mgr mgr, CONST char* tem_file ) {
921
t_ae_html_proc_data* data;
925
data = (t_ae_html_proc_data*)ae_get_mgr_cookie( mgr );
927
/* if a template file is specified, process it */
928
if( tem_file != NULL ) {
929
rc = ae_process_template( mgr, tem_file, data->output );
932
/* free the cookie list */
933
cookie = data->cookies;
934
while( cookie != NULL ) {
935
free( cookie->name );
936
free( cookie->value );
945
/* cleanup the manager */
946
ae_template_mgr_done( mgr );
948
/* convert return code into boolean -- '1' means success, '0' means failure.
949
* This is left over from the original ToHTML implementation. */
950
return ( rc == 0 ? 1 : 0 );
954
void ae_set_cookie( t_ae_template_mgr mgr,
959
t_ae_html_proc_data* data;
962
data = (t_ae_html_proc_data*)ae_get_mgr_cookie( mgr );
963
cookie = NEW( t_ae_cookie );
965
cookie->name = strdup( name );
966
cookie->value = strdup( value );
968
cookie->next = data->cookies;
970
data->cookies = cookie;
974
void ae_set_html_output( t_ae_template_mgr mgr, FILE* output ) {
975
t_ae_html_proc_data* data;
976
data = (t_ae_html_proc_data*)ae_get_mgr_cookie( mgr );
977
data->output = output;
980
/* ------------------------------------------------------------------------- */
981
/* miscellaneous utility functions */
982
/* ------------------------------------------------------------------------- */
984
char* ae_get_field( CONST char* text, CONST char* delim, int which ) {
985
DECL_CAST( ptr, text, char );
988
ptr = strstr( ptr, delim );
989
if( ptr == NULL ) break;
990
ptr += strlen( delim );
997
char* ae_get_field_alloc( CONST char* text, CONST char* delim, int which ) {
1002
ptr = ae_get_field( text, delim, which );
1003
if( ptr == NULL ) return NULL;
1004
len = ae_field_len( ptr, delim );
1005
new_ptr = (char*)malloc( len+1 );
1006
ae_field_cpy( new_ptr, ptr, delim );
1011
int ae_field_cmp( CONST char* field, CONST char* text, CONST char* delim ) {
1016
end = strstr( field, delim );
1017
if( end == NULL ) end = (char*)field+strlen(field);
1019
/* look for the end of one of the strings, or the first point where they differ */
1020
for( fptr = (char*)field, tptr = (char*)text; *tptr && *fptr == *tptr && fptr < end; fptr++, tptr++ ) {
1024
if( fptr == end && *tptr == 0 ) return 0;
1025
if( *tptr == 0 ) return 1;
1029
int ae_field_len( CONST char* field, CONST char* delim ) {
1032
end = strstr( field, delim );
1033
if( end == NULL ) end = (char*)field+strlen(field);
1035
return (int)( end - field );
1038
char* ae_field_cpy( char* dest, CONST char* field, CONST char* delim ) {
1041
end = strstr( field, delim );
1042
if( end == NULL ) end = (char*)field+strlen(field);
1044
memcpy( dest, (char*)field, (int)(end-field) );
1045
dest[ (int)(end-field) ] = 0;
1050
void* ae_load_dynamic_function( CONST char* lib, CONST char* func ) {
1051
void* func_ptr = NULL;
1054
#if defined( DLOPEN_TYPE )
1057
load_flags = RTLD_NOW;
1058
if( !( lib_handle = dlopen( lib, load_flags ) ) ) {
1059
fprintf( stderr, "[could not load shared library '%s': %s]", lib, dlerror() );
1063
if( !( func_ptr = dlsym( lib_handle, func ) ) ) {
1064
fprintf( stderr, "[could not load entry point '%s:%s', %s]", lib, func, dlerror() );
1067
#elif defined( _HPUX_SOURCE )
1070
load_flags = BIND_IMMEDIATE | BIND_VERBOSE;
1071
if( !(lib_handle = shl_load( lib, load_flags, 0 ) ) ) {
1072
fprintf( stderr, "[could not load shared library '%s']", lib );
1076
if( shl_findsym( &lib_handle, func, TYPE_PROCEDURE, (void*)&func_ptr ) ) {
1077
fprintf( stderr, "[could not load entry point '%s:%s']", lib, func );
1085
char* ae_build_library_name( char* dest, CONST char* root ) {
1092
if( buffer == NULL ) {
1093
buffer = (char*)malloc( 256 );
1096
#if defined( _HPUX_SOURCE )
1097
/* with AE's framework, all libs are in /opt/<stage>/lib, where <stage> is
1098
* 'dev', 'tst', or 'prod'. Depending on what the working directory is,
1099
* figure out which <stage> we are in and look for the library in that
1101
getcwd( cwd, sizeof( cwd ) );
1102
webroot = strstr(cwd, "web");
1106
strcpy( buffer, cwd );
1110
strcpy( buffer, "/opt/" );
1111
if( strstr( cwd, "/prod/" ) ) {
1112
strcat( buffer, "prod/" );
1113
} else if( strstr( cwd, "/tst/" ) ) {
1114
strcat( buffer, "tst/" );
1116
strcat( buffer, "dev/" );
1119
strcat( buffer, "lib/" );
1122
strcpy( buffer, "" );
1126
strcat( buffer, "lib" );
1127
strcat( buffer, root );
1128
strcat( buffer, sfx );
1134
int ae_redirect_to( FILE* output, int original_fd ) {
1137
/* if 'output' is not already 'original_fd'... */
1138
if( fileno( output ) != original_fd ) {
1139
/* flush any cached data on output */
1141
/* copy original_fd */
1142
old_fd = dup( original_fd );
1143
/* set original_fd to be the same as 'output' */
1144
dup2( fileno( output ), original_fd );
1150
void ae_restore_file( int original_fd, FILE* file ) {
1151
/* if original_fd is valid... */
1152
if( original_fd >= 0 ) {
1153
/* flush any cached data on 'file' */
1155
/* set file to be a copy of 'original_fd' */
1156
dup2( original_fd, fileno( file ) );
1157
/* close 'original_fd' */
1158
close( original_fd );
1162
/* ------------------------------------------------------------------------- */
1163
/* static function implementations */
1164
/* ------------------------------------------------------------------------- */
1166
static t_ae_file_stream* static_ae_file_stream_new( void ) {
1167
t_ae_file_stream* ptr;
1168
ptr = (t_ae_file_stream*)malloc( sizeof( t_ae_file_stream ) );
1169
ptr->get_length = static_ae_file_stream_get_length;
1170
ptr->read = static_ae_file_stream_read;
1171
ptr->close = static_ae_file_stream_close;
1177
static t_ae_buffer_stream* static_ae_buffer_stream_new( void ) {
1178
t_ae_buffer_stream* ptr;
1179
ptr = (t_ae_buffer_stream*)malloc( sizeof( t_ae_buffer_stream ) );
1180
ptr->get_length = static_ae_buffer_stream_get_length;
1181
ptr->read = static_ae_buffer_stream_read;
1182
ptr->close = static_ae_buffer_stream_close;
1188
static int static_ae_file_stream_get_length( t_ae_stream stream ) {
1189
DECL_CAST( ptr, stream, t_ae_file_stream );
1193
if( ptr->fptr == NULL ) return -1;
1195
/* compute the file's length by seeking to the end, getting the position,
1196
* and the seeking back to our original position. The position at the end
1197
* of the file is the length of the file. */
1199
pos = ftell( ptr->fptr );
1200
fseek( ptr->fptr, 0, SEEK_END );
1201
len = ftell( ptr->fptr );
1202
fseek( ptr->fptr, pos, SEEK_SET );
1207
static int static_ae_file_stream_read( t_ae_stream stream, char* buffer, int length ) {
1208
DECL_CAST( ptr, stream, t_ae_file_stream );
1209
if( ptr->fptr == NULL ) return -1;
1210
return fread( buffer, 1, length, ptr->fptr );
1213
static int static_ae_file_stream_close( t_ae_stream stream ) {
1214
DECL_CAST( ptr, stream, t_ae_file_stream );
1215
if( ptr->fptr == NULL ) return -1;
1216
if( !ptr->wrapped ) {
1217
fclose( ptr->fptr );
1224
static int static_ae_buffer_stream_get_length( t_ae_stream stream ) {
1225
DECL_CAST( ptr, stream, t_ae_buffer_stream );
1226
if( ptr->buffer == NULL ) return -1;
1227
return strlen( ptr->buffer );
1230
static int static_ae_buffer_stream_read( t_ae_stream stream, char* buffer, int length ) {
1231
DECL_CAST( ptr, stream, t_ae_buffer_stream );
1234
if( ptr->buffer == NULL ) return -1;
1235
len = strlen( ptr->buffer ) - ptr->pos;
1236
len = ( len > length ? length : len );
1237
memcpy( buffer, ptr->buffer+ptr->pos, len );
1242
static int static_ae_buffer_stream_close( t_ae_stream stream ) {
1243
DECL_CAST( ptr, stream, t_ae_buffer_stream );
1244
if( ptr->buffer == NULL ) return -1;
1245
free( ptr->buffer );
1251
static t_ae_tag static_ae_comparison_tag( CONST char* name,
1254
t_ae_comparison_tag* tag;
1256
tag = (t_ae_comparison_tag*)ae_typed_tag( name,
1257
static_ae_comparison_tag_process,
1258
sizeof( t_ae_comparison_tag ) );
1259
tag->comp_type = comparison;
1261
return (t_ae_tag)tag;
1264
static int static_ae_replace_tag_apply( t_ae_tag tag,
1266
t_ae_template_mgr mgr,
1269
DECL_CAST( tag_data, tag, t_ae_replace_tag );
1270
if( strcmp( tag_data->m_tag, text ) != 0 ) return 0;
1271
return tag_data->process( tag, text, mgr, output );
1274
static int static_ae_replace_tag_process( t_ae_tag tag,
1276
t_ae_template_mgr mgr,
1279
DECL_CAST( tag_data, tag, t_ae_replace_tag );
1280
if( tag_data->m_data != NULL ) {
1281
fputs( tag_data->m_data, output );
1286
static int static_ae_replace_tag_cleanup( t_ae_tag tag ) {
1287
DECL_CAST( tag_data, tag, t_ae_replace_tag );
1288
if( tag_data->m_data != NULL ) {
1289
free( tag_data->m_data );
1291
tag_data->m_data = NULL;
1295
static int static_ae_cyclical_replace_tag_process( t_ae_tag tag,
1297
t_ae_template_mgr mgr,
1300
DECL_CAST( tag_data, tag, t_ae_cyclical_replace_tag );
1303
if( *(tag_data->m_next) == 0 ) {
1304
fputs( "no more data values in cyclical replace tag", output );
1305
tag_data->m_next = 0;
1309
/* find the next delimiter, in the data */
1310
end = strstr( tag_data->m_next, tag_data->m_rpt_delim );
1312
fputs( "non-terminated data string in cyclical replace tag", output );
1313
tag_data->m_next = 0;
1317
/* write out all data up to the 'end' pointer */
1318
while( tag_data->m_next < end ) {
1319
fputc( *(tag_data->m_next), output );
1323
/* move the pointer past the delimiter at the end of the record, so it now
1324
* points to the beginning of the next record. */
1326
tag_data->m_next += strlen( tag_data->m_rpt_delim );
1330
static int static_ae_cyclical_replace_tag_cleanup( t_ae_tag tag ) {
1331
DECL_CAST( tag_data, tag, t_ae_cyclical_replace_tag );
1332
free( tag_data->m_data );
1333
free( tag_data->m_rpt_delim );
1334
tag_data->m_next = NULL;
1338
static int static_ae_typed_tag_apply( t_ae_tag tag,
1340
t_ae_template_mgr mgr,
1343
GENERIC_TAG( tag_data, tag );
1346
type = ae_get_field( text, tag_data->m_delim, 0 );
1347
if( type == NULL ) return 0;
1348
/* a typed tag matches if the first field of the text chunk matches the m_tag (name)
1349
* field of the tag. If it does, then we call the process function for this tag. */
1350
if( ae_field_cmp( type, tag_data->m_tag, tag_data->m_delim ) != 0 ) return 0;
1351
return tag_data->process( tag, text, mgr, output );
1354
static int static_ae_if_tag_process( t_ae_tag tag,
1356
t_ae_template_mgr mgr,
1359
GENERIC_TAG( tag_data, tag );
1364
tok = ae_get_field( text, tag_data->m_delim, 1 );
1365
ae_field_cpy( tok_buf, tok, tag_data->m_delim );
1366
value = ae_get_value( mgr, tok_buf );
1367
if( !( value && *value ) ) {
1371
ae_process_buffer( mgr, ae_get_field( text, tag_data->m_delim, 2 ), output );
1375
static int static_ae_if_not_tag_process( t_ae_tag tag,
1377
t_ae_template_mgr mgr,
1380
GENERIC_TAG( tag_data, tag );
1385
tok = ae_get_field( text, tag_data->m_delim, 1 );
1386
ae_field_cpy( tok_buf, tok, tag_data->m_delim );
1387
value = ae_get_value( mgr, tok_buf );
1388
if( value && *value ) {
1392
ae_process_buffer( mgr, ae_get_field( text, tag_data->m_delim, 2 ), output );
1396
static int static_ae_include_tag_process( t_ae_tag tag,
1398
t_ae_template_mgr mgr,
1401
GENERIC_TAG( tag_data, tag );
1404
tok = ae_get_field( text, tag_data->m_delim, 1 );
1405
if( ae_get_tag( mgr, tok ) != NULL ) {
1406
tok = ae_get_value( mgr, tok );
1408
ae_process_template( mgr, tok, output );
1413
#define ROW_NUM_TAG_NAME "ae_row_num"
1415
static int static_ae_repeat_tag_process( t_ae_tag tag,
1417
t_ae_template_mgr mgr,
1420
GENERIC_TAG( tag_data, tag );
1421
t_ae_cyclical_replace_tag* repl_tag;
1422
t_ae_replace_tag* row_tag;
1427
char row_num_tag[32];
1428
char row_num_value[10];
1431
source = ae_get_field_alloc( text, tag_data->m_delim, 1 );
1432
token = ae_get_field_alloc( text, tag_data->m_delim, 2 );
1433
delim = ae_get_field_alloc( text, tag_data->m_delim, 3 );
1434
data = ae_get_field( text, tag_data->m_delim, 4 );
1436
/* create a new cyclical replace tag from the delimited string associated with this
1437
* repeat tag. Add it to the manager */
1439
repl_tag = ae_cyclical_replace_tag( token, ae_get_value( mgr, source ), delim );
1440
ae_add_tag_ex( mgr, repl_tag );
1442
/* look for the first available row_num tag name. By default, we use ae_row_num, but
1443
* if it is taken then that means that we are currently embedded inside of a repeat tag.
1444
* So, we add a number to the end of the tag name and try again. This continues until
1445
* a number is found that is not taken. This allows repeat tags to be nested to an
1446
* arbitrary depth. */
1449
strcpy( row_num_tag, ROW_NUM_TAG_NAME );
1450
while( ae_get_tag( mgr, row_num_tag ) != NULL ) {
1452
sprintf( row_num_tag, ROW_NUM_TAG_NAME "_%d", i );
1455
/* repeatedly process the data for the repeat tag, until the cyclical replace tag
1456
* is out of data. Each pass through the data, we increment the row num and set
1457
* it in the row_num_tag variable. */
1460
while( *(repl_tag->m_next) != 0 ) {
1461
sprintf( row_num_value, "%d", i );
1462
ae_add_tag( mgr, row_num_tag, row_num_value );
1463
ae_process_buffer( mgr, data, output );
1467
/* remove the row_num_tag and the cyclical replace tag from the manager */
1468
ae_remove_tag( mgr, row_num_tag );
1469
ae_remove_tag_ex( mgr, repl_tag );
1471
/* free our allocated data */
1479
static int static_ae_env_tag_process( t_ae_tag tag,
1481
t_ae_template_mgr mgr,
1487
env = ae_get_field( text, ae_get_tag_delim( tag ), 1 );
1488
val = getenv( env );
1491
fputs( val, output );
1497
static int static_ae_exec_tag_process( t_ae_tag tag,
1499
t_ae_template_mgr mgr,
1508
tok = ae_get_field( text, ae_get_tag_delim( tag ), 1 );
1509
if( ae_get_tag( mgr, tok ) != NULL ) {
1510
tok = ae_get_value( mgr, tok );
1513
pipe_output = popen( tok, "r" );
1514
if( pipe_output == NULL ) {
1515
fprintf( output, "[popen failed: %d (%s)]", errno, strerror( errno ) );
1517
while( ( count = fread( buf, 1, sizeof( buf )-1, pipe_output ) ) > 0 ) {
1519
fputs( buf, output );
1521
pclose( pipe_output );
1527
static int static_ae_comparison_tag_process( t_ae_tag tag,
1529
t_ae_template_mgr mgr,
1532
DECL_CAST( tag_data, tag, t_ae_comparison_tag );
1537
tok_buf = ae_get_field_alloc( text, tag_data->m_delim, 1 );
1538
val_buf = ae_get_field_alloc( text, tag_data->m_delim, 2 );
1539
comp_result = strcmp( ae_get_value( mgr, tok_buf ), val_buf );
1541
switch( tag_data->comp_type ) {
1542
case COMP_TYPE_EQ: comp_result = ( comp_result == 0 ); break;
1543
case COMP_TYPE_NE: comp_result = ( comp_result != 0 ); break;
1544
case COMP_TYPE_LT: comp_result = ( comp_result < 0 ); break;
1545
case COMP_TYPE_LE: comp_result = ( comp_result <= 0 ); break;
1546
case COMP_TYPE_GT: comp_result = ( comp_result > 0 ); break;
1547
case COMP_TYPE_GE: comp_result = ( comp_result >= 0 ); break;
1551
ae_process_buffer( mgr, ae_get_field( text, tag_data->m_delim, 3 ), output );
1560
static int static_ae_include_tag_named_process( t_ae_tag tag,
1562
t_ae_template_mgr mgr,
1565
ae_process_template( mgr, ae_get_tag_value( tag ), output );
1569
static int static_ae_shared_fn_apply( t_ae_tag tag,
1571
t_ae_template_mgr mgr,
1574
GENERIC_TAG( tag_data, tag );
1576
if( ae_field_cmp( ae_get_field( text, tag_data->m_delim, 0 ), "EXEC_SHARED", tag_data->m_delim ) != 0 )
1579
if( ae_field_cmp( ae_get_field( text, tag_data->m_delim, 1 ), tag_data->m_tag, tag_data->m_delim ) != 0 )
1582
return tag_data->process( tag, text, mgr, output );
1585
static int static_ae_shared_fn_process( t_ae_tag tag,
1587
t_ae_template_mgr mgr,
1590
DECL_CAST( tag_data, tag, t_ae_shared_fn_tag );
1591
int (*func_ptr)( void* );
1592
char libname[ 256 ];
1594
ae_build_library_name( libname, tag_data->m_lib );
1595
func_ptr = (int(*)(void*))ae_load_dynamic_function( libname, tag_data->m_func );
1596
if( func_ptr == NULL ) return 1;
1598
/* flush the output, since the function may write to stdout. Although stdout is
1599
* redirected to output already, if we don't flush the output here (and flush stdout
1600
* at the end of the function) we will get text written in the wrong order, due to
1605
/* call the function */
1606
func_ptr( tag_data->m_cookie );
1612
static int static_ae_shared_fn_tag_cleanup( t_ae_tag tag ) {
1613
DECL_CAST( tag_data, tag, t_ae_shared_fn_tag );
1614
free( tag_data->m_lib );
1615
free( tag_data->m_func );
1619
static int static_html_preproc_fn( t_ae_template_mgr mgr, FILE* output ) {
1620
t_ae_html_proc_data* data;
1621
t_ae_cookie* cookie;
1624
char ttlBuffer[ 128 ];
1626
data = (t_ae_html_proc_data*)ae_get_mgr_cookie( mgr );
1628
if( data->headers ) {
1629
fputs( "Content-type: text/html\n", output );
1630
if( data->no_cache ) {
1631
fputs( "Pragma: no-cache\n", output );
1632
fputs( "Expires: Thu, 1 Jan 1970 00:00:01 GMT\n", output );
1635
/* write the cookie definitions */
1636
cookie = data->cookies;
1637
while( cookie != NULL ) {
1638
fprintf( output, "Set-Cookie: %s=%s; PATH=/", cookie->name, cookie->value );
1640
if( cookie->ttl >= 0 ) {
1641
/* compute the time-to-live as the number of seconds from the current time.
1642
* Cookie TTL's must be given in GMT. */
1643
curTime = time( NULL ) + cookie->ttl;
1644
tstr = gmtime( &curTime );
1645
strftime( ttlBuffer, sizeof( ttlBuffer ), "%a, %d-%b-%y %H:%M:%S GMT", tstr );
1646
fprintf( output, "; EXPIRES=%s", ttlBuffer );
1649
fprintf( output, "\n" );
1650
cookie = cookie->next;
1653
fputs( "\n", output );
1660
static char* static_get_non_value( t_ae_tag tag ) {
1664
static char* static_get_replace_tag_value( t_ae_tag tag ) {
1665
DECL_CAST( tag_data, tag, t_ae_replace_tag );
1666
return tag_data->m_data;