Fossil SCM
Added ability to tie client data/finalizer to th1, allowing a refactoring of OB manager to use per-interpreter-instance state instead of global state.
Commit
147c602d935c272b1f754581427fab0b326cbdba
Parent
9b3a11e16095e2c…
2 files changed
+139
-37
+25
M
src/th.c
+139
-37
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -18,26 +18,32 @@ | ||
| 18 | 18 | |
| 19 | 19 | typedef struct Th_Command Th_Command; |
| 20 | 20 | typedef struct Th_Frame Th_Frame; |
| 21 | 21 | typedef struct Th_Variable Th_Variable; |
| 22 | 22 | |
| 23 | +struct Th_GcEntry { | |
| 24 | + void * pData; | |
| 25 | + void (*xDel)( Th_Interp *, void * ); | |
| 26 | +}; | |
| 27 | +typedef struct Th_GcEntry Th_GcEntry; | |
| 28 | + | |
| 23 | 29 | /* |
| 24 | 30 | ** Interpreter structure. |
| 25 | 31 | */ |
| 26 | 32 | struct Th_Interp { |
| 27 | - Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ | |
| 33 | + Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ | |
| 28 | 34 | char *zResult; /* Current interpreter result (Th_Malloc()ed) */ |
| 29 | - int nResult; /* number of bytes in zResult */ | |
| 30 | - Th_Hash *paCmd; /* Table of registered commands */ | |
| 31 | - Th_Frame *pFrame; /* Current execution frame */ | |
| 32 | - int isListMode; /* True if thSplitList() should operate in "list" mode */ | |
| 35 | + int nResult; /* number of bytes in zResult */ | |
| 36 | + Th_Hash *paCmd; /* Table of registered commands */ | |
| 37 | + Th_Frame *pFrame; /* Current execution frame */ | |
| 38 | + int isListMode; /* True if thSplitList() should operate in "list" mode */ | |
| 39 | + Th_Hash * paGc; /* holds client-provided data owned by this object */ | |
| 33 | 40 | #ifdef TH_USE_SQLITE |
| 34 | 41 | struct { |
| 35 | 42 | sqlite3_stmt ** aStmt; |
| 36 | 43 | int nStmt; |
| 37 | - int rc; | |
| 38 | - } stmt; | |
| 44 | + } stmt; /* list of prepared statements */ | |
| 39 | 45 | #endif |
| 40 | 46 | }; |
| 41 | 47 | |
| 42 | 48 | /* |
| 43 | 49 | ** Each TH command registered using Th_CreateCommand() is represented |
| @@ -121,10 +127,11 @@ | ||
| 121 | 127 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 122 | 128 | static void thPopFrame(Th_Interp*); |
| 123 | 129 | |
| 124 | 130 | static void thFreeVariable(Th_HashEntry*, void*); |
| 125 | 131 | static void thFreeCommand(Th_HashEntry*, void*); |
| 132 | +static void thFreeGc(Th_HashEntry*, void*); | |
| 126 | 133 | |
| 127 | 134 | /* |
| 128 | 135 | ** The following are used by both the expression and language parsers. |
| 129 | 136 | ** Given that the start of the input string (z, n) is a language |
| 130 | 137 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -303,10 +310,26 @@ | ||
| 303 | 310 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 304 | 311 | } |
| 305 | 312 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 306 | 313 | pEntry->pData = 0; |
| 307 | 314 | } |
| 315 | + | |
| 316 | +/* | |
| 317 | +** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees | |
| 318 | +** pEntry->pData but not pEntry. | |
| 319 | +*/ | |
| 320 | +static void thFreeGc(Th_HashEntry *pEntry, void *pContext){ | |
| 321 | + Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData; | |
| 322 | + if(gc){ | |
| 323 | + if( gc->xDel ){ | |
| 324 | + gc->xDel( (Th_Interp*)pContext, gc->pData ); | |
| 325 | + } | |
| 326 | + Th_Free((Th_Interp *)pContext, pEntry->pData); | |
| 327 | + pEntry->pData = 0; | |
| 328 | + } | |
| 329 | +} | |
| 330 | + | |
| 308 | 331 | |
| 309 | 332 | /* |
| 310 | 333 | ** Push a new frame onto the stack. |
| 311 | 334 | */ |
| 312 | 335 | static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ |
| @@ -1682,10 +1705,17 @@ | ||
| 1682 | 1705 | */ |
| 1683 | 1706 | void Th_DeleteInterp(Th_Interp *interp){ |
| 1684 | 1707 | assert(interp->pFrame); |
| 1685 | 1708 | assert(0==interp->pFrame->pCaller); |
| 1686 | 1709 | |
| 1710 | + /* Delete any client-side gc entries first. */ | |
| 1711 | + if( interp->paGc ){ | |
| 1712 | + Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp); | |
| 1713 | + Th_HashDelete(interp, interp->paGc); | |
| 1714 | + interp->paGc = NULL; | |
| 1715 | + } | |
| 1716 | + | |
| 1687 | 1717 | /* Delete the contents of the global frame. */ |
| 1688 | 1718 | thPopFrame(interp); |
| 1689 | 1719 | |
| 1690 | 1720 | /* Delete any result currently stored in the interpreter. */ |
| 1691 | 1721 | Th_SetResult(interp, 0, 0); |
| @@ -2332,11 +2362,11 @@ | ||
| 2332 | 2362 | ** a null pointer. |
| 2333 | 2363 | */ |
| 2334 | 2364 | int th_strlen(const char *zStr){ |
| 2335 | 2365 | int n = 0; |
| 2336 | 2366 | if( zStr ){ |
| 2337 | - while( zStr[n] ) n++; | |
| 2367 | + while( zStr[n] ) ++n; | |
| 2338 | 2368 | } |
| 2339 | 2369 | return n; |
| 2340 | 2370 | } |
| 2341 | 2371 | |
| 2342 | 2372 | /* Whitespace characters: |
| @@ -2697,10 +2727,40 @@ | ||
| 2697 | 2727 | |
| 2698 | 2728 | *z = '\0'; |
| 2699 | 2729 | return Th_SetResult(interp, zBuf, -1); |
| 2700 | 2730 | } |
| 2701 | 2731 | |
| 2732 | + | |
| 2733 | +int Th_Data_Set( Th_Interp * interp, char const * key, | |
| 2734 | + void * pData, | |
| 2735 | + void (*finalizer)( Th_Interp *, void * ) ){ | |
| 2736 | + Th_HashEntry * pEnt; | |
| 2737 | + Th_GcEntry * pGc; | |
| 2738 | + if(NULL == interp->paGc){ | |
| 2739 | + interp->paGc = Th_HashNew(interp); | |
| 2740 | + assert(NULL != interp->paGc); | |
| 2741 | + if(!interp->paGc){ | |
| 2742 | + return TH_ERROR; | |
| 2743 | + } | |
| 2744 | + } | |
| 2745 | + pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1); | |
| 2746 | + if( pEnt->pData ){ | |
| 2747 | + thFreeGc( pEnt, interp ); | |
| 2748 | + } | |
| 2749 | + assert( NULL == pEnt->pData ); | |
| 2750 | + pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry)); | |
| 2751 | + pGc->pData = pData; | |
| 2752 | + pGc->xDel = finalizer; | |
| 2753 | + return 0; | |
| 2754 | + | |
| 2755 | +} | |
| 2756 | +void * Th_Data_Get( Th_Interp * interp, char const * key ){ | |
| 2757 | + Th_HashEntry * e = interp->paGc | |
| 2758 | + ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0) | |
| 2759 | + : NULL; | |
| 2760 | + return e ? e->pData : NULL; | |
| 2761 | +} | |
| 2702 | 2762 | |
| 2703 | 2763 | #ifdef TH_USE_SQLITE |
| 2704 | 2764 | int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){ |
| 2705 | 2765 | int i, x; |
| 2706 | 2766 | sqlite3_stmt * s; |
| @@ -2753,18 +2813,27 @@ | ||
| 2753 | 2813 | /* Reminder: the ob code "really" belongs in th_lang.c, |
| 2754 | 2814 | but we need access to Th_Interp internals in order to |
| 2755 | 2815 | swap out Th_Vtab parts for purposes of stacking layers |
| 2756 | 2816 | of buffers. |
| 2757 | 2817 | */ |
| 2758 | -#define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL } | |
| 2818 | +#define Th_Ob_Man_empty_m { \ | |
| 2819 | + NULL/*aBuf*/, \ | |
| 2820 | + 0/*nBuf*/, \ | |
| 2821 | + -1/*cursor*/, \ | |
| 2822 | + NULL/*interp*/, \ | |
| 2823 | + NULL/*aVtab*/ \ | |
| 2824 | +} | |
| 2759 | 2825 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2760 | 2826 | static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; |
| 2761 | - | |
| 2762 | -Th_Ob_Man * Th_ob_manager(Th_Interp *ignored){ | |
| 2763 | - return &Th_Ob_Man_instance; | |
| 2827 | +#define Th_Ob_Man_KEY "Th_Ob_Man" | |
| 2828 | +Th_Ob_Man * Th_ob_manager(Th_Interp *interp){ | |
| 2829 | + void * rc = Th_Data_Get(interp, Th_Ob_Man_KEY ); | |
| 2830 | + return rc | |
| 2831 | + ? ((Th_GcEntry*)rc)->pData | |
| 2832 | + : NULL; | |
| 2764 | 2833 | } |
| 2765 | - | |
| 2834 | + | |
| 2766 | 2835 | |
| 2767 | 2836 | Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2768 | 2837 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2769 | 2838 | } |
| 2770 | 2839 | |
| @@ -2805,11 +2874,10 @@ | ||
| 2805 | 2874 | x = pMan->nBuf + 5; |
| 2806 | 2875 | if( pMan->cursor >= x ) { |
| 2807 | 2876 | assert( 0 && "This really should not happen." ); |
| 2808 | 2877 | x = pMan->cursor + 5; |
| 2809 | 2878 | } |
| 2810 | - /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/ | |
| 2811 | 2879 | void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); |
| 2812 | 2880 | if(NULL==re){ |
| 2813 | 2881 | goto error; |
| 2814 | 2882 | } |
| 2815 | 2883 | pMan->aBuf = (Blob **)re; |
| @@ -2832,18 +2900,19 @@ | ||
| 2832 | 2900 | pMan->interp->pVtab = &Th_Vtab_Ob; |
| 2833 | 2901 | Th_Vtab_Ob.out.pState = pMan; |
| 2834 | 2902 | if( pOut ){ |
| 2835 | 2903 | *pOut = pBlob; |
| 2836 | 2904 | } |
| 2837 | - /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/ | |
| 2838 | 2905 | return TH_OK; |
| 2839 | 2906 | error: |
| 2840 | 2907 | if( pBlob ){ |
| 2841 | 2908 | Th_Free( pMan->interp, pBlob ); |
| 2842 | 2909 | } |
| 2843 | 2910 | return TH_ERROR; |
| 2844 | 2911 | } |
| 2912 | + | |
| 2913 | + | |
| 2845 | 2914 | |
| 2846 | 2915 | Blob * Th_ob_pop( Th_Ob_Man * pMan ){ |
| 2847 | 2916 | if( pMan->cursor < 0 ){ |
| 2848 | 2917 | return NULL; |
| 2849 | 2918 | }else{ |
| @@ -2852,18 +2921,28 @@ | ||
| 2852 | 2921 | rc = pMan->aBuf[pMan->cursor]; |
| 2853 | 2922 | pMan->aBuf[pMan->cursor] = NULL; |
| 2854 | 2923 | pMan->interp->pVtab = pMan->aVtab[pMan->cursor]; |
| 2855 | 2924 | pMan->aVtab[pMan->cursor] = NULL; |
| 2856 | 2925 | if(-1 == --pMan->cursor){ |
| 2926 | + Th_Interp * interp = pMan->interp; | |
| 2857 | 2927 | Th_Free( pMan->interp, pMan->aBuf ); |
| 2858 | 2928 | Th_Free( pMan->interp, pMan->aVtab ); |
| 2859 | 2929 | *pMan = Th_Ob_Man_empty; |
| 2930 | + pMan->interp = interp; | |
| 2860 | 2931 | } |
| 2861 | - /*fprintf(stderr,"OB pop: %p level=%d\n", rc, pMan->cursor-1);*/ | |
| 2862 | 2932 | return rc; |
| 2863 | 2933 | } |
| 2864 | 2934 | } |
| 2935 | + | |
| 2936 | +void Th_ob_cleanup( Th_Ob_Man * man ){ | |
| 2937 | + Blob * b; | |
| 2938 | + while( (b = Th_ob_pop(man)) ){ | |
| 2939 | + blob_reset(b); | |
| 2940 | + Th_Free( man->interp, b ); | |
| 2941 | + } | |
| 2942 | +} | |
| 2943 | + | |
| 2865 | 2944 | |
| 2866 | 2945 | /* |
| 2867 | 2946 | ** TH Syntax: |
| 2868 | 2947 | ** |
| 2869 | 2948 | ** ob clean |
| @@ -2872,20 +2951,23 @@ | ||
| 2872 | 2951 | ** the buffering level. |
| 2873 | 2952 | */ |
| 2874 | 2953 | static int ob_clean_command( Th_Interp *interp, void *ctx, |
| 2875 | 2954 | int argc, const char **argv, int *argl |
| 2876 | 2955 | ){ |
| 2877 | - Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; | |
| 2956 | + const char doRc = ctx ? 1 : 0; | |
| 2957 | + Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp); | |
| 2878 | 2958 | Blob * b; |
| 2879 | 2959 | assert( pMan && (interp == pMan->interp) ); |
| 2880 | 2960 | b = pMan ? Th_ob_current(pMan) : NULL; |
| 2881 | 2961 | if(!b){ |
| 2882 | 2962 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2883 | 2963 | return TH_ERROR; |
| 2884 | 2964 | }else{ |
| 2885 | 2965 | blob_reset(b); |
| 2886 | - Th_SetResultInt( interp, 0 ); | |
| 2966 | + if( doRc ) { | |
| 2967 | + Th_SetResultInt( interp, 0 ); | |
| 2968 | + } | |
| 2887 | 2969 | return TH_OK; |
| 2888 | 2970 | } |
| 2889 | 2971 | } |
| 2890 | 2972 | |
| 2891 | 2973 | /* |
| @@ -2896,21 +2978,24 @@ | ||
| 2896 | 2978 | ** Erases any currently buffered contents and pops the current buffer |
| 2897 | 2979 | ** from the stack. |
| 2898 | 2980 | */ |
| 2899 | 2981 | static int ob_end_command( Th_Interp *interp, void *ctx, |
| 2900 | 2982 | int argc, const char **argv, int *argl ){ |
| 2901 | - Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; | |
| 2983 | + const char doRc = ctx ? 1 : 0; | |
| 2984 | + Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp); | |
| 2902 | 2985 | Blob * b; |
| 2903 | 2986 | assert( pMan && (interp == pMan->interp) ); |
| 2904 | 2987 | b = Th_ob_pop(pMan); |
| 2905 | 2988 | if(!b){ |
| 2906 | 2989 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2907 | 2990 | return TH_ERROR; |
| 2908 | 2991 | }else{ |
| 2909 | 2992 | blob_reset(b); |
| 2910 | 2993 | Th_Free( interp, b ); |
| 2911 | - Th_SetResultInt( interp, 0 ); | |
| 2994 | + if(doRc){ | |
| 2995 | + Th_SetResultInt( interp, 0 ); | |
| 2996 | + } | |
| 2912 | 2997 | return TH_OK; |
| 2913 | 2998 | } |
| 2914 | 2999 | } |
| 2915 | 3000 | |
| 2916 | 3001 | /* |
| @@ -2949,11 +3034,11 @@ | ||
| 2949 | 3034 | int subL = argl[argPos]; |
| 2950 | 3035 | /* "flush end" */ |
| 2951 | 3036 | if(th_strlen(sub)==3 && |
| 2952 | 3037 | ((0==memcmp("end", sub, subL) |
| 2953 | 3038 | || (0==memcmp("pop", sub, subL))))){ |
| 2954 | - rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1); | |
| 3039 | + rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1); | |
| 2955 | 3040 | } |
| 2956 | 3041 | } |
| 2957 | 3042 | Th_SetResultInt( interp, 0 ); |
| 2958 | 3043 | return rc; |
| 2959 | 3044 | } |
| @@ -2987,16 +3072,16 @@ | ||
| 2987 | 3072 | if(argc>argPos){ |
| 2988 | 3073 | sub = argv[argPos]; |
| 2989 | 3074 | subL = argl[argPos]; |
| 2990 | 3075 | /* "ob get clean" */ |
| 2991 | 3076 | if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){ |
| 2992 | - rc |= ob_clean_command(interp, ctx, argc-1, argv+1, argl+1); | |
| 3077 | + rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1); | |
| 2993 | 3078 | }/* "ob get end" */ |
| 2994 | 3079 | else if(!rc && th_strlen(sub)==3 && |
| 2995 | 3080 | ((0==memcmp("end", sub, subL)) |
| 2996 | 3081 | || (0==memcmp("pop", sub, subL)))){ |
| 2997 | - rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1); | |
| 3082 | + rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1); | |
| 2998 | 3083 | } |
| 2999 | 3084 | } |
| 3000 | 3085 | return rc; |
| 3001 | 3086 | } |
| 3002 | 3087 | } |
| @@ -3036,31 +3121,43 @@ | ||
| 3036 | 3121 | if( TH_OK != rc ){ |
| 3037 | 3122 | assert( NULL == b ); |
| 3038 | 3123 | return rc; |
| 3039 | 3124 | } |
| 3040 | 3125 | assert( NULL != b ); |
| 3041 | - /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/ | |
| 3042 | 3126 | Th_SetResultInt( interp, 1 + pMan->cursor ); |
| 3043 | 3127 | return TH_OK; |
| 3044 | 3128 | } |
| 3129 | + | |
| 3130 | +static void finalizerObMan( Th_Interp * interp, void * p ){ | |
| 3131 | + Th_Ob_Man * man = (Th_Ob_Man*)p; | |
| 3132 | + /*printf("finalizerObMan(%p,%p)\n", interp, p );*/ | |
| 3133 | + if(man){ | |
| 3134 | + assert( interp == man->interp ); | |
| 3135 | + Th_ob_cleanup( man ); | |
| 3136 | + if( man != &Th_Ob_Man_instance ){ | |
| 3137 | + Th_Free( interp, man ); | |
| 3138 | + } | |
| 3139 | + } | |
| 3140 | +} | |
| 3045 | 3141 | |
| 3046 | 3142 | /* |
| 3047 | 3143 | ** TH Syntax: |
| 3048 | 3144 | ** |
| 3049 | -** ob clean|end|flush|get|level|start | |
| 3145 | +** ob clean|(end|pop)|flush|get|level|(start|push) | |
| 3050 | 3146 | ** |
| 3051 | -** Runs the given subcommand. | |
| 3147 | +** Runs the given subcommand. Some subcommands have other subcommands | |
| 3148 | +** (see their docs for details). | |
| 3052 | 3149 | ** |
| 3053 | 3150 | */ |
| 3054 | 3151 | static int ob_cmd( |
| 3055 | 3152 | Th_Interp *interp, |
| 3056 | 3153 | void *ignored, |
| 3057 | 3154 | int argc, |
| 3058 | 3155 | const char **argv, |
| 3059 | 3156 | int *argl |
| 3060 | 3157 | ){ |
| 3061 | - static Th_Ob_Man * pMan = &Th_Ob_Man_instance; | |
| 3158 | + Th_Ob_Man * pMan = Th_ob_manager(interp); | |
| 3062 | 3159 | Th_SubCommand aSub[] = { |
| 3063 | 3160 | { "clean", ob_clean_command }, |
| 3064 | 3161 | { "end", ob_end_command }, |
| 3065 | 3162 | { "flush", ob_flush_command }, |
| 3066 | 3163 | { "get", ob_get_command }, |
| @@ -3068,29 +3165,34 @@ | ||
| 3068 | 3165 | { "pop", ob_end_command }, |
| 3069 | 3166 | { "push", ob_start_command }, |
| 3070 | 3167 | { "start", ob_start_command }, |
| 3071 | 3168 | { 0, 0 } |
| 3072 | 3169 | }; |
| 3073 | - if(NULL == pMan->interp){ | |
| 3074 | - pMan->interp = interp; | |
| 3075 | - /* | |
| 3076 | - FIXME: add rudamentary at-finalization GC to Th_Interp and clean | |
| 3077 | - this up there. We currently leak only if the client does not | |
| 3078 | - close all buffering levels properly. | |
| 3079 | - */ | |
| 3080 | - } | |
| 3170 | + assert(NULL != pMan && pMan->interp==interp); | |
| 3081 | 3171 | return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); |
| 3082 | - | |
| 3083 | 3172 | } |
| 3084 | 3173 | |
| 3085 | 3174 | int th_register_ob(Th_Interp * interp){ |
| 3175 | + int rc; | |
| 3086 | 3176 | static Th_Command_Reg aCommand[] = { |
| 3087 | 3177 | {"ob", ob_cmd, 0}, |
| 3088 | 3178 | {0,0,0} |
| 3089 | 3179 | }; |
| 3090 | - return Th_register_commands( interp, aCommand ); | |
| 3180 | + rc = Th_register_commands( interp, aCommand ); | |
| 3181 | + if(NULL == Th_ob_manager(interp)){ | |
| 3182 | + Th_Ob_Man * pMan; | |
| 3183 | + pMan = 1 | |
| 3184 | + ? &Th_Ob_Man_instance | |
| 3185 | + : Th_Malloc(interp, sizeof(Th_Ob_Man)); | |
| 3186 | + /* *pMan = Th_Ob_Man_empty;*/ | |
| 3187 | + pMan->interp = interp; | |
| 3188 | + Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan ); | |
| 3189 | + assert( NULL != Th_ob_manager(interp) ); | |
| 3190 | + } | |
| 3191 | + return rc; | |
| 3091 | 3192 | } |
| 3092 | 3193 | |
| 3093 | 3194 | #undef Th_Ob_Man_empty_m |
| 3195 | +#undef Th_Ob_Man_KEY | |
| 3094 | 3196 | #endif |
| 3095 | 3197 | /* end TH_USE_OUTBUF */ |
| 3096 | 3198 | |
| 3097 | 3199 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -18,26 +18,32 @@ | |
| 18 | |
| 19 | typedef struct Th_Command Th_Command; |
| 20 | typedef struct Th_Frame Th_Frame; |
| 21 | typedef struct Th_Variable Th_Variable; |
| 22 | |
| 23 | /* |
| 24 | ** Interpreter structure. |
| 25 | */ |
| 26 | struct Th_Interp { |
| 27 | Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ |
| 28 | char *zResult; /* Current interpreter result (Th_Malloc()ed) */ |
| 29 | int nResult; /* number of bytes in zResult */ |
| 30 | Th_Hash *paCmd; /* Table of registered commands */ |
| 31 | Th_Frame *pFrame; /* Current execution frame */ |
| 32 | int isListMode; /* True if thSplitList() should operate in "list" mode */ |
| 33 | #ifdef TH_USE_SQLITE |
| 34 | struct { |
| 35 | sqlite3_stmt ** aStmt; |
| 36 | int nStmt; |
| 37 | int rc; |
| 38 | } stmt; |
| 39 | #endif |
| 40 | }; |
| 41 | |
| 42 | /* |
| 43 | ** Each TH command registered using Th_CreateCommand() is represented |
| @@ -121,10 +127,11 @@ | |
| 121 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 122 | static void thPopFrame(Th_Interp*); |
| 123 | |
| 124 | static void thFreeVariable(Th_HashEntry*, void*); |
| 125 | static void thFreeCommand(Th_HashEntry*, void*); |
| 126 | |
| 127 | /* |
| 128 | ** The following are used by both the expression and language parsers. |
| 129 | ** Given that the start of the input string (z, n) is a language |
| 130 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -303,10 +310,26 @@ | |
| 303 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 304 | } |
| 305 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 306 | pEntry->pData = 0; |
| 307 | } |
| 308 | |
| 309 | /* |
| 310 | ** Push a new frame onto the stack. |
| 311 | */ |
| 312 | static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ |
| @@ -1682,10 +1705,17 @@ | |
| 1682 | */ |
| 1683 | void Th_DeleteInterp(Th_Interp *interp){ |
| 1684 | assert(interp->pFrame); |
| 1685 | assert(0==interp->pFrame->pCaller); |
| 1686 | |
| 1687 | /* Delete the contents of the global frame. */ |
| 1688 | thPopFrame(interp); |
| 1689 | |
| 1690 | /* Delete any result currently stored in the interpreter. */ |
| 1691 | Th_SetResult(interp, 0, 0); |
| @@ -2332,11 +2362,11 @@ | |
| 2332 | ** a null pointer. |
| 2333 | */ |
| 2334 | int th_strlen(const char *zStr){ |
| 2335 | int n = 0; |
| 2336 | if( zStr ){ |
| 2337 | while( zStr[n] ) n++; |
| 2338 | } |
| 2339 | return n; |
| 2340 | } |
| 2341 | |
| 2342 | /* Whitespace characters: |
| @@ -2697,10 +2727,40 @@ | |
| 2697 | |
| 2698 | *z = '\0'; |
| 2699 | return Th_SetResult(interp, zBuf, -1); |
| 2700 | } |
| 2701 | |
| 2702 | |
| 2703 | #ifdef TH_USE_SQLITE |
| 2704 | int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){ |
| 2705 | int i, x; |
| 2706 | sqlite3_stmt * s; |
| @@ -2753,18 +2813,27 @@ | |
| 2753 | /* Reminder: the ob code "really" belongs in th_lang.c, |
| 2754 | but we need access to Th_Interp internals in order to |
| 2755 | swap out Th_Vtab parts for purposes of stacking layers |
| 2756 | of buffers. |
| 2757 | */ |
| 2758 | #define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL } |
| 2759 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2760 | static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; |
| 2761 | |
| 2762 | Th_Ob_Man * Th_ob_manager(Th_Interp *ignored){ |
| 2763 | return &Th_Ob_Man_instance; |
| 2764 | } |
| 2765 | |
| 2766 | |
| 2767 | Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2768 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2769 | } |
| 2770 | |
| @@ -2805,11 +2874,10 @@ | |
| 2805 | x = pMan->nBuf + 5; |
| 2806 | if( pMan->cursor >= x ) { |
| 2807 | assert( 0 && "This really should not happen." ); |
| 2808 | x = pMan->cursor + 5; |
| 2809 | } |
| 2810 | /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/ |
| 2811 | void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); |
| 2812 | if(NULL==re){ |
| 2813 | goto error; |
| 2814 | } |
| 2815 | pMan->aBuf = (Blob **)re; |
| @@ -2832,18 +2900,19 @@ | |
| 2832 | pMan->interp->pVtab = &Th_Vtab_Ob; |
| 2833 | Th_Vtab_Ob.out.pState = pMan; |
| 2834 | if( pOut ){ |
| 2835 | *pOut = pBlob; |
| 2836 | } |
| 2837 | /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/ |
| 2838 | return TH_OK; |
| 2839 | error: |
| 2840 | if( pBlob ){ |
| 2841 | Th_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{ |
| @@ -2852,18 +2921,28 @@ | |
| 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 | Th_Free( pMan->interp, pMan->aBuf ); |
| 2858 | Th_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 | /* |
| 2867 | ** TH Syntax: |
| 2868 | ** |
| 2869 | ** ob clean |
| @@ -2872,20 +2951,23 @@ | |
| 2872 | ** the buffering level. |
| 2873 | */ |
| 2874 | static int ob_clean_command( Th_Interp *interp, void *ctx, |
| 2875 | int argc, const char **argv, int *argl |
| 2876 | ){ |
| 2877 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2878 | Blob * b; |
| 2879 | assert( pMan && (interp == pMan->interp) ); |
| 2880 | b = pMan ? Th_ob_current(pMan) : NULL; |
| 2881 | if(!b){ |
| 2882 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2883 | return TH_ERROR; |
| 2884 | }else{ |
| 2885 | blob_reset(b); |
| 2886 | Th_SetResultInt( interp, 0 ); |
| 2887 | return TH_OK; |
| 2888 | } |
| 2889 | } |
| 2890 | |
| 2891 | /* |
| @@ -2896,21 +2978,24 @@ | |
| 2896 | ** Erases any currently buffered contents and pops the current buffer |
| 2897 | ** from the stack. |
| 2898 | */ |
| 2899 | static int ob_end_command( Th_Interp *interp, void *ctx, |
| 2900 | int argc, const char **argv, int *argl ){ |
| 2901 | Th_Ob_Man * pMan = (Th_Ob_Man *)ctx; |
| 2902 | Blob * b; |
| 2903 | assert( pMan && (interp == pMan->interp) ); |
| 2904 | b = Th_ob_pop(pMan); |
| 2905 | if(!b){ |
| 2906 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2907 | return TH_ERROR; |
| 2908 | }else{ |
| 2909 | blob_reset(b); |
| 2910 | Th_Free( interp, b ); |
| 2911 | Th_SetResultInt( interp, 0 ); |
| 2912 | return TH_OK; |
| 2913 | } |
| 2914 | } |
| 2915 | |
| 2916 | /* |
| @@ -2949,11 +3034,11 @@ | |
| 2949 | int subL = argl[argPos]; |
| 2950 | /* "flush end" */ |
| 2951 | if(th_strlen(sub)==3 && |
| 2952 | ((0==memcmp("end", sub, subL) |
| 2953 | || (0==memcmp("pop", sub, subL))))){ |
| 2954 | rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1); |
| 2955 | } |
| 2956 | } |
| 2957 | Th_SetResultInt( interp, 0 ); |
| 2958 | return rc; |
| 2959 | } |
| @@ -2987,16 +3072,16 @@ | |
| 2987 | if(argc>argPos){ |
| 2988 | sub = argv[argPos]; |
| 2989 | subL = argl[argPos]; |
| 2990 | /* "ob get clean" */ |
| 2991 | if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){ |
| 2992 | rc |= ob_clean_command(interp, ctx, argc-1, argv+1, argl+1); |
| 2993 | }/* "ob get end" */ |
| 2994 | else if(!rc && th_strlen(sub)==3 && |
| 2995 | ((0==memcmp("end", sub, subL)) |
| 2996 | || (0==memcmp("pop", sub, subL)))){ |
| 2997 | rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1); |
| 2998 | } |
| 2999 | } |
| 3000 | return rc; |
| 3001 | } |
| 3002 | } |
| @@ -3036,31 +3121,43 @@ | |
| 3036 | if( TH_OK != rc ){ |
| 3037 | assert( NULL == b ); |
| 3038 | return rc; |
| 3039 | } |
| 3040 | assert( NULL != b ); |
| 3041 | /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/ |
| 3042 | Th_SetResultInt( interp, 1 + pMan->cursor ); |
| 3043 | return TH_OK; |
| 3044 | } |
| 3045 | |
| 3046 | /* |
| 3047 | ** TH Syntax: |
| 3048 | ** |
| 3049 | ** ob clean|end|flush|get|level|start |
| 3050 | ** |
| 3051 | ** Runs the given subcommand. |
| 3052 | ** |
| 3053 | */ |
| 3054 | static int ob_cmd( |
| 3055 | Th_Interp *interp, |
| 3056 | void *ignored, |
| 3057 | int argc, |
| 3058 | const char **argv, |
| 3059 | int *argl |
| 3060 | ){ |
| 3061 | static Th_Ob_Man * pMan = &Th_Ob_Man_instance; |
| 3062 | Th_SubCommand aSub[] = { |
| 3063 | { "clean", ob_clean_command }, |
| 3064 | { "end", ob_end_command }, |
| 3065 | { "flush", ob_flush_command }, |
| 3066 | { "get", ob_get_command }, |
| @@ -3068,29 +3165,34 @@ | |
| 3068 | { "pop", ob_end_command }, |
| 3069 | { "push", ob_start_command }, |
| 3070 | { "start", ob_start_command }, |
| 3071 | { 0, 0 } |
| 3072 | }; |
| 3073 | if(NULL == pMan->interp){ |
| 3074 | pMan->interp = interp; |
| 3075 | /* |
| 3076 | FIXME: add rudamentary at-finalization GC to Th_Interp and clean |
| 3077 | this up there. We currently leak only if the client does not |
| 3078 | close all buffering levels properly. |
| 3079 | */ |
| 3080 | } |
| 3081 | return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); |
| 3082 | |
| 3083 | } |
| 3084 | |
| 3085 | int th_register_ob(Th_Interp * interp){ |
| 3086 | static Th_Command_Reg aCommand[] = { |
| 3087 | {"ob", ob_cmd, 0}, |
| 3088 | {0,0,0} |
| 3089 | }; |
| 3090 | return Th_register_commands( interp, aCommand ); |
| 3091 | } |
| 3092 | |
| 3093 | #undef Th_Ob_Man_empty_m |
| 3094 | #endif |
| 3095 | /* end TH_USE_OUTBUF */ |
| 3096 | |
| 3097 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -18,26 +18,32 @@ | |
| 18 | |
| 19 | typedef struct Th_Command Th_Command; |
| 20 | typedef struct Th_Frame Th_Frame; |
| 21 | typedef struct Th_Variable Th_Variable; |
| 22 | |
| 23 | struct Th_GcEntry { |
| 24 | void * pData; |
| 25 | void (*xDel)( Th_Interp *, void * ); |
| 26 | }; |
| 27 | typedef struct Th_GcEntry Th_GcEntry; |
| 28 | |
| 29 | /* |
| 30 | ** Interpreter structure. |
| 31 | */ |
| 32 | struct Th_Interp { |
| 33 | Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ |
| 34 | char *zResult; /* Current interpreter result (Th_Malloc()ed) */ |
| 35 | int nResult; /* number of bytes in zResult */ |
| 36 | Th_Hash *paCmd; /* Table of registered commands */ |
| 37 | Th_Frame *pFrame; /* Current execution frame */ |
| 38 | int isListMode; /* True if thSplitList() should operate in "list" mode */ |
| 39 | Th_Hash * paGc; /* holds client-provided data owned by this object */ |
| 40 | #ifdef TH_USE_SQLITE |
| 41 | struct { |
| 42 | sqlite3_stmt ** aStmt; |
| 43 | int nStmt; |
| 44 | } stmt; /* list of prepared statements */ |
| 45 | #endif |
| 46 | }; |
| 47 | |
| 48 | /* |
| 49 | ** Each TH command registered using Th_CreateCommand() is represented |
| @@ -121,10 +127,11 @@ | |
| 127 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 128 | static void thPopFrame(Th_Interp*); |
| 129 | |
| 130 | static void thFreeVariable(Th_HashEntry*, void*); |
| 131 | static void thFreeCommand(Th_HashEntry*, void*); |
| 132 | static void thFreeGc(Th_HashEntry*, void*); |
| 133 | |
| 134 | /* |
| 135 | ** The following are used by both the expression and language parsers. |
| 136 | ** Given that the start of the input string (z, n) is a language |
| 137 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -303,10 +310,26 @@ | |
| 310 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 311 | } |
| 312 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 313 | pEntry->pData = 0; |
| 314 | } |
| 315 | |
| 316 | /* |
| 317 | ** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees |
| 318 | ** pEntry->pData but not pEntry. |
| 319 | */ |
| 320 | static void thFreeGc(Th_HashEntry *pEntry, void *pContext){ |
| 321 | Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData; |
| 322 | if(gc){ |
| 323 | if( gc->xDel ){ |
| 324 | gc->xDel( (Th_Interp*)pContext, gc->pData ); |
| 325 | } |
| 326 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 327 | pEntry->pData = 0; |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | |
| 332 | /* |
| 333 | ** Push a new frame onto the stack. |
| 334 | */ |
| 335 | static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ |
| @@ -1682,10 +1705,17 @@ | |
| 1705 | */ |
| 1706 | void Th_DeleteInterp(Th_Interp *interp){ |
| 1707 | assert(interp->pFrame); |
| 1708 | assert(0==interp->pFrame->pCaller); |
| 1709 | |
| 1710 | /* Delete any client-side gc entries first. */ |
| 1711 | if( interp->paGc ){ |
| 1712 | Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp); |
| 1713 | Th_HashDelete(interp, interp->paGc); |
| 1714 | interp->paGc = NULL; |
| 1715 | } |
| 1716 | |
| 1717 | /* Delete the contents of the global frame. */ |
| 1718 | thPopFrame(interp); |
| 1719 | |
| 1720 | /* Delete any result currently stored in the interpreter. */ |
| 1721 | Th_SetResult(interp, 0, 0); |
| @@ -2332,11 +2362,11 @@ | |
| 2362 | ** a null pointer. |
| 2363 | */ |
| 2364 | int th_strlen(const char *zStr){ |
| 2365 | int n = 0; |
| 2366 | if( zStr ){ |
| 2367 | while( zStr[n] ) ++n; |
| 2368 | } |
| 2369 | return n; |
| 2370 | } |
| 2371 | |
| 2372 | /* Whitespace characters: |
| @@ -2697,10 +2727,40 @@ | |
| 2727 | |
| 2728 | *z = '\0'; |
| 2729 | return Th_SetResult(interp, zBuf, -1); |
| 2730 | } |
| 2731 | |
| 2732 | |
| 2733 | int Th_Data_Set( Th_Interp * interp, char const * key, |
| 2734 | void * pData, |
| 2735 | void (*finalizer)( Th_Interp *, void * ) ){ |
| 2736 | Th_HashEntry * pEnt; |
| 2737 | Th_GcEntry * pGc; |
| 2738 | if(NULL == interp->paGc){ |
| 2739 | interp->paGc = Th_HashNew(interp); |
| 2740 | assert(NULL != interp->paGc); |
| 2741 | if(!interp->paGc){ |
| 2742 | return TH_ERROR; |
| 2743 | } |
| 2744 | } |
| 2745 | pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1); |
| 2746 | if( pEnt->pData ){ |
| 2747 | thFreeGc( pEnt, interp ); |
| 2748 | } |
| 2749 | assert( NULL == pEnt->pData ); |
| 2750 | pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry)); |
| 2751 | pGc->pData = pData; |
| 2752 | pGc->xDel = finalizer; |
| 2753 | return 0; |
| 2754 | |
| 2755 | } |
| 2756 | void * Th_Data_Get( Th_Interp * interp, char const * key ){ |
| 2757 | Th_HashEntry * e = interp->paGc |
| 2758 | ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0) |
| 2759 | : NULL; |
| 2760 | return e ? e->pData : NULL; |
| 2761 | } |
| 2762 | |
| 2763 | #ifdef TH_USE_SQLITE |
| 2764 | int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){ |
| 2765 | int i, x; |
| 2766 | sqlite3_stmt * s; |
| @@ -2753,18 +2813,27 @@ | |
| 2813 | /* Reminder: the ob code "really" belongs in th_lang.c, |
| 2814 | but we need access to Th_Interp internals in order to |
| 2815 | swap out Th_Vtab parts for purposes of stacking layers |
| 2816 | of buffers. |
| 2817 | */ |
| 2818 | #define Th_Ob_Man_empty_m { \ |
| 2819 | NULL/*aBuf*/, \ |
| 2820 | 0/*nBuf*/, \ |
| 2821 | -1/*cursor*/, \ |
| 2822 | NULL/*interp*/, \ |
| 2823 | NULL/*aVtab*/ \ |
| 2824 | } |
| 2825 | static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; |
| 2826 | static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; |
| 2827 | #define Th_Ob_Man_KEY "Th_Ob_Man" |
| 2828 | Th_Ob_Man * Th_ob_manager(Th_Interp *interp){ |
| 2829 | void * rc = Th_Data_Get(interp, Th_Ob_Man_KEY ); |
| 2830 | return rc |
| 2831 | ? ((Th_GcEntry*)rc)->pData |
| 2832 | : NULL; |
| 2833 | } |
| 2834 | |
| 2835 | |
| 2836 | Blob * Th_ob_current( Th_Ob_Man * pMan ){ |
| 2837 | return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; |
| 2838 | } |
| 2839 | |
| @@ -2805,11 +2874,10 @@ | |
| 2874 | x = pMan->nBuf + 5; |
| 2875 | if( pMan->cursor >= x ) { |
| 2876 | assert( 0 && "This really should not happen." ); |
| 2877 | x = pMan->cursor + 5; |
| 2878 | } |
| 2879 | void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); |
| 2880 | if(NULL==re){ |
| 2881 | goto error; |
| 2882 | } |
| 2883 | pMan->aBuf = (Blob **)re; |
| @@ -2832,18 +2900,19 @@ | |
| 2900 | pMan->interp->pVtab = &Th_Vtab_Ob; |
| 2901 | Th_Vtab_Ob.out.pState = pMan; |
| 2902 | if( pOut ){ |
| 2903 | *pOut = pBlob; |
| 2904 | } |
| 2905 | return TH_OK; |
| 2906 | error: |
| 2907 | if( pBlob ){ |
| 2908 | Th_Free( pMan->interp, pBlob ); |
| 2909 | } |
| 2910 | return TH_ERROR; |
| 2911 | } |
| 2912 | |
| 2913 | |
| 2914 | |
| 2915 | Blob * Th_ob_pop( Th_Ob_Man * pMan ){ |
| 2916 | if( pMan->cursor < 0 ){ |
| 2917 | return NULL; |
| 2918 | }else{ |
| @@ -2852,18 +2921,28 @@ | |
| 2921 | rc = pMan->aBuf[pMan->cursor]; |
| 2922 | pMan->aBuf[pMan->cursor] = NULL; |
| 2923 | pMan->interp->pVtab = pMan->aVtab[pMan->cursor]; |
| 2924 | pMan->aVtab[pMan->cursor] = NULL; |
| 2925 | if(-1 == --pMan->cursor){ |
| 2926 | Th_Interp * interp = pMan->interp; |
| 2927 | Th_Free( pMan->interp, pMan->aBuf ); |
| 2928 | Th_Free( pMan->interp, pMan->aVtab ); |
| 2929 | *pMan = Th_Ob_Man_empty; |
| 2930 | pMan->interp = interp; |
| 2931 | } |
| 2932 | return rc; |
| 2933 | } |
| 2934 | } |
| 2935 | |
| 2936 | void Th_ob_cleanup( Th_Ob_Man * man ){ |
| 2937 | Blob * b; |
| 2938 | while( (b = Th_ob_pop(man)) ){ |
| 2939 | blob_reset(b); |
| 2940 | Th_Free( man->interp, b ); |
| 2941 | } |
| 2942 | } |
| 2943 | |
| 2944 | |
| 2945 | /* |
| 2946 | ** TH Syntax: |
| 2947 | ** |
| 2948 | ** ob clean |
| @@ -2872,20 +2951,23 @@ | |
| 2951 | ** the buffering level. |
| 2952 | */ |
| 2953 | static int ob_clean_command( Th_Interp *interp, void *ctx, |
| 2954 | int argc, const char **argv, int *argl |
| 2955 | ){ |
| 2956 | const char doRc = ctx ? 1 : 0; |
| 2957 | Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp); |
| 2958 | Blob * b; |
| 2959 | assert( pMan && (interp == pMan->interp) ); |
| 2960 | b = pMan ? Th_ob_current(pMan) : NULL; |
| 2961 | if(!b){ |
| 2962 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2963 | return TH_ERROR; |
| 2964 | }else{ |
| 2965 | blob_reset(b); |
| 2966 | if( doRc ) { |
| 2967 | Th_SetResultInt( interp, 0 ); |
| 2968 | } |
| 2969 | return TH_OK; |
| 2970 | } |
| 2971 | } |
| 2972 | |
| 2973 | /* |
| @@ -2896,21 +2978,24 @@ | |
| 2978 | ** Erases any currently buffered contents and pops the current buffer |
| 2979 | ** from the stack. |
| 2980 | */ |
| 2981 | static int ob_end_command( Th_Interp *interp, void *ctx, |
| 2982 | int argc, const char **argv, int *argl ){ |
| 2983 | const char doRc = ctx ? 1 : 0; |
| 2984 | Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp); |
| 2985 | Blob * b; |
| 2986 | assert( pMan && (interp == pMan->interp) ); |
| 2987 | b = Th_ob_pop(pMan); |
| 2988 | if(!b){ |
| 2989 | Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); |
| 2990 | return TH_ERROR; |
| 2991 | }else{ |
| 2992 | blob_reset(b); |
| 2993 | Th_Free( interp, b ); |
| 2994 | if(doRc){ |
| 2995 | Th_SetResultInt( interp, 0 ); |
| 2996 | } |
| 2997 | return TH_OK; |
| 2998 | } |
| 2999 | } |
| 3000 | |
| 3001 | /* |
| @@ -2949,11 +3034,11 @@ | |
| 3034 | int subL = argl[argPos]; |
| 3035 | /* "flush end" */ |
| 3036 | if(th_strlen(sub)==3 && |
| 3037 | ((0==memcmp("end", sub, subL) |
| 3038 | || (0==memcmp("pop", sub, subL))))){ |
| 3039 | rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1); |
| 3040 | } |
| 3041 | } |
| 3042 | Th_SetResultInt( interp, 0 ); |
| 3043 | return rc; |
| 3044 | } |
| @@ -2987,16 +3072,16 @@ | |
| 3072 | if(argc>argPos){ |
| 3073 | sub = argv[argPos]; |
| 3074 | subL = argl[argPos]; |
| 3075 | /* "ob get clean" */ |
| 3076 | if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){ |
| 3077 | rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1); |
| 3078 | }/* "ob get end" */ |
| 3079 | else if(!rc && th_strlen(sub)==3 && |
| 3080 | ((0==memcmp("end", sub, subL)) |
| 3081 | || (0==memcmp("pop", sub, subL)))){ |
| 3082 | rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1); |
| 3083 | } |
| 3084 | } |
| 3085 | return rc; |
| 3086 | } |
| 3087 | } |
| @@ -3036,31 +3121,43 @@ | |
| 3121 | if( TH_OK != rc ){ |
| 3122 | assert( NULL == b ); |
| 3123 | return rc; |
| 3124 | } |
| 3125 | assert( NULL != b ); |
| 3126 | Th_SetResultInt( interp, 1 + pMan->cursor ); |
| 3127 | return TH_OK; |
| 3128 | } |
| 3129 | |
| 3130 | static void finalizerObMan( Th_Interp * interp, void * p ){ |
| 3131 | Th_Ob_Man * man = (Th_Ob_Man*)p; |
| 3132 | /*printf("finalizerObMan(%p,%p)\n", interp, p );*/ |
| 3133 | if(man){ |
| 3134 | assert( interp == man->interp ); |
| 3135 | Th_ob_cleanup( man ); |
| 3136 | if( man != &Th_Ob_Man_instance ){ |
| 3137 | Th_Free( interp, man ); |
| 3138 | } |
| 3139 | } |
| 3140 | } |
| 3141 | |
| 3142 | /* |
| 3143 | ** TH Syntax: |
| 3144 | ** |
| 3145 | ** ob clean|(end|pop)|flush|get|level|(start|push) |
| 3146 | ** |
| 3147 | ** Runs the given subcommand. Some subcommands have other subcommands |
| 3148 | ** (see their docs for details). |
| 3149 | ** |
| 3150 | */ |
| 3151 | static int ob_cmd( |
| 3152 | Th_Interp *interp, |
| 3153 | void *ignored, |
| 3154 | int argc, |
| 3155 | const char **argv, |
| 3156 | int *argl |
| 3157 | ){ |
| 3158 | Th_Ob_Man * pMan = Th_ob_manager(interp); |
| 3159 | Th_SubCommand aSub[] = { |
| 3160 | { "clean", ob_clean_command }, |
| 3161 | { "end", ob_end_command }, |
| 3162 | { "flush", ob_flush_command }, |
| 3163 | { "get", ob_get_command }, |
| @@ -3068,29 +3165,34 @@ | |
| 3165 | { "pop", ob_end_command }, |
| 3166 | { "push", ob_start_command }, |
| 3167 | { "start", ob_start_command }, |
| 3168 | { 0, 0 } |
| 3169 | }; |
| 3170 | assert(NULL != pMan && pMan->interp==interp); |
| 3171 | return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); |
| 3172 | } |
| 3173 | |
| 3174 | int th_register_ob(Th_Interp * interp){ |
| 3175 | int rc; |
| 3176 | static Th_Command_Reg aCommand[] = { |
| 3177 | {"ob", ob_cmd, 0}, |
| 3178 | {0,0,0} |
| 3179 | }; |
| 3180 | rc = Th_register_commands( interp, aCommand ); |
| 3181 | if(NULL == Th_ob_manager(interp)){ |
| 3182 | Th_Ob_Man * pMan; |
| 3183 | pMan = 1 |
| 3184 | ? &Th_Ob_Man_instance |
| 3185 | : Th_Malloc(interp, sizeof(Th_Ob_Man)); |
| 3186 | /* *pMan = Th_Ob_Man_empty;*/ |
| 3187 | pMan->interp = interp; |
| 3188 | Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan ); |
| 3189 | assert( NULL != Th_ob_manager(interp) ); |
| 3190 | } |
| 3191 | return rc; |
| 3192 | } |
| 3193 | |
| 3194 | #undef Th_Ob_Man_empty_m |
| 3195 | #undef Th_Ob_Man_KEY |
| 3196 | #endif |
| 3197 | /* end TH_USE_OUTBUF */ |
| 3198 | |
| 3199 |
M
src/th.h
+25
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -269,10 +269,35 @@ | ||
| 269 | 269 | #define Th_Render_Flags_DEFAULT 0 |
| 270 | 270 | #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1) |
| 271 | 271 | /*};*/ |
| 272 | 272 | |
| 273 | 273 | int Th_Render(const char *z, int flags); |
| 274 | + | |
| 275 | +/* | |
| 276 | +** Adds a piece of memory to the given interpreter, such that: | |
| 277 | +** | |
| 278 | +** a) it will be cleaned up when the interpreter is destroyed, by | |
| 279 | +** calling finalizer(interp, pData). The finalizer may be NULL. | |
| 280 | +** Cleanup happens in an unspecified/unpredictable order. | |
| 281 | +** | |
| 282 | +** b) it can be fetched via Th_Data_Get(). | |
| 283 | +** | |
| 284 | +** If a given key is added more than once then any previous | |
| 285 | +** entry is cleaned up before adding it. | |
| 286 | +** | |
| 287 | +** Returns 0 on success, non-0 on allocation error. | |
| 288 | +*/ | |
| 289 | +int Th_Data_Set( Th_Interp * interp, char const * key, | |
| 290 | + void * pData, | |
| 291 | + void (*finalizer)( Th_Interp *, void * ) ); | |
| 292 | + | |
| 293 | +/* | |
| 294 | +** Fetches data added via Th_Data_Set(), or NULL if no data | |
| 295 | +** has been associated with the given key. | |
| 296 | +*/ | |
| 297 | +void * Th_Data_Get( Th_Interp * interp, char const * key ); | |
| 298 | + | |
| 274 | 299 | |
| 275 | 300 | /* |
| 276 | 301 | ** Registers a list of commands with the interpreter. pList must be a non-NULL |
| 277 | 302 | ** pointer to an array of Th_Command_Reg objects, the last one of which MUST |
| 278 | 303 | ** have a NULL zName field (that is the end-of-list marker). |
| 279 | 304 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -269,10 +269,35 @@ | |
| 269 | #define Th_Render_Flags_DEFAULT 0 |
| 270 | #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1) |
| 271 | /*};*/ |
| 272 | |
| 273 | int Th_Render(const char *z, int flags); |
| 274 | |
| 275 | /* |
| 276 | ** Registers a list of commands with the interpreter. pList must be a non-NULL |
| 277 | ** pointer to an array of Th_Command_Reg objects, the last one of which MUST |
| 278 | ** have a NULL zName field (that is the end-of-list marker). |
| 279 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -269,10 +269,35 @@ | |
| 269 | #define Th_Render_Flags_DEFAULT 0 |
| 270 | #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1) |
| 271 | /*};*/ |
| 272 | |
| 273 | int Th_Render(const char *z, int flags); |
| 274 | |
| 275 | /* |
| 276 | ** Adds a piece of memory to the given interpreter, such that: |
| 277 | ** |
| 278 | ** a) it will be cleaned up when the interpreter is destroyed, by |
| 279 | ** calling finalizer(interp, pData). The finalizer may be NULL. |
| 280 | ** Cleanup happens in an unspecified/unpredictable order. |
| 281 | ** |
| 282 | ** b) it can be fetched via Th_Data_Get(). |
| 283 | ** |
| 284 | ** If a given key is added more than once then any previous |
| 285 | ** entry is cleaned up before adding it. |
| 286 | ** |
| 287 | ** Returns 0 on success, non-0 on allocation error. |
| 288 | */ |
| 289 | int Th_Data_Set( Th_Interp * interp, char const * key, |
| 290 | void * pData, |
| 291 | void (*finalizer)( Th_Interp *, void * ) ); |
| 292 | |
| 293 | /* |
| 294 | ** Fetches data added via Th_Data_Set(), or NULL if no data |
| 295 | ** has been associated with the given key. |
| 296 | */ |
| 297 | void * Th_Data_Get( Th_Interp * interp, char const * key ); |
| 298 | |
| 299 | |
| 300 | /* |
| 301 | ** Registers a list of commands with the interpreter. pList must be a non-NULL |
| 302 | ** pointer to an array of Th_Command_Reg objects, the last one of which MUST |
| 303 | ** have a NULL zName field (that is the end-of-list marker). |
| 304 |