Fossil SCM
Documented the "ob" API, added "ob level".
Commit
66104f8b5d8278cd7a15cdd4f8ea09437ae53c31
Parent
ef68eb01069462e…
2 files changed
+129
-28
+2
M
src/th.c
+129
-28
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -2751,71 +2751,92 @@ | ||
| 2751 | 2751 | #endif |
| 2752 | 2752 | /* end TH_USE_SQLITE */ |
| 2753 | 2753 | |
| 2754 | 2754 | |
| 2755 | 2755 | #ifdef TH_USE_OUTBUF |
| 2756 | +/* Reminder: the ob code "really" belongs in th_lang.c, | |
| 2757 | + but we need access to Th_Interp internals in order to | |
| 2758 | + swap out Th_Vtab parts for purposes of stacking layers | |
| 2759 | + of buffers. | |
| 2760 | +*/ | |
| 2761 | +/* | |
| 2762 | +** Manager of a stack of Blob objects for output buffering. | |
| 2763 | +*/ | |
| 2756 | 2764 | struct Th_Ob_Man { |
| 2757 | - Blob ** aBuf; | |
| 2758 | - int nBuf; | |
| 2759 | - int cursor; | |
| 2760 | - Th_Interp * interp; | |
| 2761 | - Th_Vtab ** aVtab; | |
| 2765 | + Blob ** aBuf; /* Stack of Blobs */ | |
| 2766 | + int nBuf; /* Number of blobs */ | |
| 2767 | + int cursor; /* Current level (-1=not active) */ | |
| 2768 | + Th_Interp * interp; /* The associated interpreter */ | |
| 2769 | + Th_Vtab ** aVtab; /* Stack of Vtabs (they get restored | |
| 2770 | + when a buffering level is popped). | |
| 2771 | + Has nBuf entries. | |
| 2772 | + | |
| 2773 | + FIXME? Only swap out the "out" members? | |
| 2774 | + */ | |
| 2762 | 2775 | }; |
| 2763 | 2776 | |
| 2764 | 2777 | typedef struct Th_Ob_Man Th_Ob_Man; |
| 2765 | 2778 | #define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL } |
| 2766 | 2779 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2767 | 2780 | static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; |
| 2768 | 2781 | |
| 2782 | +/* | |
| 2783 | +** Returns the top-most Blob in pMan's stack, or NULL | |
| 2784 | +** if buffering is not active. | |
| 2785 | +*/ | |
| 2769 | 2786 | static Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2770 | 2787 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2771 | 2788 | } |
| 2772 | 2789 | |
| 2773 | 2790 | |
| 2774 | -static int Th_output_ob( char const * zData, int len, void * pState ){ | |
| 2791 | +/* | |
| 2792 | +** Th_output_f() impl which expects pState to be (Th_Ob_Man*). | |
| 2793 | +** (zData,len) are appended to pState's current output buffer. | |
| 2794 | +*/ | |
| 2795 | +static int Th_output_f_ob( char const * zData, int len, void * pState ){ | |
| 2775 | 2796 | Th_Ob_Man * pMan = (Th_Ob_Man*)pState; |
| 2776 | 2797 | Blob * b = Th_ob_current( pMan ); |
| 2777 | 2798 | assert( NULL != pMan ); |
| 2778 | 2799 | assert( b ); |
| 2779 | 2800 | blob_append( b, zData, len ); |
| 2780 | 2801 | return len; |
| 2781 | 2802 | } |
| 2782 | 2803 | |
| 2804 | +/* | |
| 2805 | +** Vtab impl for the ob buffering layer. | |
| 2806 | +*/ | |
| 2783 | 2807 | static Th_Vtab Th_Vtab_Ob = { th_fossil_realloc, |
| 2784 | 2808 | { |
| 2785 | - Th_output_ob, | |
| 2809 | + Th_output_f_ob, | |
| 2786 | 2810 | NULL, |
| 2787 | 2811 | 1 |
| 2788 | 2812 | } |
| 2789 | 2813 | }; |
| 2790 | 2814 | |
| 2791 | -#if 0 | |
| 2792 | -#define OB_MALLOC(I,N) malloc((N)) | |
| 2793 | -#define OB_REALLOC(I,P,N) realloc((P),(N)) | |
| 2794 | -#define OB_FREE(I,P) free((P)) | |
| 2795 | -#else | |
| 2796 | -#define OB_MALLOC(I,N) Th_Malloc((I),(N)) | |
| 2797 | -#define OB_REALLOC(I,P,N) Th_Realloc((I),(P),(N)) | |
| 2798 | -#define OB_FREE(I,P) Th_Free((I),(P)) | |
| 2799 | -#endif | |
| 2815 | +/* | |
| 2816 | +** Pushes a new blob onto pMan's stack. On success | |
| 2817 | +** returns TH_OK and assigns *pOut (if pOut is not NULL) | |
| 2818 | +** to the new blob (which is owned by pMan). On error | |
| 2819 | +** pOut is not modified and non-0 is returned. | |
| 2820 | +*/ | |
| 2800 | 2821 | int Th_ob_push( Th_Ob_Man * pMan, Blob ** pOut ){ |
| 2801 | 2822 | Blob * pBlob; |
| 2802 | 2823 | int x, i; |
| 2803 | 2824 | assert( NULL != pMan->interp ); |
| 2804 | - pBlob = (Blob *)OB_MALLOC(pMan->interp, sizeof(Blob)); | |
| 2825 | + pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob)); | |
| 2805 | 2826 | *pBlob = empty_blob; |
| 2806 | 2827 | |
| 2807 | 2828 | if( pMan->cursor <= pMan->nBuf ){ |
| 2808 | 2829 | /* expand if needed */ |
| 2809 | 2830 | x = (pMan->cursor>0 ? pMan->cursor : 1) * 2; |
| 2810 | 2831 | /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/ |
| 2811 | - void * re = OB_REALLOC( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); | |
| 2832 | + void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); | |
| 2812 | 2833 | if(NULL==re){ |
| 2813 | 2834 | goto error; |
| 2814 | 2835 | } |
| 2815 | 2836 | pMan->aBuf = (Blob **)re; |
| 2816 | - re = OB_REALLOC( pMan->interp, pMan->aVtab, x * sizeof(Th_Vtab*) ); | |
| 2837 | + re = Th_Realloc( pMan->interp, pMan->aVtab, x * sizeof(Th_Vtab*) ); | |
| 2817 | 2838 | if(NULL==re){ |
| 2818 | 2839 | goto error; |
| 2819 | 2840 | } |
| 2820 | 2841 | pMan->aVtab = (Th_Vtab**)re; |
| 2821 | 2842 | for( i = pMan->nBuf; i < x; ++i ){ |
| @@ -2836,15 +2857,23 @@ | ||
| 2836 | 2857 | } |
| 2837 | 2858 | /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/ |
| 2838 | 2859 | return TH_OK; |
| 2839 | 2860 | error: |
| 2840 | 2861 | if( pBlob ){ |
| 2841 | - OB_FREE( pMan->interp, pBlob ); | |
| 2862 | + Th_Free( pMan->interp, pBlob ); | |
| 2842 | 2863 | } |
| 2843 | 2864 | return TH_ERROR; |
| 2844 | 2865 | } |
| 2845 | 2866 | |
| 2867 | +/* | |
| 2868 | +** Pops the top-most output buffer off the stack and returns | |
| 2869 | +** it. Returns NULL if there is no current buffer. When the last | |
| 2870 | +** buffer is popped, pMan's internals are cleaned up. | |
| 2871 | +** | |
| 2872 | +** The caller owns the returned object and must eventually call | |
| 2873 | +** blob_reset() on it. | |
| 2874 | +*/ | |
| 2846 | 2875 | Blob * Th_ob_pop( Th_Ob_Man * pMan ){ |
| 2847 | 2876 | if( pMan->cursor < 0 ){ |
| 2848 | 2877 | return NULL; |
| 2849 | 2878 | }else{ |
| 2850 | 2879 | Blob * rc; |
| @@ -2852,19 +2881,27 @@ | ||
| 2852 | 2881 | rc = pMan->aBuf[pMan->cursor]; |
| 2853 | 2882 | pMan->aBuf[pMan->cursor] = NULL; |
| 2854 | 2883 | pMan->interp->pVtab = pMan->aVtab[pMan->cursor]; |
| 2855 | 2884 | pMan->aVtab[pMan->cursor] = NULL; |
| 2856 | 2885 | if(-1 == --pMan->cursor){ |
| 2857 | - OB_FREE( pMan->interp, pMan->aBuf ); | |
| 2858 | - OB_FREE( pMan->interp, pMan->aVtab ); | |
| 2886 | + Th_Free( pMan->interp, pMan->aBuf ); | |
| 2887 | + Th_Free( pMan->interp, pMan->aVtab ); | |
| 2859 | 2888 | *pMan = Th_Ob_Man_empty; |
| 2860 | 2889 | } |
| 2861 | 2890 | /*fprintf(stderr,"OB pop: %p level=%d\n", rc, pMan->cursor-1);*/ |
| 2862 | 2891 | return rc; |
| 2863 | 2892 | } |
| 2864 | 2893 | } |
| 2865 | 2894 | |
| 2895 | +/* | |
| 2896 | +** TH Syntax: | |
| 2897 | +** | |
| 2898 | +** ob clean | |
| 2899 | +** | |
| 2900 | +** Erases any currently buffered contents but does not modify | |
| 2901 | +** the buffering level. | |
| 2902 | +*/ | |
| 2866 | 2903 | static int ob_clean_command( Th_Interp *interp, void *ctx, |
| 2867 | 2904 | int argc, const char **argv, int *argl |
| 2868 | 2905 | ){ |
| 2869 | 2906 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2870 | 2907 | Blob * b; |
| @@ -2877,10 +2914,18 @@ | ||
| 2877 | 2914 | blob_reset(b); |
| 2878 | 2915 | } |
| 2879 | 2916 | return TH_OK; |
| 2880 | 2917 | } |
| 2881 | 2918 | |
| 2919 | +/* | |
| 2920 | +** TH Syntax: | |
| 2921 | +** | |
| 2922 | +** ob end | |
| 2923 | +** | |
| 2924 | +** Erases any currently buffered contents and pops the current buffer | |
| 2925 | +** from the stack. | |
| 2926 | +*/ | |
| 2882 | 2927 | static int ob_end_command( Th_Interp *interp, void *ctx, |
| 2883 | 2928 | int argc, const char **argv, int *argl ){ |
| 2884 | 2929 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2885 | 2930 | Blob * b; |
| 2886 | 2931 | assert( pMan && (interp == pMan->interp) ); |
| @@ -2888,15 +2933,27 @@ | ||
| 2888 | 2933 | if(!b){ |
| 2889 | 2934 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2890 | 2935 | return TH_ERROR; |
| 2891 | 2936 | }else{ |
| 2892 | 2937 | blob_reset(b); |
| 2893 | - OB_FREE( interp, b ); | |
| 2938 | + Th_Free( interp, b ); | |
| 2894 | 2939 | } |
| 2895 | 2940 | return TH_OK; |
| 2896 | 2941 | } |
| 2897 | 2942 | |
| 2943 | +/* | |
| 2944 | +** TH Syntax: | |
| 2945 | +** | |
| 2946 | +** ob flush | |
| 2947 | +** | |
| 2948 | +** UNTESTED! Maybe not needed. | |
| 2949 | +** | |
| 2950 | +** Briefly reverts the output layer to the next-lower | |
| 2951 | +** level, flushes the current buffer to that output layer, | |
| 2952 | +** and clears out the current buffer. Does not change the | |
| 2953 | +** buffering level. | |
| 2954 | +*/ | |
| 2898 | 2955 | static int ob_flush_command( Th_Interp *interp, void *ctx, |
| 2899 | 2956 | int argc, const char **argv, int *argl ){ |
| 2900 | 2957 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2901 | 2958 | Blob * b = NULL; |
| 2902 | 2959 | Th_Vtab * oldVtab; |
| @@ -2912,10 +2969,21 @@ | ||
| 2912 | 2969 | interp->pVtab = oldVtab; |
| 2913 | 2970 | blob_reset(b); |
| 2914 | 2971 | return TH_OK; |
| 2915 | 2972 | } |
| 2916 | 2973 | |
| 2974 | +/* | |
| 2975 | +** TH Syntax: | |
| 2976 | +** | |
| 2977 | +** ob get ?clean|end? | |
| 2978 | +** | |
| 2979 | +** Fetches the contents of the current buffer level. If either | |
| 2980 | +** 'clean' or 'end' are specified then the effect is as if "ob clean" | |
| 2981 | +** or "ob end", respectively, are called after fetching the | |
| 2982 | +** value. Calling "ob get end" is functionality equivalent to "ob get" | |
| 2983 | +** followed by "ob end". | |
| 2984 | +*/ | |
| 2917 | 2985 | static int ob_get_command( Th_Interp *interp, void *ctx, |
| 2918 | 2986 | int argc, const char **argv, int *argl){ |
| 2919 | 2987 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2920 | 2988 | Blob * b = NULL; |
| 2921 | 2989 | assert( pMan && (interp == pMan->interp) ); |
| @@ -2942,10 +3010,34 @@ | ||
| 2942 | 3010 | } |
| 2943 | 3011 | return rc; |
| 2944 | 3012 | } |
| 2945 | 3013 | } |
| 2946 | 3014 | |
| 3015 | +/* | |
| 3016 | +** TH Syntax: | |
| 3017 | +** | |
| 3018 | +** ob level | |
| 3019 | +** | |
| 3020 | +** Returns the buffering level, where 0 means no buffering is | |
| 3021 | +** active, 1 means 1 level is active, etc. | |
| 3022 | +*/ | |
| 3023 | +static int ob_level_command( Th_Interp *interp, void *ctx, | |
| 3024 | + int argc, const char **argv, int *argl | |
| 3025 | +){ | |
| 3026 | + Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; | |
| 3027 | + Th_SetResultInt( interp, 1 + pMan->cursor ); | |
| 3028 | + return TH_OK; | |
| 3029 | +} | |
| 3030 | + | |
| 3031 | +/* | |
| 3032 | +** TH Syntax: | |
| 3033 | +** | |
| 3034 | +** ob start | |
| 3035 | +** | |
| 3036 | +** Pushes a new level of buffering onto the buffer stack. | |
| 3037 | +** Returns the new buffering level (1-based). | |
| 3038 | +*/ | |
| 2947 | 3039 | static int ob_start_command( Th_Interp *interp, void *ctx, |
| 2948 | 3040 | int argc, const char **argv, int *argl |
| 2949 | 3041 | ){ |
| 2950 | 3042 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2951 | 3043 | Blob * b = NULL; |
| @@ -2956,14 +3048,22 @@ | ||
| 2956 | 3048 | assert( NULL == b ); |
| 2957 | 3049 | return rc; |
| 2958 | 3050 | } |
| 2959 | 3051 | assert( NULL != b ); |
| 2960 | 3052 | /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/ |
| 2961 | - Th_SetResultInt( interp, pMan->cursor ); | |
| 3053 | + Th_SetResultInt( interp, 1 + pMan->cursor ); | |
| 2962 | 3054 | return TH_OK; |
| 2963 | 3055 | } |
| 2964 | 3056 | |
| 3057 | +/* | |
| 3058 | +** TH Syntax: | |
| 3059 | +** | |
| 3060 | +** ob clean|end|flush|get|level|start | |
| 3061 | +** | |
| 3062 | +** Runs the given subcommand. | |
| 3063 | +** | |
| 3064 | +*/ | |
| 2965 | 3065 | static int ob_cmd( |
| 2966 | 3066 | Th_Interp *interp, |
| 2967 | 3067 | void *ignored, |
| 2968 | 3068 | int argc, |
| 2969 | 3069 | const char **argv, |
| @@ -2973,18 +3073,20 @@ | ||
| 2973 | 3073 | Th_SubCommand aSub[] = { |
| 2974 | 3074 | { "clean", ob_clean_command }, |
| 2975 | 3075 | { "end", ob_end_command }, |
| 2976 | 3076 | { "flush", ob_flush_command }, |
| 2977 | 3077 | { "get", ob_get_command }, |
| 3078 | + { "level", ob_level_command }, | |
| 2978 | 3079 | { "start", ob_start_command }, |
| 2979 | 3080 | { 0, 0 } |
| 2980 | 3081 | }; |
| 2981 | 3082 | if(NULL == pMan->interp){ |
| 2982 | 3083 | pMan->interp = interp; |
| 2983 | 3084 | /* |
| 2984 | 3085 | FIXME: add rudamentary at-finalization GC to Th_Interp and clean |
| 2985 | - this up there. | |
| 3086 | + this up there. We currently leak only if the client does not | |
| 3087 | + close all buffering levels properly. | |
| 2986 | 3088 | */ |
| 2987 | 3089 | } |
| 2988 | 3090 | return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); |
| 2989 | 3091 | |
| 2990 | 3092 | } |
| @@ -2994,11 +3096,10 @@ | ||
| 2994 | 3096 | {"ob", ob_cmd, 0}, |
| 2995 | 3097 | {0,0,0} |
| 2996 | 3098 | }; |
| 2997 | 3099 | return Th_register_commands( interp, aCommand ); |
| 2998 | 3100 | } |
| 2999 | -#undef OB_MALLOC | |
| 3000 | -#undef OB_REALLOC | |
| 3001 | -#undef OB_FREE | |
| 3101 | + | |
| 3102 | +#undef Th_Ob_Man_empty_m | |
| 3002 | 3103 | #endif |
| 3003 | 3104 | /* end TH_USE_OUTBUF */ |
| 3004 | 3105 | |
| 3005 | 3106 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -2751,71 +2751,92 @@ | |
| 2751 | #endif |
| 2752 | /* end TH_USE_SQLITE */ |
| 2753 | |
| 2754 | |
| 2755 | #ifdef TH_USE_OUTBUF |
| 2756 | struct Th_Ob_Man { |
| 2757 | Blob ** aBuf; |
| 2758 | int nBuf; |
| 2759 | int cursor; |
| 2760 | Th_Interp * interp; |
| 2761 | Th_Vtab ** aVtab; |
| 2762 | }; |
| 2763 | |
| 2764 | typedef struct Th_Ob_Man Th_Ob_Man; |
| 2765 | #define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL } |
| 2766 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2767 | static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; |
| 2768 | |
| 2769 | static Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2770 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2771 | } |
| 2772 | |
| 2773 | |
| 2774 | static int Th_output_ob( char const * zData, int len, void * pState ){ |
| 2775 | Th_Ob_Man * pMan = (Th_Ob_Man*)pState; |
| 2776 | Blob * b = Th_ob_current( pMan ); |
| 2777 | assert( NULL != pMan ); |
| 2778 | assert( b ); |
| 2779 | blob_append( b, zData, len ); |
| 2780 | return len; |
| 2781 | } |
| 2782 | |
| 2783 | static Th_Vtab Th_Vtab_Ob = { th_fossil_realloc, |
| 2784 | { |
| 2785 | Th_output_ob, |
| 2786 | NULL, |
| 2787 | 1 |
| 2788 | } |
| 2789 | }; |
| 2790 | |
| 2791 | #if 0 |
| 2792 | #define OB_MALLOC(I,N) malloc((N)) |
| 2793 | #define OB_REALLOC(I,P,N) realloc((P),(N)) |
| 2794 | #define OB_FREE(I,P) free((P)) |
| 2795 | #else |
| 2796 | #define OB_MALLOC(I,N) Th_Malloc((I),(N)) |
| 2797 | #define OB_REALLOC(I,P,N) Th_Realloc((I),(P),(N)) |
| 2798 | #define OB_FREE(I,P) Th_Free((I),(P)) |
| 2799 | #endif |
| 2800 | int Th_ob_push( Th_Ob_Man * pMan, Blob ** pOut ){ |
| 2801 | Blob * pBlob; |
| 2802 | int x, i; |
| 2803 | assert( NULL != pMan->interp ); |
| 2804 | pBlob = (Blob *)OB_MALLOC(pMan->interp, sizeof(Blob)); |
| 2805 | *pBlob = empty_blob; |
| 2806 | |
| 2807 | if( pMan->cursor <= pMan->nBuf ){ |
| 2808 | /* expand if needed */ |
| 2809 | x = (pMan->cursor>0 ? pMan->cursor : 1) * 2; |
| 2810 | /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/ |
| 2811 | void * re = OB_REALLOC( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); |
| 2812 | if(NULL==re){ |
| 2813 | goto error; |
| 2814 | } |
| 2815 | pMan->aBuf = (Blob **)re; |
| 2816 | re = OB_REALLOC( pMan->interp, pMan->aVtab, x * sizeof(Th_Vtab*) ); |
| 2817 | if(NULL==re){ |
| 2818 | goto error; |
| 2819 | } |
| 2820 | pMan->aVtab = (Th_Vtab**)re; |
| 2821 | for( i = pMan->nBuf; i < x; ++i ){ |
| @@ -2836,15 +2857,23 @@ | |
| 2836 | } |
| 2837 | /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/ |
| 2838 | return TH_OK; |
| 2839 | error: |
| 2840 | if( pBlob ){ |
| 2841 | OB_FREE( pMan->interp, pBlob ); |
| 2842 | } |
| 2843 | return TH_ERROR; |
| 2844 | } |
| 2845 | |
| 2846 | Blob * Th_ob_pop( Th_Ob_Man * pMan ){ |
| 2847 | if( pMan->cursor < 0 ){ |
| 2848 | return NULL; |
| 2849 | }else{ |
| 2850 | Blob * rc; |
| @@ -2852,19 +2881,27 @@ | |
| 2852 | rc = pMan->aBuf[pMan->cursor]; |
| 2853 | pMan->aBuf[pMan->cursor] = NULL; |
| 2854 | pMan->interp->pVtab = pMan->aVtab[pMan->cursor]; |
| 2855 | pMan->aVtab[pMan->cursor] = NULL; |
| 2856 | if(-1 == --pMan->cursor){ |
| 2857 | OB_FREE( pMan->interp, pMan->aBuf ); |
| 2858 | OB_FREE( pMan->interp, pMan->aVtab ); |
| 2859 | *pMan = Th_Ob_Man_empty; |
| 2860 | } |
| 2861 | /*fprintf(stderr,"OB pop: %p level=%d\n", rc, pMan->cursor-1);*/ |
| 2862 | return rc; |
| 2863 | } |
| 2864 | } |
| 2865 | |
| 2866 | static int ob_clean_command( Th_Interp *interp, void *ctx, |
| 2867 | int argc, const char **argv, int *argl |
| 2868 | ){ |
| 2869 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2870 | Blob * b; |
| @@ -2877,10 +2914,18 @@ | |
| 2877 | blob_reset(b); |
| 2878 | } |
| 2879 | return TH_OK; |
| 2880 | } |
| 2881 | |
| 2882 | static int ob_end_command( Th_Interp *interp, void *ctx, |
| 2883 | int argc, const char **argv, int *argl ){ |
| 2884 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2885 | Blob * b; |
| 2886 | assert( pMan && (interp == pMan->interp) ); |
| @@ -2888,15 +2933,27 @@ | |
| 2888 | if(!b){ |
| 2889 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2890 | return TH_ERROR; |
| 2891 | }else{ |
| 2892 | blob_reset(b); |
| 2893 | OB_FREE( interp, b ); |
| 2894 | } |
| 2895 | return TH_OK; |
| 2896 | } |
| 2897 | |
| 2898 | static int ob_flush_command( Th_Interp *interp, void *ctx, |
| 2899 | int argc, const char **argv, int *argl ){ |
| 2900 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2901 | Blob * b = NULL; |
| 2902 | Th_Vtab * oldVtab; |
| @@ -2912,10 +2969,21 @@ | |
| 2912 | interp->pVtab = oldVtab; |
| 2913 | blob_reset(b); |
| 2914 | return TH_OK; |
| 2915 | } |
| 2916 | |
| 2917 | static int ob_get_command( Th_Interp *interp, void *ctx, |
| 2918 | int argc, const char **argv, int *argl){ |
| 2919 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2920 | Blob * b = NULL; |
| 2921 | assert( pMan && (interp == pMan->interp) ); |
| @@ -2942,10 +3010,34 @@ | |
| 2942 | } |
| 2943 | return rc; |
| 2944 | } |
| 2945 | } |
| 2946 | |
| 2947 | static int ob_start_command( Th_Interp *interp, void *ctx, |
| 2948 | int argc, const char **argv, int *argl |
| 2949 | ){ |
| 2950 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2951 | Blob * b = NULL; |
| @@ -2956,14 +3048,22 @@ | |
| 2956 | assert( NULL == b ); |
| 2957 | return rc; |
| 2958 | } |
| 2959 | assert( NULL != b ); |
| 2960 | /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/ |
| 2961 | Th_SetResultInt( interp, pMan->cursor ); |
| 2962 | return TH_OK; |
| 2963 | } |
| 2964 | |
| 2965 | static int ob_cmd( |
| 2966 | Th_Interp *interp, |
| 2967 | void *ignored, |
| 2968 | int argc, |
| 2969 | const char **argv, |
| @@ -2973,18 +3073,20 @@ | |
| 2973 | Th_SubCommand aSub[] = { |
| 2974 | { "clean", ob_clean_command }, |
| 2975 | { "end", ob_end_command }, |
| 2976 | { "flush", ob_flush_command }, |
| 2977 | { "get", ob_get_command }, |
| 2978 | { "start", ob_start_command }, |
| 2979 | { 0, 0 } |
| 2980 | }; |
| 2981 | if(NULL == pMan->interp){ |
| 2982 | pMan->interp = interp; |
| 2983 | /* |
| 2984 | FIXME: add rudamentary at-finalization GC to Th_Interp and clean |
| 2985 | this up there. |
| 2986 | */ |
| 2987 | } |
| 2988 | return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); |
| 2989 | |
| 2990 | } |
| @@ -2994,11 +3096,10 @@ | |
| 2994 | {"ob", ob_cmd, 0}, |
| 2995 | {0,0,0} |
| 2996 | }; |
| 2997 | return Th_register_commands( interp, aCommand ); |
| 2998 | } |
| 2999 | #undef OB_MALLOC |
| 3000 | #undef OB_REALLOC |
| 3001 | #undef OB_FREE |
| 3002 | #endif |
| 3003 | /* end TH_USE_OUTBUF */ |
| 3004 | |
| 3005 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -2751,71 +2751,92 @@ | |
| 2751 | #endif |
| 2752 | /* end TH_USE_SQLITE */ |
| 2753 | |
| 2754 | |
| 2755 | #ifdef TH_USE_OUTBUF |
| 2756 | /* Reminder: the ob code "really" belongs in th_lang.c, |
| 2757 | but we need access to Th_Interp internals in order to |
| 2758 | swap out Th_Vtab parts for purposes of stacking layers |
| 2759 | of buffers. |
| 2760 | */ |
| 2761 | /* |
| 2762 | ** Manager of a stack of Blob objects for output buffering. |
| 2763 | */ |
| 2764 | struct Th_Ob_Man { |
| 2765 | Blob ** aBuf; /* Stack of Blobs */ |
| 2766 | int nBuf; /* Number of blobs */ |
| 2767 | int cursor; /* Current level (-1=not active) */ |
| 2768 | Th_Interp * interp; /* The associated interpreter */ |
| 2769 | Th_Vtab ** aVtab; /* Stack of Vtabs (they get restored |
| 2770 | when a buffering level is popped). |
| 2771 | Has nBuf entries. |
| 2772 | |
| 2773 | FIXME? Only swap out the "out" members? |
| 2774 | */ |
| 2775 | }; |
| 2776 | |
| 2777 | typedef struct Th_Ob_Man Th_Ob_Man; |
| 2778 | #define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL } |
| 2779 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2780 | static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; |
| 2781 | |
| 2782 | /* |
| 2783 | ** Returns the top-most Blob in pMan's stack, or NULL |
| 2784 | ** if buffering is not active. |
| 2785 | */ |
| 2786 | static Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2787 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2788 | } |
| 2789 | |
| 2790 | |
| 2791 | /* |
| 2792 | ** Th_output_f() impl which expects pState to be (Th_Ob_Man*). |
| 2793 | ** (zData,len) are appended to pState's current output buffer. |
| 2794 | */ |
| 2795 | static int Th_output_f_ob( char const * zData, int len, void * pState ){ |
| 2796 | Th_Ob_Man * pMan = (Th_Ob_Man*)pState; |
| 2797 | Blob * b = Th_ob_current( pMan ); |
| 2798 | assert( NULL != pMan ); |
| 2799 | assert( b ); |
| 2800 | blob_append( b, zData, len ); |
| 2801 | return len; |
| 2802 | } |
| 2803 | |
| 2804 | /* |
| 2805 | ** Vtab impl for the ob buffering layer. |
| 2806 | */ |
| 2807 | static Th_Vtab Th_Vtab_Ob = { th_fossil_realloc, |
| 2808 | { |
| 2809 | Th_output_f_ob, |
| 2810 | NULL, |
| 2811 | 1 |
| 2812 | } |
| 2813 | }; |
| 2814 | |
| 2815 | /* |
| 2816 | ** Pushes a new blob onto pMan's stack. On success |
| 2817 | ** returns TH_OK and assigns *pOut (if pOut is not NULL) |
| 2818 | ** to the new blob (which is owned by pMan). On error |
| 2819 | ** pOut is not modified and non-0 is returned. |
| 2820 | */ |
| 2821 | int Th_ob_push( Th_Ob_Man * pMan, Blob ** pOut ){ |
| 2822 | Blob * pBlob; |
| 2823 | int x, i; |
| 2824 | assert( NULL != pMan->interp ); |
| 2825 | pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob)); |
| 2826 | *pBlob = empty_blob; |
| 2827 | |
| 2828 | if( pMan->cursor <= pMan->nBuf ){ |
| 2829 | /* expand if needed */ |
| 2830 | x = (pMan->cursor>0 ? pMan->cursor : 1) * 2; |
| 2831 | /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/ |
| 2832 | void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); |
| 2833 | if(NULL==re){ |
| 2834 | goto error; |
| 2835 | } |
| 2836 | pMan->aBuf = (Blob **)re; |
| 2837 | re = Th_Realloc( pMan->interp, pMan->aVtab, x * sizeof(Th_Vtab*) ); |
| 2838 | if(NULL==re){ |
| 2839 | goto error; |
| 2840 | } |
| 2841 | pMan->aVtab = (Th_Vtab**)re; |
| 2842 | for( i = pMan->nBuf; i < x; ++i ){ |
| @@ -2836,15 +2857,23 @@ | |
| 2857 | } |
| 2858 | /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/ |
| 2859 | return TH_OK; |
| 2860 | error: |
| 2861 | if( pBlob ){ |
| 2862 | Th_Free( pMan->interp, pBlob ); |
| 2863 | } |
| 2864 | return TH_ERROR; |
| 2865 | } |
| 2866 | |
| 2867 | /* |
| 2868 | ** Pops the top-most output buffer off the stack and returns |
| 2869 | ** it. Returns NULL if there is no current buffer. When the last |
| 2870 | ** buffer is popped, pMan's internals are cleaned up. |
| 2871 | ** |
| 2872 | ** The caller owns the returned object and must eventually call |
| 2873 | ** blob_reset() on it. |
| 2874 | */ |
| 2875 | Blob * Th_ob_pop( Th_Ob_Man * pMan ){ |
| 2876 | if( pMan->cursor < 0 ){ |
| 2877 | return NULL; |
| 2878 | }else{ |
| 2879 | Blob * rc; |
| @@ -2852,19 +2881,27 @@ | |
| 2881 | rc = pMan->aBuf[pMan->cursor]; |
| 2882 | pMan->aBuf[pMan->cursor] = NULL; |
| 2883 | pMan->interp->pVtab = pMan->aVtab[pMan->cursor]; |
| 2884 | pMan->aVtab[pMan->cursor] = NULL; |
| 2885 | if(-1 == --pMan->cursor){ |
| 2886 | Th_Free( pMan->interp, pMan->aBuf ); |
| 2887 | Th_Free( pMan->interp, pMan->aVtab ); |
| 2888 | *pMan = Th_Ob_Man_empty; |
| 2889 | } |
| 2890 | /*fprintf(stderr,"OB pop: %p level=%d\n", rc, pMan->cursor-1);*/ |
| 2891 | return rc; |
| 2892 | } |
| 2893 | } |
| 2894 | |
| 2895 | /* |
| 2896 | ** TH Syntax: |
| 2897 | ** |
| 2898 | ** ob clean |
| 2899 | ** |
| 2900 | ** Erases any currently buffered contents but does not modify |
| 2901 | ** the buffering level. |
| 2902 | */ |
| 2903 | static int ob_clean_command( Th_Interp *interp, void *ctx, |
| 2904 | int argc, const char **argv, int *argl |
| 2905 | ){ |
| 2906 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2907 | Blob * b; |
| @@ -2877,10 +2914,18 @@ | |
| 2914 | blob_reset(b); |
| 2915 | } |
| 2916 | return TH_OK; |
| 2917 | } |
| 2918 | |
| 2919 | /* |
| 2920 | ** TH Syntax: |
| 2921 | ** |
| 2922 | ** ob end |
| 2923 | ** |
| 2924 | ** Erases any currently buffered contents and pops the current buffer |
| 2925 | ** from the stack. |
| 2926 | */ |
| 2927 | static int ob_end_command( Th_Interp *interp, void *ctx, |
| 2928 | int argc, const char **argv, int *argl ){ |
| 2929 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2930 | Blob * b; |
| 2931 | assert( pMan && (interp == pMan->interp) ); |
| @@ -2888,15 +2933,27 @@ | |
| 2933 | if(!b){ |
| 2934 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2935 | return TH_ERROR; |
| 2936 | }else{ |
| 2937 | blob_reset(b); |
| 2938 | Th_Free( interp, b ); |
| 2939 | } |
| 2940 | return TH_OK; |
| 2941 | } |
| 2942 | |
| 2943 | /* |
| 2944 | ** TH Syntax: |
| 2945 | ** |
| 2946 | ** ob flush |
| 2947 | ** |
| 2948 | ** UNTESTED! Maybe not needed. |
| 2949 | ** |
| 2950 | ** Briefly reverts the output layer to the next-lower |
| 2951 | ** level, flushes the current buffer to that output layer, |
| 2952 | ** and clears out the current buffer. Does not change the |
| 2953 | ** buffering level. |
| 2954 | */ |
| 2955 | static int ob_flush_command( Th_Interp *interp, void *ctx, |
| 2956 | int argc, const char **argv, int *argl ){ |
| 2957 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2958 | Blob * b = NULL; |
| 2959 | Th_Vtab * oldVtab; |
| @@ -2912,10 +2969,21 @@ | |
| 2969 | interp->pVtab = oldVtab; |
| 2970 | blob_reset(b); |
| 2971 | return TH_OK; |
| 2972 | } |
| 2973 | |
| 2974 | /* |
| 2975 | ** TH Syntax: |
| 2976 | ** |
| 2977 | ** ob get ?clean|end? |
| 2978 | ** |
| 2979 | ** Fetches the contents of the current buffer level. If either |
| 2980 | ** 'clean' or 'end' are specified then the effect is as if "ob clean" |
| 2981 | ** or "ob end", respectively, are called after fetching the |
| 2982 | ** value. Calling "ob get end" is functionality equivalent to "ob get" |
| 2983 | ** followed by "ob end". |
| 2984 | */ |
| 2985 | static int ob_get_command( Th_Interp *interp, void *ctx, |
| 2986 | int argc, const char **argv, int *argl){ |
| 2987 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2988 | Blob * b = NULL; |
| 2989 | assert( pMan && (interp == pMan->interp) ); |
| @@ -2942,10 +3010,34 @@ | |
| 3010 | } |
| 3011 | return rc; |
| 3012 | } |
| 3013 | } |
| 3014 | |
| 3015 | /* |
| 3016 | ** TH Syntax: |
| 3017 | ** |
| 3018 | ** ob level |
| 3019 | ** |
| 3020 | ** Returns the buffering level, where 0 means no buffering is |
| 3021 | ** active, 1 means 1 level is active, etc. |
| 3022 | */ |
| 3023 | static int ob_level_command( Th_Interp *interp, void *ctx, |
| 3024 | int argc, const char **argv, int *argl |
| 3025 | ){ |
| 3026 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 3027 | Th_SetResultInt( interp, 1 + pMan->cursor ); |
| 3028 | return TH_OK; |
| 3029 | } |
| 3030 | |
| 3031 | /* |
| 3032 | ** TH Syntax: |
| 3033 | ** |
| 3034 | ** ob start |
| 3035 | ** |
| 3036 | ** Pushes a new level of buffering onto the buffer stack. |
| 3037 | ** Returns the new buffering level (1-based). |
| 3038 | */ |
| 3039 | static int ob_start_command( Th_Interp *interp, void *ctx, |
| 3040 | int argc, const char **argv, int *argl |
| 3041 | ){ |
| 3042 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 3043 | Blob * b = NULL; |
| @@ -2956,14 +3048,22 @@ | |
| 3048 | assert( NULL == b ); |
| 3049 | return rc; |
| 3050 | } |
| 3051 | assert( NULL != b ); |
| 3052 | /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/ |
| 3053 | Th_SetResultInt( interp, 1 + pMan->cursor ); |
| 3054 | return TH_OK; |
| 3055 | } |
| 3056 | |
| 3057 | /* |
| 3058 | ** TH Syntax: |
| 3059 | ** |
| 3060 | ** ob clean|end|flush|get|level|start |
| 3061 | ** |
| 3062 | ** Runs the given subcommand. |
| 3063 | ** |
| 3064 | */ |
| 3065 | static int ob_cmd( |
| 3066 | Th_Interp *interp, |
| 3067 | void *ignored, |
| 3068 | int argc, |
| 3069 | const char **argv, |
| @@ -2973,18 +3073,20 @@ | |
| 3073 | Th_SubCommand aSub[] = { |
| 3074 | { "clean", ob_clean_command }, |
| 3075 | { "end", ob_end_command }, |
| 3076 | { "flush", ob_flush_command }, |
| 3077 | { "get", ob_get_command }, |
| 3078 | { "level", ob_level_command }, |
| 3079 | { "start", ob_start_command }, |
| 3080 | { 0, 0 } |
| 3081 | }; |
| 3082 | if(NULL == pMan->interp){ |
| 3083 | pMan->interp = interp; |
| 3084 | /* |
| 3085 | FIXME: add rudamentary at-finalization GC to Th_Interp and clean |
| 3086 | this up there. We currently leak only if the client does not |
| 3087 | close all buffering levels properly. |
| 3088 | */ |
| 3089 | } |
| 3090 | return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); |
| 3091 | |
| 3092 | } |
| @@ -2994,11 +3096,10 @@ | |
| 3096 | {"ob", ob_cmd, 0}, |
| 3097 | {0,0,0} |
| 3098 | }; |
| 3099 | return Th_register_commands( interp, aCommand ); |
| 3100 | } |
| 3101 | |
| 3102 | #undef Th_Ob_Man_empty_m |
| 3103 | #endif |
| 3104 | /* end TH_USE_OUTBUF */ |
| 3105 | |
| 3106 |
M
src/th.h
+2
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -5,10 +5,12 @@ | ||
| 5 | 5 | #include "sqlite3.h" |
| 6 | 6 | #endif |
| 7 | 7 | |
| 8 | 8 | /* |
| 9 | 9 | ** TH_USE_OUTBUF, if defined, enables the "ob" family of functions. |
| 10 | +** They are functionally similar to PHP's ob_start(), ob_end(), etc. | |
| 11 | +** family of functions, providing output capturing/buffering. | |
| 10 | 12 | */ |
| 11 | 13 | #define TH_USE_OUTBUF |
| 12 | 14 | /*#undef TH_USE_OUTBUF*/ |
| 13 | 15 | |
| 14 | 16 | |
| 15 | 17 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -5,10 +5,12 @@ | |
| 5 | #include "sqlite3.h" |
| 6 | #endif |
| 7 | |
| 8 | /* |
| 9 | ** TH_USE_OUTBUF, if defined, enables the "ob" family of functions. |
| 10 | */ |
| 11 | #define TH_USE_OUTBUF |
| 12 | /*#undef TH_USE_OUTBUF*/ |
| 13 | |
| 14 | |
| 15 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -5,10 +5,12 @@ | |
| 5 | #include "sqlite3.h" |
| 6 | #endif |
| 7 | |
| 8 | /* |
| 9 | ** TH_USE_OUTBUF, if defined, enables the "ob" family of functions. |
| 10 | ** They are functionally similar to PHP's ob_start(), ob_end(), etc. |
| 11 | ** family of functions, providing output capturing/buffering. |
| 12 | */ |
| 13 | #define TH_USE_OUTBUF |
| 14 | /*#undef TH_USE_OUTBUF*/ |
| 15 | |
| 16 | |
| 17 |