| | @@ -26,13 +26,21 @@ |
| 26 | 26 | extern void *fossil_realloc(void *p, size_t n); |
| 27 | 27 | static void * th_fossil_realloc(void *p, unsigned int n){ |
| 28 | 28 | return fossil_realloc( p, n ); |
| 29 | 29 | } |
| 30 | 30 | static int Th_output_f_ob( char const * zData, int len, void * pState ); |
| 31 | +static void Th_output_dispose_ob( void * pState ); |
| 31 | 32 | typedef struct Th_Command Th_Command; |
| 32 | 33 | typedef struct Th_Frame Th_Frame; |
| 33 | 34 | typedef struct Th_Variable Th_Variable; |
| 35 | + |
| 36 | +const Th_Vtab_Output Th_Vtab_Output_FILE = { |
| 37 | + Th_output_f_FILE /* write() */, |
| 38 | + Th_output_dispose_FILE /* dispose() */, |
| 39 | + NULL /*pState*/, |
| 40 | + 1/*enabled*/ |
| 41 | +}; |
| 34 | 42 | |
| 35 | 43 | /* |
| 36 | 44 | ** Holds client-provided "garbage collected" data for |
| 37 | 45 | ** a Th_Interp instance. |
| 38 | 46 | */ |
| | @@ -1442,16 +1450,16 @@ |
| 1442 | 1450 | return p; |
| 1443 | 1451 | } |
| 1444 | 1452 | |
| 1445 | 1453 | |
| 1446 | 1454 | int Th_Vtab_output( Th_Vtab *vTab, char const * zData, int nData ){ |
| 1447 | | - if(!vTab->out.f){ |
| 1455 | + if(!vTab->out.write){ |
| 1448 | 1456 | return -1; |
| 1449 | 1457 | }else if(!vTab->out.enabled){ |
| 1450 | 1458 | return 0; |
| 1451 | 1459 | }else{ |
| 1452 | | - return vTab->out.f( zData, nData, vTab->out.pState ); |
| 1460 | + return vTab->out.write( zData, nData, vTab->out.pState ); |
| 1453 | 1461 | } |
| 1454 | 1462 | } |
| 1455 | 1463 | |
| 1456 | 1464 | |
| 1457 | 1465 | int Th_output( Th_Interp *pInterp, char const * zData, int nData ){ |
| | @@ -1462,10 +1470,21 @@ |
| 1462 | 1470 | FILE * dest = pState ? (FILE*)pState : stdout; |
| 1463 | 1471 | int rc = (int)fwrite(zData, 1, nData, dest); |
| 1464 | 1472 | fflush(dest); |
| 1465 | 1473 | return rc; |
| 1466 | 1474 | } |
| 1475 | + |
| 1476 | +void Th_output_dispose_FILE( void * pState ){ |
| 1477 | + FILE * f = pState ? (FILE*)pState : NULL; |
| 1478 | + if(f |
| 1479 | + && (f != stdout) |
| 1480 | + && (f != stderr) |
| 1481 | + && (f != stdin)){ |
| 1482 | + fflush(f); |
| 1483 | + fclose(f); |
| 1484 | + } |
| 1485 | +} |
| 1467 | 1486 | |
| 1468 | 1487 | /* |
| 1469 | 1488 | ** Install a new th1 command. |
| 1470 | 1489 | ** |
| 1471 | 1490 | ** If a command of the same name already exists, it is deleted automatically. |
| | @@ -2772,17 +2791,23 @@ |
| 2772 | 2791 | 0/*nBuf*/, \ |
| 2773 | 2792 | -1/*cursor*/, \ |
| 2774 | 2793 | NULL/*interp*/, \ |
| 2775 | 2794 | NULL/*aOutput*/ \ |
| 2776 | 2795 | } |
| 2796 | + |
| 2797 | +/* |
| 2798 | +** Vtab impl for the ob buffering layer. |
| 2799 | +*/ |
| 2777 | 2800 | #define Th_Vtab_Output_empty_m { \ |
| 2778 | | - Th_output_f_ob /*f*/, \ |
| 2801 | + NULL /* write() */, \ |
| 2802 | + NULL /* dispose() */, \ |
| 2779 | 2803 | NULL /*pState*/,\ |
| 2780 | 2804 | 1/*enabled*/\ |
| 2781 | 2805 | } |
| 2782 | | -#define Th_Vtab_Output_ob_m { \ |
| 2783 | | - Th_output_f_ob /*f*/, \ |
| 2806 | +#define Th_Vtab_Output_ob_m { \ |
| 2807 | + Th_output_f_ob /*write()*/, \ |
| 2808 | + Th_output_dispose_ob /* dispose() */, \ |
| 2784 | 2809 | NULL /*pState*/,\ |
| 2785 | 2810 | 1/*enabled*/\ |
| 2786 | 2811 | } |
| 2787 | 2812 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2788 | 2813 | static Th_Vtab_Output Th_Vtab_Output_ob = Th_Vtab_Output_ob_m; |
| | @@ -2789,11 +2814,10 @@ |
| 2789 | 2814 | static Th_Vtab_Output Th_Vtab_Output_empty = Th_Vtab_Output_empty_m; |
| 2790 | 2815 | #define Th_Ob_Man_KEY "Th_Ob_Man" |
| 2791 | 2816 | Th_Ob_Man * Th_ob_manager(Th_Interp *interp){ |
| 2792 | 2817 | return (Th_Ob_Man*) Th_Data_Get(interp, Th_Ob_Man_KEY ); |
| 2793 | 2818 | } |
| 2794 | | - |
| 2795 | 2819 | |
| 2796 | 2820 | Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2797 | 2821 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2798 | 2822 | } |
| 2799 | 2823 | |
| | @@ -2809,24 +2833,32 @@ |
| 2809 | 2833 | assert( b ); |
| 2810 | 2834 | blob_append( b, zData, len ); |
| 2811 | 2835 | return len; |
| 2812 | 2836 | } |
| 2813 | 2837 | |
| 2814 | | -/* |
| 2815 | | -** Vtab impl for the ob buffering layer. |
| 2816 | | -*/ |
| 2817 | | -static Th_Vtab Th_Vtab_Ob = { th_fossil_realloc, |
| 2818 | | - { |
| 2819 | | - Th_output_f_ob, |
| 2820 | | - NULL, |
| 2821 | | - 1 |
| 2822 | | - } |
| 2823 | | -}; |
| 2824 | | - |
| 2825 | | -int Th_ob_push( Th_Ob_Man * pMan, Blob ** pOut ){ |
| 2838 | +static void Th_output_dispose_ob( void * pState ){ |
| 2839 | + /* possible todo: move the cleanup logic from |
| 2840 | + Th_ob_pop() to here? */ |
| 2841 | + /*printf("disposing() ob vtab.\n"); */ |
| 2842 | +#if 0 |
| 2843 | + Th_Ob_Man * pMan = (Th_Ob_Man*)pState; |
| 2844 | + Blob * b = Th_ob_current( pMan ); |
| 2845 | + assert( NULL != pMan ); |
| 2846 | + assert( b ); |
| 2847 | +#endif |
| 2848 | +} |
| 2849 | + |
| 2850 | + |
| 2851 | + |
| 2852 | +int Th_ob_push( Th_Ob_Man * pMan, |
| 2853 | + Th_Vtab_Output const * pWriter, |
| 2854 | + Blob ** pOut ){ |
| 2826 | 2855 | Blob * pBlob; |
| 2827 | 2856 | int x, i; |
| 2857 | + if( NULL == pWriter ){ |
| 2858 | + pWriter = &Th_Vtab_Output_ob; |
| 2859 | + } |
| 2828 | 2860 | assert( NULL != pMan->interp ); |
| 2829 | 2861 | pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob)); |
| 2830 | 2862 | *pBlob = empty_blob; |
| 2831 | 2863 | |
| 2832 | 2864 | if( pMan->cursor >= pMan->nBuf-2 ){ |
| | @@ -2855,11 +2887,11 @@ |
| 2855 | 2887 | assert( pMan->nBuf > pMan->cursor ); |
| 2856 | 2888 | assert( pMan->cursor >= -1 ); |
| 2857 | 2889 | ++pMan->cursor; |
| 2858 | 2890 | pMan->aBuf[pMan->cursor] = pBlob; |
| 2859 | 2891 | pMan->aOutput[pMan->cursor] = pMan->interp->pVtab->out; |
| 2860 | | - pMan->interp->pVtab->out = Th_Vtab_Ob.out; |
| 2892 | + pMan->interp->pVtab->out = *pWriter; |
| 2861 | 2893 | pMan->interp->pVtab->out.pState = pMan; |
| 2862 | 2894 | if( pOut ){ |
| 2863 | 2895 | *pOut = pBlob; |
| 2864 | 2896 | } |
| 2865 | 2897 | /*printf( "push: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/ |
| | @@ -2874,15 +2906,20 @@ |
| 2874 | 2906 | Blob * Th_ob_pop( Th_Ob_Man * pMan ){ |
| 2875 | 2907 | if( pMan->cursor < 0 ){ |
| 2876 | 2908 | return NULL; |
| 2877 | 2909 | }else{ |
| 2878 | 2910 | Blob * rc; |
| 2911 | + Th_Vtab_Output * theOut; |
| 2879 | 2912 | /*printf( "pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/ |
| 2880 | 2913 | assert( pMan->nBuf > pMan->cursor ); |
| 2881 | 2914 | rc = pMan->aBuf[pMan->cursor]; |
| 2882 | 2915 | pMan->aBuf[pMan->cursor] = NULL; |
| 2883 | | - pMan->interp->pVtab->out = pMan->aOutput[pMan->cursor]; |
| 2916 | + theOut = &pMan->aOutput[pMan->cursor]; |
| 2917 | + if( theOut->dispose ){ |
| 2918 | + theOut->dispose( theOut->pState ); |
| 2919 | + } |
| 2920 | + pMan->interp->pVtab->out = *theOut; |
| 2884 | 2921 | pMan->aOutput[pMan->cursor] = Th_Vtab_Output_empty; |
| 2885 | 2922 | if(-1 == --pMan->cursor){ |
| 2886 | 2923 | Th_Interp * interp = pMan->interp; |
| 2887 | 2924 | Th_Free( pMan->interp, pMan->aBuf ); |
| 2888 | 2925 | Th_Free( pMan->interp, pMan->aOutput ); |
| | @@ -3071,23 +3108,28 @@ |
| 3071 | 3108 | } |
| 3072 | 3109 | |
| 3073 | 3110 | /* |
| 3074 | 3111 | ** TH Syntax: |
| 3075 | 3112 | ** |
| 3076 | | -** ob start |
| 3113 | +** ob start|push |
| 3077 | 3114 | ** |
| 3078 | 3115 | ** Pushes a new level of buffering onto the buffer stack. |
| 3079 | 3116 | ** Returns the new buffering level (1-based). |
| 3117 | +** |
| 3118 | +** TODO: take an optional final argument naming the output handler. |
| 3119 | +** e.g. "stdout" or "cgi" or "default" |
| 3120 | +** |
| 3080 | 3121 | */ |
| 3081 | 3122 | static int ob_start_command( Th_Interp *interp, void *ctx, |
| 3082 | 3123 | int argc, const char **argv, int *argl |
| 3083 | 3124 | ){ |
| 3084 | 3125 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 3085 | 3126 | Blob * b = NULL; |
| 3086 | 3127 | int rc; |
| 3128 | + Th_Vtab_Output const * pWriter = &Th_Vtab_Output_ob; |
| 3087 | 3129 | assert( pMan && (interp == pMan->interp) ); |
| 3088 | | - rc = Th_ob_push(pMan, &b); |
| 3130 | + rc = Th_ob_push(pMan, NULL, &b); |
| 3089 | 3131 | if( TH_OK != rc ){ |
| 3090 | 3132 | assert( NULL == b ); |
| 3091 | 3133 | return rc; |
| 3092 | 3134 | } |
| 3093 | 3135 | assert( NULL != b ); |
| 3094 | 3136 | |