Fossil SCM

Moved /fileedit and friends to fileedit.c.

stephan 2020-05-03 14:57 checkin-without-checkout
Commit 6cdb091adb503f8e73cd9524838d17694dc77fb4af6ed7d695068e48223df1e9
+1 -1304
--- src/checkin.c
+++ src/checkin.c
@@ -1406,11 +1406,11 @@
14061406
** Returns true if the checkin identified by the first parameter is
14071407
** older than the given (valid) date/time string, else returns false.
14081408
** Also returns true if rid does not refer to a checkin, but it is not
14091409
** intended to be used for that case.
14101410
*/
1411
-static int checkin_is_younger(
1411
+int checkin_is_younger(
14121412
int rid, /* The record ID of the ancestor */
14131413
const char *zDate /* Date & time of the current check-in */
14141414
){
14151415
return db_exists(
14161416
"SELECT 1 FROM event"
@@ -2694,1308 +2694,5 @@
26942694
fossil_print("**** warning: a fork has occurred *****\n");
26952695
}else{
26962696
leaf_ambiguity_warning(nvid,nvid);
26972697
}
26982698
}
2699
-
2700
-/*
2701
-** State for the "mini-checkin" infrastructure, which enables the
2702
-** ability to commit changes to a single file without a checkout
2703
-** db, e.g. for use via an HTTP request.
2704
-**
2705
-** Use CheckinMiniInfo_init() to cleanly initialize one to a known
2706
-** valid/empty default state.
2707
-**
2708
-** Memory for all non-const (char *) members is owned by the
2709
-** CheckinMiniInfo instance and is freed by CheckinMiniInfo_cleanup().
2710
-*/
2711
-struct CheckinMiniInfo {
2712
- Manifest * pParent; /* parent checkin. Memory is owned by this
2713
- object. */
2714
- char *zParentUuid; /* Full UUID of pParent */
2715
- char *zFilename; /* Name of single file to commit. Must be
2716
- relative to the top of the repo. */
2717
- Blob fileContent; /* Content of file referred to by zFilename. */
2718
- Blob fileHash; /* Hash of this->fileContent, using the repo's
2719
- preferred hash method. */
2720
- Blob comment; /* Check-in comment text */
2721
- char *zMimetype; /* Mimetype of comment. May be NULL */
2722
- char *zUser; /* User name */
2723
- char *zDate; /* Optionally force this date string (anything
2724
- supported by date_in_standard_format()).
2725
- Maybe be NULL. */
2726
- Blob *pMfOut; /* If not NULL, checkin_mini() will write a
2727
- copy of the generated manifest here. This
2728
- memory is NOT owned by CheckinMiniInfo. */
2729
- int filePerm; /* Permissions (via file_perm()) of the input
2730
- file. We need to store this before calling
2731
- checkin_mini() because the real input file
2732
- name may differ from the repo-centric
2733
- this->zFilename, and checkin_mini() requires
2734
- the permissions of the original file. For
2735
- web commits, set this to PERM_REG or (when
2736
- editing executable scripts) PERM_EXE before
2737
- calling checkin_mini(). */
2738
- int flags; /* Bitmask of fossil_cimini_flags. */
2739
-};
2740
-typedef struct CheckinMiniInfo CheckinMiniInfo;
2741
-
2742
-/*
2743
-** CheckinMiniInfo::flags values.
2744
-*/
2745
-enum fossil_cimini_flags {
2746
-CIMINI_NONE = 0,
2747
-/*
2748
-** Tells checkin_mini() to use dry-run mode.
2749
-*/
2750
-CIMINI_DRY_RUN = 1,
2751
-/*
2752
-** Tells checkin_mini() to allow forking from a non-leaf commit.
2753
-*/
2754
-CIMINI_ALLOW_FORK = 1<<1,
2755
-/*
2756
-** Tells checkin_mini() to dump its generated manifest to stdout.
2757
-*/
2758
-CIMINI_DUMP_MANIFEST = 1<<2,
2759
-
2760
-/*
2761
-** By default, content containing what appears to be a merge conflict
2762
-** marker is not permitted. This flag relaxes that requirement.
2763
-*/
2764
-CIMINI_ALLOW_MERGE_MARKER = 1<<3,
2765
-
2766
-/*
2767
-** By default mini-checkins are not allowed to be "older"
2768
-** than their parent. i.e. they may not have a timestamp
2769
-** which predates their parent. This flag bypasses that
2770
-** check.
2771
-*/
2772
-CIMINI_ALLOW_OLDER = 1<<4,
2773
-
2774
-/*
2775
-** Indicates that the content of the newly-checked-in file is
2776
-** converted, if needed, to use the same EOL style as the previous
2777
-** version of that file. Only the in-memory/in-repo copies are
2778
-** affected, not the original file (if any).
2779
-*/
2780
-CIMINI_CONVERT_EOL_INHERIT = 1<<5,
2781
-/*
2782
-** Indicates that the input's EOLs should be converted to Unix-style.
2783
-*/
2784
-CIMINI_CONVERT_EOL_UNIX = 1<<6,
2785
-/*
2786
-** Indicates that the input's EOLs should be converted to Windows-style.
2787
-*/
2788
-CIMINI_CONVERT_EOL_WINDOWS = 1<<7,
2789
-/*
2790
-** A hint to checkin_mini() to "prefer" creation of a delta manifest.
2791
-** It may decide not to for various reasons.
2792
-*/
2793
-CIMINI_PREFER_DELTA = 1<<8,
2794
-/*
2795
-** A "stronger hint" to checkin_mini() to prefer creation of a delta
2796
-** manifest if it at all can. It will decide not to only if creation
2797
-** of a delta is not a realistic option. For this to work, it must be
2798
-** set together with the CIMINI_PREFER_DELTA flag, but the two cannot
2799
-** be combined in this enum.
2800
-**
2801
-** This option is ONLY INTENDED FOR TESTING, used in bypassing
2802
-** heuristics which may otherwise disable generation of a delta on the
2803
-** grounds of efficiency (e.g. not generating a delta if the parent
2804
-** non-delta only has a few F-cards).
2805
-**
2806
-** The forbid-delta-manifests repo config option trumps this.
2807
-*/
2808
-CIMINI_STRONGLY_PREFER_DELTA = 1<<9,
2809
-/*
2810
-** Tells checkin_mini() to permit the addition of a new file. Normally
2811
-** this is disabled because there are many cases where it could cause
2812
-** the inadvertent addition of a new file when an update to an
2813
-** existing was intended, as a side-effect of name-case differences.
2814
-*/
2815
-CIMINI_ALLOW_NEW_FILE = 1<<10
2816
-};
2817
-
2818
-/*
2819
-** Initializes p to a known-valid default state.
2820
-*/
2821
-static void CheckinMiniInfo_init( CheckinMiniInfo * p ){
2822
- memset(p, 0, sizeof(CheckinMiniInfo));
2823
- p->flags = CIMINI_NONE;
2824
- p->filePerm = -1;
2825
- p->comment = p->fileContent = p->fileHash = empty_blob;
2826
-}
2827
-
2828
-/*
2829
-** Frees all memory owned by p, but does not free p.
2830
- */
2831
-static void CheckinMiniInfo_cleanup( CheckinMiniInfo * p ){
2832
- blob_reset(&p->comment);
2833
- blob_reset(&p->fileContent);
2834
- blob_reset(&p->fileHash);
2835
- if(p->pParent){
2836
- manifest_destroy(p->pParent);
2837
- }
2838
- fossil_free(p->zFilename);
2839
- fossil_free(p->zDate);
2840
- fossil_free(p->zParentUuid);
2841
- fossil_free(p->zMimetype);
2842
- fossil_free(p->zUser);
2843
- CheckinMiniInfo_init(p);
2844
-}
2845
-
2846
-/*
2847
-** Internal helper which returns an F-card perms string suitable for
2848
-** writing into a manifest.
2849
-*/
2850
-static const char * mfile_permint_mstring(int perm){
2851
- switch(perm){
2852
- case PERM_EXE: return " x";
2853
- case PERM_LNK: return " l";
2854
- default: return "";
2855
- }
2856
-}
2857
-
2858
-/*
2859
-** Given a ManifestFile permission string (or NULL), it returns one of
2860
-** PERM_REG, PERM_EXE, or PERM_LNK.
2861
-*/
2862
-static int mfile_permstr_int(const char *zPerm){
2863
- if(!zPerm || !*zPerm) return PERM_REG;
2864
- else if(strstr(zPerm,"x")) return PERM_EXE;
2865
- else if(strstr(zPerm,"l")) return PERM_LNK;
2866
- else return PERM_REG/*???*/;
2867
-}
2868
-
2869
-static const char * mfile_perm_mstring(const ManifestFile * p){
2870
- return mfile_permint_mstring(manifest_file_mperm(p));
2871
-}
2872
-
2873
-/*
2874
-** Internal helper for checkin_mini() and friends. Appends an F-card
2875
-** for p to pOut.
2876
-*/
2877
-static void checkin_mini_append_fcard(Blob *pOut, const ManifestFile *p){
2878
- if(p->zUuid){
2879
- assert(*p->zUuid);
2880
- blob_appendf(pOut, "F %F %s%s", p->zName,
2881
- p->zUuid, mfile_perm_mstring(p));
2882
- if(p->zPrior){
2883
- assert(*p->zPrior);
2884
- blob_appendf(pOut, " %F\n", p->zPrior);
2885
- }else{
2886
- blob_append(pOut, "\n", 1);
2887
- }
2888
- }else{
2889
- /* File was removed from parent delta. */
2890
- blob_appendf(pOut, "F %F\n", p->zName);
2891
- }
2892
-}
2893
-/*
2894
-** Handles the F-card parts for create_manifest_mini().
2895
-**
2896
-** If asDelta is true, F-cards will be handled as for a delta
2897
-** manifest, and the caller MUST have added a B-card to pOut before
2898
-** calling this.
2899
-**
2900
-** Returns 1 on success, 0 on error, and writes any error message to
2901
-** pErr (if it's not NULL). The only non-immediately-fatal/panic error
2902
-** is if pCI->filePerm is PERM_LNK or pCI would update a PERM_LNK
2903
-** in-repo file.
2904
-*/
2905
-static int create_manifest_mini_fcards( Blob * pOut,
2906
- CheckinMiniInfo * pCI,
2907
- int asDelta,
2908
- Blob * pErr){
2909
- int wroteThisCard = 0;
2910
- const ManifestFile * pFile;
2911
- int (*fncmp)(char const *, char const *) = /* filename comparator */
2912
- filenames_are_case_sensitive()
2913
- ? fossil_strcmp
2914
- : fossil_stricmp;
2915
-#define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2916
-#define write_this_card(NAME) \
2917
- blob_appendf(pOut, "F %F %b%s\n", (NAME), &pCI->fileHash, \
2918
- mfile_permint_mstring(pCI->filePerm)); \
2919
- wroteThisCard = 1
2920
-
2921
- assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2922
- assert(pCI->filePerm==PERM_REG || pCI->filePerm==PERM_EXE);
2923
- if(PERM_LNK==pCI->filePerm){
2924
- goto err_no_symlink;
2925
- }
2926
- manifest_file_rewind(pCI->pParent);
2927
- if(asDelta!=0 && (pCI->pParent->zBaseline==0
2928
- || pCI->pParent->nFile==0)){
2929
- /* Parent is a baseline or a delta with no F-cards, so this is
2930
- ** the simplest case: create a delta with a single F-card.
2931
- */
2932
- pFile = manifest_file_find(pCI->pParent, pCI->zFilename);
2933
- if(pFile!=0 && manifest_file_mperm(pFile)==PERM_LNK){
2934
- goto err_no_symlink;
2935
- }
2936
- write_this_card(pFile ? pFile->zName : pCI->zFilename);
2937
- return 1;
2938
- }
2939
- while(1){
2940
- int cmp;
2941
- if(asDelta==0){
2942
- pFile = manifest_file_next(pCI->pParent, 0);
2943
- }else{
2944
- /* Parent is a delta manifest with F-cards. Traversal of delta
2945
- ** manifest file entries is normally done via
2946
- ** manifest_file_next(), which takes into account the
2947
- ** differences between the delta and its parent and returns
2948
- ** F-cards from both. Each successive delta from the same
2949
- ** baseline includes all F-card changes from the previous
2950
- ** deltas, so we instead clone the parent's F-cards except for
2951
- ** the one (if any) which matches the new file.
2952
- */
2953
- pFile = pCI->pParent->iFile < pCI->pParent->nFile
2954
- ? &pCI->pParent->aFile[pCI->pParent->iFile++]
2955
- : 0;
2956
- }
2957
- if(0==pFile) break;
2958
- cmp = fncmp(pFile->zName, pCI->zFilename);
2959
- if(cmp<0){
2960
- checkin_mini_append_fcard(pOut,pFile);
2961
- }else{
2962
- if(cmp==0 || 0==wroteThisCard){
2963
- assert(0==wroteThisCard);
2964
- if(PERM_LNK==manifest_file_mperm(pFile)){
2965
- goto err_no_symlink;
2966
- }
2967
- write_this_card(cmp==0 ? pFile->zName : pCI->zFilename);
2968
- }
2969
- if(cmp>0){
2970
- assert(wroteThisCard!=0);
2971
- checkin_mini_append_fcard(pOut,pFile);
2972
- }
2973
- }
2974
- }
2975
- if(wroteThisCard==0){
2976
- write_this_card(pCI->zFilename);
2977
- }
2978
- return 1;
2979
-err_no_symlink:
2980
- mf_err((pErr,"Cannot commit or overwrite symlinks "
2981
- "via mini-checkin."));
2982
- return 0;
2983
-#undef write_this_card
2984
-#undef mf_err
2985
-}
2986
-
2987
-/*
2988
-** Creates a manifest file, written to pOut, from the state in the
2989
-** fully-populated and semantically valid pCI argument. pCI is not
2990
-** *semantically* modified but cannot be const because blob_str() may
2991
-** need to NUL-terminate any given blob.
2992
-**
2993
-** Returns true on success. On error, returns 0 and, if pErr is not
2994
-** NULL, writes an error message there.
2995
-**
2996
-** Intended only to be called via checkin_mini() or routines which
2997
-** have already completely vetted pCI.
2998
-*/
2999
-static int create_manifest_mini( Blob * pOut, CheckinMiniInfo * pCI,
3000
- Blob * pErr){
3001
- Blob zCard = empty_blob; /* Z-card checksum */
3002
- int asDelta = 0;
3003
-#define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
3004
-
3005
- assert(blob_str(&pCI->fileHash));
3006
- assert(pCI->pParent);
3007
- assert(pCI->zFilename);
3008
- assert(pCI->zUser);
3009
- assert(pCI->zDate);
3010
-
3011
- /* Potential TODOs include...
3012
- **
3013
- ** - Maybe add support for tags. Those can be edited via /info page,
3014
- ** and feel like YAGNI/feature creep for this purpose.
3015
- */
3016
- blob_zero(pOut);
3017
- manifest_file_rewind(pCI->pParent) /* force load of baseline */;
3018
- /* Determine whether we want to create a delta manifest... */
3019
- if((CIMINI_PREFER_DELTA & pCI->flags)
3020
- && ((CIMINI_STRONGLY_PREFER_DELTA & pCI->flags)
3021
- || (pCI->pParent->pBaseline
3022
- ? pCI->pParent->pBaseline
3023
- : pCI->pParent)->nFile > 15
3024
- /* 15 is arbitrary: don't create a delta when there is only a
3025
- ** tiny gain for doing so. That heuristic is not *quite*
3026
- ** right, in that when we're deriving from another delta, we
3027
- ** really should compare the F-card count between it and its
3028
- ** baseline, and create a delta if the baseline has (say)
3029
- ** twice or more as many F-cards as the previous delta. */)
3030
- && !db_get_boolean("forbid-delta-manifests",0)
3031
- ){
3032
- asDelta = 1;
3033
- blob_appendf(pOut, "B %s\n",
3034
- pCI->pParent->zBaseline
3035
- ? pCI->pParent->zBaseline
3036
- : pCI->zParentUuid);
3037
- }
3038
- blob_reserve(pOut, 1024 *
3039
- (asDelta ? 2 : pCI->pParent->nFile/11+1
3040
- /* In the fossil core repo, each 12-ish F-cards (on
3041
- ** average) take up roughly 1kb */));
3042
- if(blob_size(&pCI->comment)!=0){
3043
- blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
3044
- }else{
3045
- blob_append(pOut, "C (no\\scomment)\n", 16);
3046
- }
3047
- blob_appendf(pOut, "D %s\n", pCI->zDate);
3048
- if(create_manifest_mini_fcards(pOut,pCI,asDelta,pErr)==0){
3049
- return 0;
3050
- }
3051
- if(pCI->zMimetype!=0 && pCI->zMimetype[0]!=0){
3052
- blob_appendf(pOut, "N %F\n", pCI->zMimetype);
3053
- }
3054
- blob_appendf(pOut, "P %s\n", pCI->zParentUuid);
3055
- blob_appendf(pOut, "U %F\n", pCI->zUser);
3056
- md5sum_blob(pOut, &zCard);
3057
- blob_appendf(pOut, "Z %b\n", &zCard);
3058
- blob_reset(&zCard);
3059
- return 1;
3060
-#undef mf_err
3061
-}
3062
-
3063
-/*
3064
-** EXPERIMENTAL! Subject to change or removal at any time.
3065
-**
3066
-** A so-called "single-file/mini/web checkin" is a slimmed-down form
3067
-** of the checkin command which accepts only a single file and is
3068
-** intended to accept edits to a file via the web interface or from
3069
-** the CLI from outside of a checkout.
3070
-**
3071
-** Being fully non-interactive is a requirement for this function,
3072
-** thus it cannot perform autosync or similar activities.
3073
-**
3074
-** This routine uses the state from the given fully-populated pCI
3075
-** argument to add pCI->fileContent to the database, and create and
3076
-** save a manifest for that change. Ownership of pCI and its contents
3077
-** are unchanged.
3078
-**
3079
-** This function may may modify pCI as follows:
3080
-**
3081
-** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL,
3082
-** then the other will be assigned based on its counterpart. Both
3083
-** may not be NULL.
3084
-**
3085
-** - pCI->zDate is normalized to/replaced with a valid date/time
3086
-** string. If its original value cannot be validated then
3087
-** this function fails. If pCI->zDate is NULL, the current time
3088
-** is used.
3089
-**
3090
-** - If the CIMINI_CONVERT_EOL_INHERIT flag is set,
3091
-** pCI->fileContent appears to be plain text, and its line-ending
3092
-** style differs from its previous version, it is converted to the
3093
-** same EOL style as the previous version. If this is done, the
3094
-** pCI->fileHash is re-computed. Note that only pCI->fileContent,
3095
-** not the original file, is affected by the conversion.
3096
-**
3097
-** - If pCI->fileHash is empty, this routine populates it with the
3098
-** repository's preferred hash algorithm.
3099
-**
3100
-** - pCI->comment may be converted to Unix-style newlines.
3101
-**
3102
-** pCI's ownership is not modified.
3103
-**
3104
-** This function validates several of the inputs and fails if any
3105
-** validation fails.
3106
-**
3107
-** On error, returns false (0) and, if pErr is not NULL, writes a
3108
-** diagnostic message there.
3109
-**
3110
-** Returns true on success. If pRid is not NULL, the RID of the
3111
-** resulting manifest is written to *pRid.
3112
-**
3113
-** The checkin process is largely influenced by pCI->flags, and that
3114
-** must be populated before calling this. See the fossil_cimini_flags
3115
-** enum for the docs for each flag.
3116
-*/
3117
-static int checkin_mini(CheckinMiniInfo * pCI, int *pRid, Blob * pErr){
3118
- Blob mf = empty_blob; /* output manifest */
3119
- int rid = 0, frid = 0; /* various RIDs */
3120
- int isPrivate; /* whether this is private content
3121
- or not */
3122
- ManifestFile * zFilePrev; /* file entry from pCI->pParent */
3123
- int prevFRid = 0; /* RID of file's prev. version */
3124
-#define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
3125
-
3126
- if(!(pCI->flags & CIMINI_DRY_RUN)){
3127
- /* Until this feature is fully vetted, disallow it in the main
3128
- ** fossil repo unless dry-run mode is being used. */
3129
- char * zProjCode = db_get("project-code",0);
3130
- assert(zProjCode);
3131
- if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
3132
- zProjCode)){
3133
- fossil_fatal("Never, ever run this in/on the core fossil repo "
3134
- "in non-dry-run mode until it's been well-vetted. "
3135
- "Use a temp/test repo.");
3136
- }
3137
- fossil_free(zProjCode);
3138
- }
3139
- db_begin_transaction();
3140
-
3141
- if(pCI->pParent==0 && pCI->zParentUuid==0){
3142
- ci_err((pErr, "Cannot determine parent version."));
3143
- }
3144
- else if(pCI->pParent==0){
3145
- pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
3146
- if(pCI->pParent==0){
3147
- ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
3148
- }
3149
- }else if(pCI->zParentUuid==0){
3150
- pCI->zParentUuid = rid_to_uuid(pCI->pParent->rid);
3151
- assert(pCI->zParentUuid);
3152
- }
3153
-
3154
- assert(pCI->pParent->rid>0);
3155
- if(leaf_is_closed(pCI->pParent->rid)){
3156
- ci_err((pErr,"Cannot commit to a closed leaf."));
3157
- /* Remember that in order to override this we'd also need to
3158
- ** cancel TAG_CLOSED on pCI->pParent. There would seem to be no
3159
- ** reason we can't do that via the generated manifest, but the
3160
- ** commit command does not offer that option, so mini-checkin
3161
- ** probably shouldn't, either.
3162
- */
3163
- }
3164
- if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
3165
- ci_err((pErr,"No such user: %s", pCI->zUser));
3166
- }
3167
- if(!(CIMINI_ALLOW_FORK & pCI->flags)
3168
- && !is_a_leaf(pCI->pParent->rid)){
3169
- ci_err((pErr,"Parent [%S] is not a leaf and forking is disabled.",
3170
- pCI->zParentUuid));
3171
- }
3172
- if(!(CIMINI_ALLOW_MERGE_MARKER & pCI->flags)
3173
- && contains_merge_marker(&pCI->fileContent)){
3174
- ci_err((pErr,"Content appears to contain a merge conflict marker."));
3175
- }
3176
- if(!file_is_simple_pathname(pCI->zFilename, 1)){
3177
- ci_err((pErr,"Invalid filename for use in a repository: %s",
3178
- pCI->zFilename));
3179
- }
3180
- if(!(CIMINI_ALLOW_OLDER & pCI->flags)
3181
- && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
3182
- ci_err((pErr,"Checkin time (%s) may not be older "
3183
- "than its parent (%z).",
3184
- pCI->zDate,
3185
- db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
3186
- pCI->pParent->rDate)
3187
- ));
3188
- }
3189
- {
3190
- /*
3191
- ** Normalize the timestamp. We don't use date_in_standard_format()
3192
- ** because that has side-effects we don't want to trigger here.
3193
- */
3194
- char * zDVal = db_text(
3195
- 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
3196
- pCI->zDate ? pCI->zDate : "now");
3197
- if(zDVal==0 || zDVal[0]==0){
3198
- fossil_free(zDVal);
3199
- ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
3200
- }
3201
- fossil_free(pCI->zDate);
3202
- pCI->zDate = zDVal;
3203
- }
3204
- { /* Confirm that only one EOL policy is in place. */
3205
- int n = 0;
3206
- if(CIMINI_CONVERT_EOL_INHERIT & pCI->flags) ++n;
3207
- if(CIMINI_CONVERT_EOL_UNIX & pCI->flags) ++n;
3208
- if(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags) ++n;
3209
- if(n>1){
3210
- ci_err((pErr,"More than 1 EOL conversion policy was specified."));
3211
- }
3212
- }
3213
- /* Potential TODOs include:
3214
- **
3215
- ** - Commit allows an empty checkin only with a flag, but we
3216
- ** currently disallow it entirely. Conform with commit?
3217
- **
3218
- ** Non-TODOs:
3219
- **
3220
- ** - Check for a commit lock would require auto-sync, which this
3221
- ** code cannot do if it's going to be run via a web page.
3222
- */
3223
-
3224
- /*
3225
- ** Confirm that pCI->zFilename can be found in pCI->pParent. If
3226
- ** not, fail unless the CIMINI_ALLOW_NEW_FILE flag is set. This is
3227
- ** admittedly an artificial limitation, not strictly necessary. We
3228
- ** do it to hopefully reduce the chance of an "oops" where file
3229
- ** X/Y/z gets committed as X/Y/Z or X/y/z due to a typo or
3230
- ** case-sensitivity mismatch between the user/repo/filesystem, or
3231
- ** some such.
3232
- */
3233
- manifest_file_rewind(pCI->pParent);
3234
- zFilePrev = manifest_file_find(pCI->pParent, pCI->zFilename);
3235
- if(!(CIMINI_ALLOW_NEW_FILE & pCI->flags)
3236
- && (!zFilePrev
3237
- || !zFilePrev->zUuid/*was removed from parent delta manifest*/)
3238
- ){
3239
- ci_err((pErr,"File [%s] not found in manifest [%S]. "
3240
- "Adding new files is currently not permitted.",
3241
- pCI->zFilename, pCI->zParentUuid));
3242
- }else if(zFilePrev
3243
- && manifest_file_mperm(zFilePrev)==PERM_LNK){
3244
- ci_err((pErr,"Cannot save a symlink via a mini-checkin."));
3245
- }
3246
- if(zFilePrev){
3247
- prevFRid = fast_uuid_to_rid(zFilePrev->zUuid);
3248
- }
3249
-
3250
- if(((CIMINI_CONVERT_EOL_INHERIT & pCI->flags)
3251
- || (CIMINI_CONVERT_EOL_UNIX & pCI->flags)
3252
- || (CIMINI_CONVERT_EOL_WINDOWS & pCI->flags))
3253
- && blob_size(&pCI->fileContent)>0
3254
- ){
3255
- /* Convert to the requested EOL style. Note that this inherently
3256
- ** runs a risk of breaking content, e.g. string literals which
3257
- ** contain embedded newlines. Note that HTML5 specifies that
3258
- ** form-submitted TEXTAREA content gets normalized to CRLF-style:
3259
- **
3260
- ** https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element
3261
- */
3262
- const int pseudoBinary = LOOK_LONG | LOOK_NUL;
3263
- const int lookFlags = LOOK_CRLF | pseudoBinary;
3264
- const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags );
3265
- if(!(pseudoBinary & lookNew)){
3266
- int rehash = 0;
3267
- if(CIMINI_CONVERT_EOL_INHERIT & pCI->flags){
3268
- Blob contentPrev = empty_blob;
3269
- int lookOrig, nOrig;
3270
- content_get(prevFRid, &contentPrev);
3271
- lookOrig = looks_like_utf8(&contentPrev, lookFlags);
3272
- nOrig = blob_size(&contentPrev);
3273
- blob_reset(&contentPrev);
3274
- if(nOrig>0 && lookOrig!=lookNew){
3275
- /* If there is a newline-style mismatch, adjust the new
3276
- ** content version to the previous style, then re-hash the
3277
- ** content. Note that this means that what we insert is NOT
3278
- ** what's in the filesystem.
3279
- */
3280
- if(!(lookOrig & LOOK_CRLF) && (lookNew & LOOK_CRLF)){
3281
- /* Old has Unix-style, new has Windows-style. */
3282
- blob_to_lf_only(&pCI->fileContent);
3283
- rehash = 1;
3284
- }else if((lookOrig & LOOK_CRLF) && !(lookNew & LOOK_CRLF)){
3285
- /* Old has Windows-style, new has Unix-style. */
3286
- blob_add_cr(&pCI->fileContent);
3287
- rehash = 1;
3288
- }
3289
- }
3290
- }else{
3291
- const int oldSize = blob_size(&pCI->fileContent);
3292
- if(CIMINI_CONVERT_EOL_UNIX & pCI->flags){
3293
- blob_to_lf_only(&pCI->fileContent);
3294
- }else{
3295
- assert(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags);
3296
- blob_add_cr(&pCI->fileContent);
3297
- }
3298
- if(blob_size(&pCI->fileContent)!=oldSize){
3299
- rehash = 1;
3300
- }
3301
- }
3302
- if(rehash!=0){
3303
- hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
3304
- }
3305
- }
3306
- }/* end EOL conversion */
3307
-
3308
- if(blob_size(&pCI->fileHash)==0){
3309
- /* Hash the content if it's not done already... */
3310
- hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
3311
- assert(blob_size(&pCI->fileHash)>0);
3312
- }
3313
- if(zFilePrev){
3314
- /* Has this file been changed since its previous commit? Note
3315
- ** that we have to delay this check until after the potentially
3316
- ** expensive EOL conversion. */
3317
- assert(blob_size(&pCI->fileHash));
3318
- if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash))
3319
- && manifest_file_mperm(zFilePrev)==pCI->filePerm){
3320
- ci_err((pErr,"File is unchanged. Not saving."));
3321
- }
3322
- }
3323
-#if 1
3324
- /* Do we really want to normalize comment EOLs? Web-posting will
3325
- ** submit them in CRLF format. */
3326
- blob_to_lf_only(&pCI->comment);
3327
-#endif
3328
- /* Create, save, deltify, and crosslink the manifest... */
3329
- if(create_manifest_mini(&mf, pCI, pErr)==0){
3330
- return 0;
3331
- }
3332
- isPrivate = content_is_private(pCI->pParent->rid);
3333
- rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
3334
- if(pCI->flags & CIMINI_DUMP_MANIFEST){
3335
- fossil_print("%b", &mf);
3336
- }
3337
- if(pCI->pMfOut!=0){
3338
- /* Cross-linking clears mf, so we have to copy it,
3339
- ** instead of taking over its memory. */
3340
- blob_reset(pCI->pMfOut);
3341
- blob_append(pCI->pMfOut, blob_buffer(&mf), blob_size(&mf));
3342
- }
3343
- content_deltify(rid, &pCI->pParent->rid, 1, 0);
3344
- manifest_crosslink(rid, &mf, 0);
3345
- blob_reset(&mf);
3346
- /* Save and deltify the file content... */
3347
- frid = content_put_ex(&pCI->fileContent, blob_str(&pCI->fileHash),
3348
- 0, 0, isPrivate);
3349
- if(zFilePrev!=0){
3350
- assert(prevFRid>0);
3351
- content_deltify(frid, &prevFRid, 1, 0);
3352
- }
3353
- db_end_transaction((CIMINI_DRY_RUN & pCI->flags) ? 1 : 0);
3354
- if(pRid!=0){
3355
- *pRid = rid;
3356
- }
3357
- return 1;
3358
-ci_error:
3359
- assert(db_transaction_nesting_depth()>0);
3360
- db_end_transaction(1);
3361
- return 0;
3362
-#undef ci_err
3363
-}
3364
-
3365
-/*
3366
-** COMMAND: test-ci-mini
3367
-**
3368
-** This is an on-going experiment, subject to change or removal at
3369
-** any time.
3370
-**
3371
-** Usage: %fossil test-ci-mini ?OPTIONS? FILENAME
3372
-**
3373
-** where FILENAME is a repo-relative name as it would appear in the
3374
-** vfile table.
3375
-**
3376
-** Options:
3377
-**
3378
-** --repository|-R REPO The repository file to commit to.
3379
-** --as FILENAME The repository-side name of the input
3380
-** file, relative to the top of the
3381
-** repository. Default is the same as the
3382
-** input file name.
3383
-** --comment|-m COMMENT Required checkin comment.
3384
-** --comment-file|-M FILE Reads checkin comment from the given file.
3385
-** --revision|-r VERSION Commit from this version. Default is
3386
-** the checkout version (if available) or
3387
-** trunk (if used without a checkout).
3388
-** --allow-fork Allows the commit to be made against a
3389
-** non-leaf parent. Note that no autosync
3390
-** is performed beforehand.
3391
-** --allow-merge-conflict Allows checkin of a file even if it
3392
-** appears to contain a fossil merge conflict
3393
-** marker.
3394
-** --user-override USER USER to use instead of the current
3395
-** default.
3396
-** --date-override DATETIME DATE to use instead of 'now'.
3397
-** --allow-older Allow a commit to be older than its
3398
-** ancestor.
3399
-** --convert-eol Convert EOL style of the checkin to match
3400
-** the previous version's content. Does not
3401
-** modify the input file, only the checked-in
3402
-** content.
3403
-** --delta Prefer to generate a delta manifest, if
3404
-** able. The forbid-delta-manifests repo
3405
-** config option trumps this, as do certain
3406
-** heuristics.
3407
-** --allow-new-file Allow addition of a new file this way.
3408
-** Disabled by default to avoid that case-
3409
-** sensitivity errors inadvertently lead to
3410
-** adding a new file where an update is
3411
-** intended.
3412
-** --dump-manifest|-d Dumps the generated manifest to stdout
3413
-** immediately after it's generated.
3414
-** --save-manifest FILE Saves the generated manifest to a file
3415
-** after successfully processing it.
3416
-** --wet-run Disables the default dry-run mode.
3417
-**
3418
-** Example:
3419
-**
3420
-** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c
3421
-**
3422
-*/
3423
-void test_ci_mini_cmd(){
3424
- CheckinMiniInfo cimi; /* checkin state */
3425
- int newRid = 0; /* RID of new version */
3426
- const char * zFilename; /* argv[2] */
3427
- const char * zComment; /* -m comment */
3428
- const char * zCommentFile; /* -M FILE */
3429
- const char * zAsFilename; /* --as filename */
3430
- const char * zRevision; /* --revision|-r [=trunk|checkout] */
3431
- const char * zUser; /* --user-override */
3432
- const char * zDate; /* --date-override */
3433
- char const * zManifestFile = 0;/* --save-manifest FILE */
3434
-
3435
- /* This function should perform only the minimal "business logic" it
3436
- ** needs in order to fully/properly populate the CheckinMiniInfo and
3437
- ** then pass it on to checkin_mini() to do most of the validation
3438
- ** and work. The point of this is to avoid duplicate code when a web
3439
- ** front-end is added for checkin_mini().
3440
- */
3441
- CheckinMiniInfo_init(&cimi);
3442
- zComment = find_option("comment","m",1);
3443
- zCommentFile = find_option("comment-file","M",1);
3444
- zAsFilename = find_option("as",0,1);
3445
- zRevision = find_option("revision","r",1);
3446
- zUser = find_option("user-override",0,1);
3447
- zDate = find_option("date-override",0,1);
3448
- zManifestFile = find_option("save-manifest",0,1);
3449
- if(find_option("wet-run",0,0)==0){
3450
- cimi.flags |= CIMINI_DRY_RUN;
3451
- }
3452
- if(find_option("allow-fork",0,0)!=0){
3453
- cimi.flags |= CIMINI_ALLOW_FORK;
3454
- }
3455
- if(find_option("dump-manifest","d",0)!=0){
3456
- cimi.flags |= CIMINI_DUMP_MANIFEST;
3457
- }
3458
- if(find_option("allow-merge-conflict",0,0)!=0){
3459
- cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
3460
- }
3461
- if(find_option("allow-older",0,0)!=0){
3462
- cimi.flags |= CIMINI_ALLOW_OLDER;
3463
- }
3464
- if(find_option("convert-eol-prev",0,0)!=0){
3465
- cimi.flags |= CIMINI_CONVERT_EOL_INHERIT;
3466
- }
3467
- if(find_option("delta",0,0)!=0){
3468
- cimi.flags |= CIMINI_PREFER_DELTA;
3469
- }
3470
- if(find_option("delta2",0,0)!=0){
3471
- /* Undocumented. For testing only. */
3472
- cimi.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA;
3473
- }
3474
- if(find_option("allow-new-file",0,0)!=0){
3475
- cimi.flags |= CIMINI_ALLOW_NEW_FILE;
3476
- }
3477
- db_find_and_open_repository(0, 0);
3478
- verify_all_options();
3479
- user_select();
3480
- if(g.argc!=3){
3481
- usage("INFILE");
3482
- }
3483
- if(zComment && zCommentFile){
3484
- fossil_fatal("Only one of -m or -M, not both, may be used.");
3485
- }else{
3486
- if(zCommentFile && *zCommentFile){
3487
- blob_read_from_file(&cimi.comment, zCommentFile, ExtFILE);
3488
- }else if(zComment && *zComment){
3489
- blob_append(&cimi.comment, zComment, -1);
3490
- }
3491
- if(!blob_size(&cimi.comment)){
3492
- fossil_fatal("Non-empty checkin comment is required.");
3493
- }
3494
- }
3495
- db_begin_transaction();
3496
- zFilename = g.argv[2];
3497
- cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3498
- cimi.filePerm = file_perm(zFilename, ExtFILE);
3499
- cimi.zUser = mprintf("%s", zUser ? zUser : login_name());
3500
- if(zDate){
3501
- cimi.zDate = mprintf("%s", zDate);
3502
- }
3503
- if(zRevision==0 || zRevision[0]==0){
3504
- if(g.localOpen/*checkout*/){
3505
- zRevision = db_lget("checkout-hash", 0)/*leak*/;
3506
- }else{
3507
- zRevision = "trunk";
3508
- }
3509
- }
3510
- name_to_uuid2(zRevision, "ci", &cimi.zParentUuid);
3511
- if(cimi.zParentUuid==0){
3512
- fossil_fatal("Cannot determine version to commit to.");
3513
- }
3514
- blob_read_from_file(&cimi.fileContent, zFilename, ExtFILE);
3515
- {
3516
- Blob theManifest = empty_blob; /* --save-manifest target */
3517
- Blob errMsg = empty_blob;
3518
- int rc;
3519
- if(zManifestFile){
3520
- cimi.pMfOut = &theManifest;
3521
- }
3522
- rc = checkin_mini(&cimi, &newRid, &errMsg);
3523
- if(rc){
3524
- assert(blob_size(&errMsg)==0);
3525
- }else{
3526
- assert(blob_size(&errMsg));
3527
- fossil_fatal("%b", &errMsg);
3528
- }
3529
- if(zManifestFile){
3530
- fossil_print("Writing manifest to: %s\n", zManifestFile);
3531
- assert(blob_size(&theManifest)>0);
3532
- blob_write_to_file(&theManifest, zManifestFile);
3533
- blob_reset(&theManifest);
3534
- }
3535
- }
3536
- if(newRid!=0){
3537
- fossil_print("New version%s: %z\n",
3538
- (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3539
- rid_to_uuid(newRid));
3540
- }
3541
- db_end_transaction(0/*checkin_mini() will have triggered it to roll
3542
- ** back in dry-run mode, but we need access to
3543
- ** the transaction-written db state in this
3544
- ** routine.*/);
3545
- if(!(cimi.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){
3546
- fossil_warning("The checkout state is now out of sync "
3547
- "with regards to this commit. It needs to be "
3548
- "'update'd or 'close'd and re-'open'ed.");
3549
- }
3550
- CheckinMiniInfo_cleanup(&cimi);
3551
-}
3552
-
3553
-
3554
-/*
3555
-** Returns true if the given filename qualifies for online editing by
3556
-** the current user, else returns false.
3557
-**
3558
-** Editing requires that the user have the Write permission and that
3559
-** the filename match the glob defined by the fileedit-glob setting.
3560
-** A missing or empty value for that glob disables all editing.
3561
-*/
3562
-int fileedit_is_editable(const char *zFilename){
3563
- static Glob * pGlobs = 0;
3564
- static int once = 0;
3565
- if(0==g.perm.Write || zFilename==0 || *zFilename==0
3566
- || (once!=0 && pGlobs==0)){
3567
- return 0;
3568
- }else if(0==pGlobs){
3569
- char * zGlobs = db_get("fileedit-glob",0);
3570
- once = 1;
3571
- if(0==zGlobs) return 0;
3572
- pGlobs = glob_create(zGlobs);
3573
- fossil_free(zGlobs);
3574
- }
3575
- return glob_match(pGlobs, zFilename);
3576
-}
3577
-
3578
-static void fileedit_emit_script(int phase){
3579
- if(0==phase){
3580
- CX("<script nonce='%s'>", style_nonce());
3581
- }else{
3582
- CX("</script>\n");
3583
- }
3584
-}
3585
-
3586
-#if 0
3587
-/*
3588
-** This function is for potential TODO features for /fileedit.
3589
-** It's been tested with that code but is not currently used
3590
-** by it.
3591
-*/
3592
-static void fileedit_emit_script_fetch(){
3593
- fileedit_emit_script(0);
3594
- CX("window.fossilFetch = function(path,opt){\n");
3595
- CX(" if('function'===typeof opt){\n");
3596
- CX(" opt={onload:opt};\n");
3597
- CX(" }else{\n");
3598
- CX(" opt=opt||{onload:function(r){console.debug('response:',r)}}\n");
3599
- CX(" }\n");
3600
- CX(" const url='%R/'+path, x=new XMLHttpRequest();\n");
3601
- CX(" x.open(opt.method||'GET', url, true);\n");
3602
- CX(" x.responseType=opt.responseType||'text';\n");
3603
- CX(" if(opt.onload){\n");
3604
- CX(" x.onload = function(e){\n");
3605
- CX(" if(200!==this.status){\n");
3606
- CX(" if(opt.onerror) opt.onerror(e);\n");
3607
- CX(" return;\n");
3608
- CX(" }\n");
3609
- CX(" opt.onload(this.response);\n");
3610
- CX(" }\n");
3611
- CX(" }\n");
3612
- CX(" x.send();");
3613
- CX("};\n");
3614
- fileedit_emit_script(1);
3615
-};
3616
-#endif /* fileedit_emit_script_fetch() */
3617
-
3618
-/*
3619
-** Outputs a labeled checkbox element:
3620
-**
3621
-** <span class='input-with-label' title={{zTip}}>
3622
-** <input type='checkbox' name={{zFieldName}} value={{zValue}}
3623
-** {{isChecked ? " checked : ""}}/>
3624
-** <span>{{zLabel}}</span>
3625
-** </span>
3626
-**
3627
-** zFieldName, zLabel, and zValue are required. zTip is optional.
3628
-*/
3629
-static void style_labeled_checkbox(const char *zFieldName,
3630
- const char * zLabel,
3631
- const char * zValue,
3632
- const char * zTip,
3633
- int isChecked){
3634
- CX("<span class='input-with-label'");
3635
- if(zTip && *zTip){
3636
- CX(" title='%h'", zTip);
3637
- }
3638
- CX("><input type='checkbox' name='%s' value='%T'%s/>",
3639
- zFieldName,
3640
- zValue ? zValue : "", isChecked ? " checked" : "");
3641
- CX("<span>%h</span></span>", zLabel);
3642
-}
3643
-
3644
-/*
3645
-** WEBPAGE: fileedit
3646
-**
3647
-** EXPERIMENTAL and subject to change and removal at any time. The goal
3648
-** is to allow online edits of files.
3649
-**
3650
-** Query parameters:
3651
-**
3652
-** file=FILENAME Repo-relative path to the file.
3653
-** r=VERSION Checkin version, using any unambiguous
3654
-** supported symbolic version name.
3655
-**
3656
-** All other parameters are for internal use only, submitted via the
3657
-** form-submission process, and may change with any given revision of
3658
-** this code.
3659
-*/
3660
-void fileedit_page(){
3661
- const char * zFilename = PD("file",P("name")); /* filename */
3662
- const char * zRev = P("r"); /* checkin version */
3663
- const char * zContent = P("content"); /* file content */
3664
- const char * zComment = P("comment"); /* checkin comment */
3665
- CheckinMiniInfo cimi; /* Checkin state */
3666
- int submitMode = 0; /* See mapping below */
3667
- int vid, newVid = 0; /* checkin rid */
3668
- char * zFileUuid = 0; /* File content UUID */
3669
- int frid = 0; /* File content rid */
3670
- Blob err = empty_blob; /* Error report */
3671
- const char * zFlagCheck = 0; /* Temp url flag holder */
3672
- Blob endScript = empty_blob; /* Script code to run at the
3673
- end. This content will be
3674
- combined into a single JS
3675
- function call, thus each
3676
- entry must end with a
3677
- semicolon. */
3678
- Stmt stmt = empty_Stmt;
3679
-#define fail(EXPR) blob_appendf EXPR; goto end_footer
3680
-
3681
- login_check_credentials();
3682
- if( !g.perm.Write ){
3683
- login_needed(g.anon.Write);
3684
- return;
3685
- }
3686
- db_begin_transaction();
3687
- CheckinMiniInfo_init(&cimi);
3688
- submitMode = atoi(PD("submit","0"))
3689
- /* Submit modes: 0=initial request,
3690
- ** 1=submit (save), 2=preview, 3=diff */;
3691
- zFlagCheck = P("comment_mimetype");
3692
- if(zFlagCheck){
3693
- cimi.zMimetype = mprintf("%s",zFlagCheck);
3694
- zFlagCheck = 0;
3695
- }
3696
- cimi.zUser = mprintf("%s",g.zLogin);
3697
-
3698
- style_header("File Editor");
3699
- /* As of this point, don't use return or fossil_fatal(), use
3700
- ** fail((&err,...)) instead so that we can be sure to do any
3701
- ** cleanup and end the transaction cleanly.
3702
- */
3703
- if(!zRev || !*zRev || !zFilename || !*zFilename){
3704
- fail((&err,"Missing required URL parameters."));
3705
- }
3706
- if(0==fileedit_is_editable(zFilename)){
3707
- fail((&err,"Filename <code>%h</code> is disallowed "
3708
- "by the <code>fileedit-glob</code> repository "
3709
- "setting.",
3710
- zFilename));
3711
- }
3712
- vid = symbolic_name_to_rid(zRev, "ci");
3713
- if(0==vid){
3714
- fail((&err,"Could not resolve checkin version."));
3715
- }
3716
-
3717
- /* Find the repo-side file entry or fail... */
3718
- cimi.zParentUuid = rid_to_uuid(vid);
3719
- db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
3720
- "WHERE filename=%Q %s AND checkinID=%d",
3721
- zFilename, filename_collation(), vid);
3722
- if(SQLITE_ROW==db_step(&stmt)){
3723
- const char * zPerm = db_column_text(&stmt, 1);
3724
- cimi.filePerm = mfile_permstr_int(zPerm);
3725
- if(PERM_LNK==cimi.filePerm){
3726
- fail((&err,"Editing symlinks is not permitted."));
3727
- }
3728
- zFileUuid = mprintf("%s",db_column_text(&stmt, 0));
3729
- }
3730
- db_finalize(&stmt);
3731
- if(!zFileUuid){
3732
- fail((&err,"Checkin [%S] does not contain file: "
3733
- "<code>%h</code>",
3734
- cimi.zParentUuid, zFilename));
3735
- }
3736
- frid = fast_uuid_to_rid(zFileUuid);
3737
- assert(frid);
3738
-
3739
- /* Read file content from submit request or repo... */
3740
- if(zContent==0){
3741
- content_get(frid, &cimi.fileContent);
3742
- zContent = blob_size(&cimi.fileContent)
3743
- ? blob_str(&cimi.fileContent) : NULL;
3744
- }else{
3745
- blob_init(&cimi.fileContent,zContent,-1);
3746
- }
3747
- if(looks_like_binary(&cimi.fileContent)){
3748
- fail((&err,"File appears to be binary. Cannot edit: "
3749
- "<code>%h</code>",zFilename));
3750
- }
3751
-
3752
- /* All set. Here we go... */
3753
-
3754
- CX("<h1>Editing:</h1>");
3755
- CX("<p class='fileedit-hint'>");
3756
- CX("File: <code>%h</code><br>"
3757
- "Checkin Version: <code id='r-label'>%s</code><br>",
3758
- zFilename, cimi.zParentUuid);
3759
- CX("Permalink: <code>"
3760
- "<a id='permalink' href='%R/fileedit?file=%T&r=%!S'>"
3761
- "/fileedit?file=%T&r=%!S</a></code><br>"
3762
- "(Clicking the permalink will reload the page and discard "
3763
- "all edits!)",
3764
- zFilename, cimi.zParentUuid,
3765
- zFilename, cimi.zParentUuid);
3766
- CX("</p>");
3767
- CX("<p>This page is <em>far from complete</em> and may still have "
3768
- "significant bugs. USE AT YOUR OWN RISK, preferably on a test "
3769
- "repo.</p>\n");
3770
-
3771
- CX("<form action='%R/fileedit' method='POST' "
3772
- "class='fileedit-form'>\n");
3773
-
3774
- /******* Hidden fields *******/
3775
- CX("<input type='hidden' name='r' value='%s'>",
3776
- cimi.zParentUuid);
3777
- CX("<input type='hidden' name='file' value='%T'>",
3778
- zFilename);
3779
-
3780
- /******* Comment *******/
3781
- CX("<h3>Checkin Comment</h3>\n");
3782
- CX("<textarea name='comment' rows='3' cols='80'>");
3783
- if(zComment && *zComment){
3784
- CX("%h"/*%h? %s?*/, zComment);
3785
- }
3786
- CX("</textarea>\n");
3787
- CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup "
3788
- "syntax.</div>"/*TODO: radiobuttons for fossil/me/plain text*/);
3789
-
3790
- /******* Content *******/
3791
- CX("<h3>File Content</h3>\n");
3792
- CX("<textarea name='content' id='fileedit-content' "
3793
- "rows='20' cols='80'>");
3794
- CX("Loading...");
3795
- CX("</textarea>\n");
3796
- /******* Flags/options *******/
3797
- CX("<fieldset class='fileedit-options'>"
3798
- "<legend>Options</legend><div>"
3799
- /* Chrome does not sanely lay out multiple
3800
- ** fieldset children after the <legend>, so
3801
- ** a containing div is necessary. */);
3802
- /*
3803
- ** TODO?: date-override date selection field. Maybe use
3804
- ** an input[type=datetime-local].
3805
- */
3806
- if(0==submitMode || P("dry_run")!=0){
3807
- cimi.flags |= CIMINI_DRY_RUN;
3808
- }
3809
- style_labeled_checkbox("dry_run", "Dry-run?", "1",
3810
- "In dry-run mode, the Save button performs "
3811
- "all work needed for saving but then rolls "
3812
- "back the transaction, and thus does not "
3813
- "really save.",
3814
- cimi.flags & CIMINI_DRY_RUN);
3815
- if(P("allow_fork")!=0){
3816
- cimi.flags |= CIMINI_ALLOW_FORK;
3817
- }
3818
- style_labeled_checkbox("allow_fork", "Allow fork?", "1",
3819
- "Allow saving to create a fork?",
3820
- cimi.flags & CIMINI_ALLOW_FORK);
3821
- if(P("allow_older")!=0){
3822
- cimi.flags |= CIMINI_ALLOW_OLDER;
3823
- }
3824
- style_labeled_checkbox("allow_older", "Allow older?", "1",
3825
- "Allow saving against a parent version "
3826
- "which has a newer timestamp?",
3827
- cimi.flags & CIMINI_ALLOW_OLDER);
3828
- if(P("exec_bit")!=0){
3829
- cimi.filePerm = PERM_EXE;
3830
- }
3831
- style_labeled_checkbox("exec_bit", "Executable?", "1",
3832
- "Set the executable bit?",
3833
- PERM_EXE==cimi.filePerm);
3834
- if(P("allow_merge_conflict")!=0){
3835
- cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
3836
- }
3837
- style_labeled_checkbox("allow_merge_conflict",
3838
- "Allow merge conflict markers?", "1",
3839
- "Allow saving even if the content contains "
3840
- "what appear to be fossil merge conflict "
3841
- "markers?",
3842
- cimi.flags & CIMINI_ALLOW_MERGE_MARKER);
3843
- if(P("prefer_delta")!=0){
3844
- cimi.flags |= CIMINI_PREFER_DELTA;
3845
- }
3846
- style_labeled_checkbox("prefer_delta",
3847
- "Prefer delta manifest?", "1",
3848
- "Will create a delta manifest, instead of "
3849
- "baseline, if conditions are favorable to do "
3850
- "so. This option is only a suggestion.",
3851
- cimi.flags & CIMINI_PREFER_DELTA);
3852
- {/* EOL conversion policy... */
3853
- const int eolMode = submitMode==0 ? 0 : atoi(PD("eol","0"));
3854
- switch(eolMode){
3855
- case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
3856
- case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
3857
- default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
3858
- }
3859
- CX("<select name='eol' "
3860
- "title='EOL conversion policy, noting that form-processing "
3861
- "may implicitly change the line endings of the input.'>");
3862
- CX("<option value='0'%s>Inherit EOLs</option>",
3863
- (eolMode!=1 && eolMode!=2) ? " selected" : "");
3864
- CX("<option value='1'%s/>Unix EOLs</option>",
3865
- eolMode==1 ? " selected" : "");
3866
- CX("<option value='2'%s>Windows EOLs</option>",
3867
- eolMode==2 ? " selected" : "");
3868
- CX("</select>");
3869
- }
3870
-
3871
- CX("</div></fieldset>") /* end of checkboxes */;
3872
-
3873
- /******* Buttons *******/
3874
- CX("<fieldset class='fileedit-options'>"
3875
- "<legend>Tell the server to...</legend><div>");
3876
- CX("<button type='submit' name='submit' value='1'>"
3877
- "Save</button>");
3878
- CX("<button type='submit' name='submit' value='2'>"
3879
- "Preview (TODO)</button>");
3880
- CX("<button type='submit' name='submit' value='3'>"
3881
- "Diff (TODO)</button>");
3882
- CX("</div></fieldset>");
3883
-
3884
- /******* End of form *******/
3885
- CX("</form>\n");
3886
-
3887
- {
3888
- /* Populate the editor...
3889
- **
3890
- ** To avoid all escaping-related issues, we have to do this one of
3891
- ** two ways:
3892
- **
3893
- ** 1) Fetch the content via AJAX. That only works if the content
3894
- ** is already in the db, but not for edited versions.
3895
- **
3896
- ** 2) Store the content as JSON and feed it into the textarea
3897
- ** using JavaScript.
3898
- */
3899
- char const * zQuoted = 0;
3900
- if(blob_size(&cimi.fileContent)>0){
3901
- db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
3902
- db_step(&stmt);
3903
- zQuoted = db_column_text(&stmt,0);
3904
- }
3905
- blob_appendf(&endScript,
3906
- "/* populate editor form */\n"
3907
- "document.getElementById('fileedit-content')"
3908
- ".value=%s;", zQuoted ? zQuoted : "'';\n");
3909
- if(stmt.pStmt){
3910
- db_finalize(&stmt);
3911
- }
3912
- }
3913
-
3914
- if(1==submitMode/*save*/){
3915
- Blob manifest = empty_blob;
3916
- char * zNewUuid = 0;
3917
- /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
3918
- if(zComment && *zComment){
3919
- blob_append(&cimi.comment, zComment, -1);
3920
- }else{
3921
- fail((&err,"Empty comment is not permitted."));
3922
- }
3923
- /*cimi.pParent = manifest_get(vid, CFTYPE_MANIFEST, 0);
3924
- assert(cimi.pParent && "We know vid is valid.");*/
3925
- cimi.zFilename = mprintf("%s",zFilename);
3926
- cimi.pMfOut = &manifest;
3927
- checkin_mini(&cimi, &newVid, &err);
3928
- if(newVid!=0){
3929
- zNewUuid = rid_to_uuid(newVid);
3930
- CX("<h3>Manifest%s: %S</h3><pre>"
3931
- "<code class='fileedit-manifest'>%h</code>"
3932
- "</pre>",
3933
- (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3934
- zNewUuid, blob_str(&manifest));
3935
- if(!(CIMINI_DRY_RUN & cimi.flags)){
3936
- /* We need to update certain form fields and UI elements so
3937
- ** they're not left pointing to the previous version. While
3938
- ** we're at it, we'll re-enable dry-run mode for sanity's
3939
- ** sake.
3940
- */
3941
- blob_appendf(&endScript,
3942
- "/* Toggle dry-run back on */\n"
3943
- "document.querySelector('input[type=checkbox]"
3944
- "[name=dry_run]').checked=true;\n");
3945
- blob_appendf(&endScript,
3946
- "/* Update version number */\n"
3947
- "document.querySelector('input[name=r]')"
3948
- ".value=%Q;\n"
3949
- "document.querySelector('#r-label')"
3950
- ".innerText=%Q;\n",
3951
- zNewUuid, zNewUuid);
3952
- blob_appendf(&endScript,
3953
- "/* Update permalink */\n"
3954
- "const urlFull='%R/fileedit?file=%T&r=%!S';\n"
3955
- "const urlShort='/fileedit?file=%T&r=%!S';\n"
3956
- "let link=document.querySelector('#permalink');\n"
3957
- "link.innerText=urlShort;\n"
3958
- "link.setAttribute('href',urlFull);\n",
3959
- zFilename, zNewUuid, zFilename, zNewUuid);
3960
- }
3961
- fossil_free(zNewUuid);
3962
- zNewUuid = 0;
3963
- }
3964
- /* On error, the error message is in the err blob and will
3965
- ** be emitted below. */
3966
- cimi.pMfOut = 0;
3967
- blob_reset(&manifest);
3968
- }else if(2==submitMode/*preview*/){
3969
- /* TODO */
3970
- fail((&err,"Preview mode is still TODO."));
3971
- }else if(3==submitMode/*diff*/){
3972
- fail((&err,"Diff mode is still TODO."));
3973
- }else{
3974
- /* Ignore invalid submitMode value */
3975
- goto end_footer;
3976
- }
3977
-
3978
-end_footer:
3979
- zContent = 0;
3980
- fossil_free(zFileUuid);
3981
- if(stmt.pStmt){
3982
- db_finalize(&stmt);
3983
- }
3984
- if(blob_size(&err)){
3985
- CX("<div class='fileedit-error-report'>%s</div>",
3986
- blob_str(&err));
3987
- }
3988
- blob_reset(&err);
3989
- CheckinMiniInfo_cleanup(&cimi);
3990
- if(blob_size(&endScript)>0){
3991
- fileedit_emit_script(0);
3992
- CX("(function(){\n");
3993
- CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
3994
- &endScript);
3995
- CX("})();");
3996
- fileedit_emit_script(1);
3997
- }
3998
- db_end_transaction(0/*noting that dry-run mode will have already
3999
- ** set this to rollback mode. */);
4000
- style_footer();
4001
-}
40022699
40032700
ADDED src/fileedit.c
--- src/checkin.c
+++ src/checkin.c
@@ -1406,11 +1406,11 @@
1406 ** Returns true if the checkin identified by the first parameter is
1407 ** older than the given (valid) date/time string, else returns false.
1408 ** Also returns true if rid does not refer to a checkin, but it is not
1409 ** intended to be used for that case.
1410 */
1411 static int checkin_is_younger(
1412 int rid, /* The record ID of the ancestor */
1413 const char *zDate /* Date & time of the current check-in */
1414 ){
1415 return db_exists(
1416 "SELECT 1 FROM event"
@@ -2694,1308 +2694,5 @@
2694 fossil_print("**** warning: a fork has occurred *****\n");
2695 }else{
2696 leaf_ambiguity_warning(nvid,nvid);
2697 }
2698 }
2699
2700 /*
2701 ** State for the "mini-checkin" infrastructure, which enables the
2702 ** ability to commit changes to a single file without a checkout
2703 ** db, e.g. for use via an HTTP request.
2704 **
2705 ** Use CheckinMiniInfo_init() to cleanly initialize one to a known
2706 ** valid/empty default state.
2707 **
2708 ** Memory for all non-const (char *) members is owned by the
2709 ** CheckinMiniInfo instance and is freed by CheckinMiniInfo_cleanup().
2710 */
2711 struct CheckinMiniInfo {
2712 Manifest * pParent; /* parent checkin. Memory is owned by this
2713 object. */
2714 char *zParentUuid; /* Full UUID of pParent */
2715 char *zFilename; /* Name of single file to commit. Must be
2716 relative to the top of the repo. */
2717 Blob fileContent; /* Content of file referred to by zFilename. */
2718 Blob fileHash; /* Hash of this->fileContent, using the repo's
2719 preferred hash method. */
2720 Blob comment; /* Check-in comment text */
2721 char *zMimetype; /* Mimetype of comment. May be NULL */
2722 char *zUser; /* User name */
2723 char *zDate; /* Optionally force this date string (anything
2724 supported by date_in_standard_format()).
2725 Maybe be NULL. */
2726 Blob *pMfOut; /* If not NULL, checkin_mini() will write a
2727 copy of the generated manifest here. This
2728 memory is NOT owned by CheckinMiniInfo. */
2729 int filePerm; /* Permissions (via file_perm()) of the input
2730 file. We need to store this before calling
2731 checkin_mini() because the real input file
2732 name may differ from the repo-centric
2733 this->zFilename, and checkin_mini() requires
2734 the permissions of the original file. For
2735 web commits, set this to PERM_REG or (when
2736 editing executable scripts) PERM_EXE before
2737 calling checkin_mini(). */
2738 int flags; /* Bitmask of fossil_cimini_flags. */
2739 };
2740 typedef struct CheckinMiniInfo CheckinMiniInfo;
2741
2742 /*
2743 ** CheckinMiniInfo::flags values.
2744 */
2745 enum fossil_cimini_flags {
2746 CIMINI_NONE = 0,
2747 /*
2748 ** Tells checkin_mini() to use dry-run mode.
2749 */
2750 CIMINI_DRY_RUN = 1,
2751 /*
2752 ** Tells checkin_mini() to allow forking from a non-leaf commit.
2753 */
2754 CIMINI_ALLOW_FORK = 1<<1,
2755 /*
2756 ** Tells checkin_mini() to dump its generated manifest to stdout.
2757 */
2758 CIMINI_DUMP_MANIFEST = 1<<2,
2759
2760 /*
2761 ** By default, content containing what appears to be a merge conflict
2762 ** marker is not permitted. This flag relaxes that requirement.
2763 */
2764 CIMINI_ALLOW_MERGE_MARKER = 1<<3,
2765
2766 /*
2767 ** By default mini-checkins are not allowed to be "older"
2768 ** than their parent. i.e. they may not have a timestamp
2769 ** which predates their parent. This flag bypasses that
2770 ** check.
2771 */
2772 CIMINI_ALLOW_OLDER = 1<<4,
2773
2774 /*
2775 ** Indicates that the content of the newly-checked-in file is
2776 ** converted, if needed, to use the same EOL style as the previous
2777 ** version of that file. Only the in-memory/in-repo copies are
2778 ** affected, not the original file (if any).
2779 */
2780 CIMINI_CONVERT_EOL_INHERIT = 1<<5,
2781 /*
2782 ** Indicates that the input's EOLs should be converted to Unix-style.
2783 */
2784 CIMINI_CONVERT_EOL_UNIX = 1<<6,
2785 /*
2786 ** Indicates that the input's EOLs should be converted to Windows-style.
2787 */
2788 CIMINI_CONVERT_EOL_WINDOWS = 1<<7,
2789 /*
2790 ** A hint to checkin_mini() to "prefer" creation of a delta manifest.
2791 ** It may decide not to for various reasons.
2792 */
2793 CIMINI_PREFER_DELTA = 1<<8,
2794 /*
2795 ** A "stronger hint" to checkin_mini() to prefer creation of a delta
2796 ** manifest if it at all can. It will decide not to only if creation
2797 ** of a delta is not a realistic option. For this to work, it must be
2798 ** set together with the CIMINI_PREFER_DELTA flag, but the two cannot
2799 ** be combined in this enum.
2800 **
2801 ** This option is ONLY INTENDED FOR TESTING, used in bypassing
2802 ** heuristics which may otherwise disable generation of a delta on the
2803 ** grounds of efficiency (e.g. not generating a delta if the parent
2804 ** non-delta only has a few F-cards).
2805 **
2806 ** The forbid-delta-manifests repo config option trumps this.
2807 */
2808 CIMINI_STRONGLY_PREFER_DELTA = 1<<9,
2809 /*
2810 ** Tells checkin_mini() to permit the addition of a new file. Normally
2811 ** this is disabled because there are many cases where it could cause
2812 ** the inadvertent addition of a new file when an update to an
2813 ** existing was intended, as a side-effect of name-case differences.
2814 */
2815 CIMINI_ALLOW_NEW_FILE = 1<<10
2816 };
2817
2818 /*
2819 ** Initializes p to a known-valid default state.
2820 */
2821 static void CheckinMiniInfo_init( CheckinMiniInfo * p ){
2822 memset(p, 0, sizeof(CheckinMiniInfo));
2823 p->flags = CIMINI_NONE;
2824 p->filePerm = -1;
2825 p->comment = p->fileContent = p->fileHash = empty_blob;
2826 }
2827
2828 /*
2829 ** Frees all memory owned by p, but does not free p.
2830 */
2831 static void CheckinMiniInfo_cleanup( CheckinMiniInfo * p ){
2832 blob_reset(&p->comment);
2833 blob_reset(&p->fileContent);
2834 blob_reset(&p->fileHash);
2835 if(p->pParent){
2836 manifest_destroy(p->pParent);
2837 }
2838 fossil_free(p->zFilename);
2839 fossil_free(p->zDate);
2840 fossil_free(p->zParentUuid);
2841 fossil_free(p->zMimetype);
2842 fossil_free(p->zUser);
2843 CheckinMiniInfo_init(p);
2844 }
2845
2846 /*
2847 ** Internal helper which returns an F-card perms string suitable for
2848 ** writing into a manifest.
2849 */
2850 static const char * mfile_permint_mstring(int perm){
2851 switch(perm){
2852 case PERM_EXE: return " x";
2853 case PERM_LNK: return " l";
2854 default: return "";
2855 }
2856 }
2857
2858 /*
2859 ** Given a ManifestFile permission string (or NULL), it returns one of
2860 ** PERM_REG, PERM_EXE, or PERM_LNK.
2861 */
2862 static int mfile_permstr_int(const char *zPerm){
2863 if(!zPerm || !*zPerm) return PERM_REG;
2864 else if(strstr(zPerm,"x")) return PERM_EXE;
2865 else if(strstr(zPerm,"l")) return PERM_LNK;
2866 else return PERM_REG/*???*/;
2867 }
2868
2869 static const char * mfile_perm_mstring(const ManifestFile * p){
2870 return mfile_permint_mstring(manifest_file_mperm(p));
2871 }
2872
2873 /*
2874 ** Internal helper for checkin_mini() and friends. Appends an F-card
2875 ** for p to pOut.
2876 */
2877 static void checkin_mini_append_fcard(Blob *pOut, const ManifestFile *p){
2878 if(p->zUuid){
2879 assert(*p->zUuid);
2880 blob_appendf(pOut, "F %F %s%s", p->zName,
2881 p->zUuid, mfile_perm_mstring(p));
2882 if(p->zPrior){
2883 assert(*p->zPrior);
2884 blob_appendf(pOut, " %F\n", p->zPrior);
2885 }else{
2886 blob_append(pOut, "\n", 1);
2887 }
2888 }else{
2889 /* File was removed from parent delta. */
2890 blob_appendf(pOut, "F %F\n", p->zName);
2891 }
2892 }
2893 /*
2894 ** Handles the F-card parts for create_manifest_mini().
2895 **
2896 ** If asDelta is true, F-cards will be handled as for a delta
2897 ** manifest, and the caller MUST have added a B-card to pOut before
2898 ** calling this.
2899 **
2900 ** Returns 1 on success, 0 on error, and writes any error message to
2901 ** pErr (if it's not NULL). The only non-immediately-fatal/panic error
2902 ** is if pCI->filePerm is PERM_LNK or pCI would update a PERM_LNK
2903 ** in-repo file.
2904 */
2905 static int create_manifest_mini_fcards( Blob * pOut,
2906 CheckinMiniInfo * pCI,
2907 int asDelta,
2908 Blob * pErr){
2909 int wroteThisCard = 0;
2910 const ManifestFile * pFile;
2911 int (*fncmp)(char const *, char const *) = /* filename comparator */
2912 filenames_are_case_sensitive()
2913 ? fossil_strcmp
2914 : fossil_stricmp;
2915 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2916 #define write_this_card(NAME) \
2917 blob_appendf(pOut, "F %F %b%s\n", (NAME), &pCI->fileHash, \
2918 mfile_permint_mstring(pCI->filePerm)); \
2919 wroteThisCard = 1
2920
2921 assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2922 assert(pCI->filePerm==PERM_REG || pCI->filePerm==PERM_EXE);
2923 if(PERM_LNK==pCI->filePerm){
2924 goto err_no_symlink;
2925 }
2926 manifest_file_rewind(pCI->pParent);
2927 if(asDelta!=0 && (pCI->pParent->zBaseline==0
2928 || pCI->pParent->nFile==0)){
2929 /* Parent is a baseline or a delta with no F-cards, so this is
2930 ** the simplest case: create a delta with a single F-card.
2931 */
2932 pFile = manifest_file_find(pCI->pParent, pCI->zFilename);
2933 if(pFile!=0 && manifest_file_mperm(pFile)==PERM_LNK){
2934 goto err_no_symlink;
2935 }
2936 write_this_card(pFile ? pFile->zName : pCI->zFilename);
2937 return 1;
2938 }
2939 while(1){
2940 int cmp;
2941 if(asDelta==0){
2942 pFile = manifest_file_next(pCI->pParent, 0);
2943 }else{
2944 /* Parent is a delta manifest with F-cards. Traversal of delta
2945 ** manifest file entries is normally done via
2946 ** manifest_file_next(), which takes into account the
2947 ** differences between the delta and its parent and returns
2948 ** F-cards from both. Each successive delta from the same
2949 ** baseline includes all F-card changes from the previous
2950 ** deltas, so we instead clone the parent's F-cards except for
2951 ** the one (if any) which matches the new file.
2952 */
2953 pFile = pCI->pParent->iFile < pCI->pParent->nFile
2954 ? &pCI->pParent->aFile[pCI->pParent->iFile++]
2955 : 0;
2956 }
2957 if(0==pFile) break;
2958 cmp = fncmp(pFile->zName, pCI->zFilename);
2959 if(cmp<0){
2960 checkin_mini_append_fcard(pOut,pFile);
2961 }else{
2962 if(cmp==0 || 0==wroteThisCard){
2963 assert(0==wroteThisCard);
2964 if(PERM_LNK==manifest_file_mperm(pFile)){
2965 goto err_no_symlink;
2966 }
2967 write_this_card(cmp==0 ? pFile->zName : pCI->zFilename);
2968 }
2969 if(cmp>0){
2970 assert(wroteThisCard!=0);
2971 checkin_mini_append_fcard(pOut,pFile);
2972 }
2973 }
2974 }
2975 if(wroteThisCard==0){
2976 write_this_card(pCI->zFilename);
2977 }
2978 return 1;
2979 err_no_symlink:
2980 mf_err((pErr,"Cannot commit or overwrite symlinks "
2981 "via mini-checkin."));
2982 return 0;
2983 #undef write_this_card
2984 #undef mf_err
2985 }
2986
2987 /*
2988 ** Creates a manifest file, written to pOut, from the state in the
2989 ** fully-populated and semantically valid pCI argument. pCI is not
2990 ** *semantically* modified but cannot be const because blob_str() may
2991 ** need to NUL-terminate any given blob.
2992 **
2993 ** Returns true on success. On error, returns 0 and, if pErr is not
2994 ** NULL, writes an error message there.
2995 **
2996 ** Intended only to be called via checkin_mini() or routines which
2997 ** have already completely vetted pCI.
2998 */
2999 static int create_manifest_mini( Blob * pOut, CheckinMiniInfo * pCI,
3000 Blob * pErr){
3001 Blob zCard = empty_blob; /* Z-card checksum */
3002 int asDelta = 0;
3003 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
3004
3005 assert(blob_str(&pCI->fileHash));
3006 assert(pCI->pParent);
3007 assert(pCI->zFilename);
3008 assert(pCI->zUser);
3009 assert(pCI->zDate);
3010
3011 /* Potential TODOs include...
3012 **
3013 ** - Maybe add support for tags. Those can be edited via /info page,
3014 ** and feel like YAGNI/feature creep for this purpose.
3015 */
3016 blob_zero(pOut);
3017 manifest_file_rewind(pCI->pParent) /* force load of baseline */;
3018 /* Determine whether we want to create a delta manifest... */
3019 if((CIMINI_PREFER_DELTA & pCI->flags)
3020 && ((CIMINI_STRONGLY_PREFER_DELTA & pCI->flags)
3021 || (pCI->pParent->pBaseline
3022 ? pCI->pParent->pBaseline
3023 : pCI->pParent)->nFile > 15
3024 /* 15 is arbitrary: don't create a delta when there is only a
3025 ** tiny gain for doing so. That heuristic is not *quite*
3026 ** right, in that when we're deriving from another delta, we
3027 ** really should compare the F-card count between it and its
3028 ** baseline, and create a delta if the baseline has (say)
3029 ** twice or more as many F-cards as the previous delta. */)
3030 && !db_get_boolean("forbid-delta-manifests",0)
3031 ){
3032 asDelta = 1;
3033 blob_appendf(pOut, "B %s\n",
3034 pCI->pParent->zBaseline
3035 ? pCI->pParent->zBaseline
3036 : pCI->zParentUuid);
3037 }
3038 blob_reserve(pOut, 1024 *
3039 (asDelta ? 2 : pCI->pParent->nFile/11+1
3040 /* In the fossil core repo, each 12-ish F-cards (on
3041 ** average) take up roughly 1kb */));
3042 if(blob_size(&pCI->comment)!=0){
3043 blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
3044 }else{
3045 blob_append(pOut, "C (no\\scomment)\n", 16);
3046 }
3047 blob_appendf(pOut, "D %s\n", pCI->zDate);
3048 if(create_manifest_mini_fcards(pOut,pCI,asDelta,pErr)==0){
3049 return 0;
3050 }
3051 if(pCI->zMimetype!=0 && pCI->zMimetype[0]!=0){
3052 blob_appendf(pOut, "N %F\n", pCI->zMimetype);
3053 }
3054 blob_appendf(pOut, "P %s\n", pCI->zParentUuid);
3055 blob_appendf(pOut, "U %F\n", pCI->zUser);
3056 md5sum_blob(pOut, &zCard);
3057 blob_appendf(pOut, "Z %b\n", &zCard);
3058 blob_reset(&zCard);
3059 return 1;
3060 #undef mf_err
3061 }
3062
3063 /*
3064 ** EXPERIMENTAL! Subject to change or removal at any time.
3065 **
3066 ** A so-called "single-file/mini/web checkin" is a slimmed-down form
3067 ** of the checkin command which accepts only a single file and is
3068 ** intended to accept edits to a file via the web interface or from
3069 ** the CLI from outside of a checkout.
3070 **
3071 ** Being fully non-interactive is a requirement for this function,
3072 ** thus it cannot perform autosync or similar activities.
3073 **
3074 ** This routine uses the state from the given fully-populated pCI
3075 ** argument to add pCI->fileContent to the database, and create and
3076 ** save a manifest for that change. Ownership of pCI and its contents
3077 ** are unchanged.
3078 **
3079 ** This function may may modify pCI as follows:
3080 **
3081 ** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL,
3082 ** then the other will be assigned based on its counterpart. Both
3083 ** may not be NULL.
3084 **
3085 ** - pCI->zDate is normalized to/replaced with a valid date/time
3086 ** string. If its original value cannot be validated then
3087 ** this function fails. If pCI->zDate is NULL, the current time
3088 ** is used.
3089 **
3090 ** - If the CIMINI_CONVERT_EOL_INHERIT flag is set,
3091 ** pCI->fileContent appears to be plain text, and its line-ending
3092 ** style differs from its previous version, it is converted to the
3093 ** same EOL style as the previous version. If this is done, the
3094 ** pCI->fileHash is re-computed. Note that only pCI->fileContent,
3095 ** not the original file, is affected by the conversion.
3096 **
3097 ** - If pCI->fileHash is empty, this routine populates it with the
3098 ** repository's preferred hash algorithm.
3099 **
3100 ** - pCI->comment may be converted to Unix-style newlines.
3101 **
3102 ** pCI's ownership is not modified.
3103 **
3104 ** This function validates several of the inputs and fails if any
3105 ** validation fails.
3106 **
3107 ** On error, returns false (0) and, if pErr is not NULL, writes a
3108 ** diagnostic message there.
3109 **
3110 ** Returns true on success. If pRid is not NULL, the RID of the
3111 ** resulting manifest is written to *pRid.
3112 **
3113 ** The checkin process is largely influenced by pCI->flags, and that
3114 ** must be populated before calling this. See the fossil_cimini_flags
3115 ** enum for the docs for each flag.
3116 */
3117 static int checkin_mini(CheckinMiniInfo * pCI, int *pRid, Blob * pErr){
3118 Blob mf = empty_blob; /* output manifest */
3119 int rid = 0, frid = 0; /* various RIDs */
3120 int isPrivate; /* whether this is private content
3121 or not */
3122 ManifestFile * zFilePrev; /* file entry from pCI->pParent */
3123 int prevFRid = 0; /* RID of file's prev. version */
3124 #define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
3125
3126 if(!(pCI->flags & CIMINI_DRY_RUN)){
3127 /* Until this feature is fully vetted, disallow it in the main
3128 ** fossil repo unless dry-run mode is being used. */
3129 char * zProjCode = db_get("project-code",0);
3130 assert(zProjCode);
3131 if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
3132 zProjCode)){
3133 fossil_fatal("Never, ever run this in/on the core fossil repo "
3134 "in non-dry-run mode until it's been well-vetted. "
3135 "Use a temp/test repo.");
3136 }
3137 fossil_free(zProjCode);
3138 }
3139 db_begin_transaction();
3140
3141 if(pCI->pParent==0 && pCI->zParentUuid==0){
3142 ci_err((pErr, "Cannot determine parent version."));
3143 }
3144 else if(pCI->pParent==0){
3145 pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
3146 if(pCI->pParent==0){
3147 ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
3148 }
3149 }else if(pCI->zParentUuid==0){
3150 pCI->zParentUuid = rid_to_uuid(pCI->pParent->rid);
3151 assert(pCI->zParentUuid);
3152 }
3153
3154 assert(pCI->pParent->rid>0);
3155 if(leaf_is_closed(pCI->pParent->rid)){
3156 ci_err((pErr,"Cannot commit to a closed leaf."));
3157 /* Remember that in order to override this we'd also need to
3158 ** cancel TAG_CLOSED on pCI->pParent. There would seem to be no
3159 ** reason we can't do that via the generated manifest, but the
3160 ** commit command does not offer that option, so mini-checkin
3161 ** probably shouldn't, either.
3162 */
3163 }
3164 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
3165 ci_err((pErr,"No such user: %s", pCI->zUser));
3166 }
3167 if(!(CIMINI_ALLOW_FORK & pCI->flags)
3168 && !is_a_leaf(pCI->pParent->rid)){
3169 ci_err((pErr,"Parent [%S] is not a leaf and forking is disabled.",
3170 pCI->zParentUuid));
3171 }
3172 if(!(CIMINI_ALLOW_MERGE_MARKER & pCI->flags)
3173 && contains_merge_marker(&pCI->fileContent)){
3174 ci_err((pErr,"Content appears to contain a merge conflict marker."));
3175 }
3176 if(!file_is_simple_pathname(pCI->zFilename, 1)){
3177 ci_err((pErr,"Invalid filename for use in a repository: %s",
3178 pCI->zFilename));
3179 }
3180 if(!(CIMINI_ALLOW_OLDER & pCI->flags)
3181 && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
3182 ci_err((pErr,"Checkin time (%s) may not be older "
3183 "than its parent (%z).",
3184 pCI->zDate,
3185 db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
3186 pCI->pParent->rDate)
3187 ));
3188 }
3189 {
3190 /*
3191 ** Normalize the timestamp. We don't use date_in_standard_format()
3192 ** because that has side-effects we don't want to trigger here.
3193 */
3194 char * zDVal = db_text(
3195 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
3196 pCI->zDate ? pCI->zDate : "now");
3197 if(zDVal==0 || zDVal[0]==0){
3198 fossil_free(zDVal);
3199 ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
3200 }
3201 fossil_free(pCI->zDate);
3202 pCI->zDate = zDVal;
3203 }
3204 { /* Confirm that only one EOL policy is in place. */
3205 int n = 0;
3206 if(CIMINI_CONVERT_EOL_INHERIT & pCI->flags) ++n;
3207 if(CIMINI_CONVERT_EOL_UNIX & pCI->flags) ++n;
3208 if(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags) ++n;
3209 if(n>1){
3210 ci_err((pErr,"More than 1 EOL conversion policy was specified."));
3211 }
3212 }
3213 /* Potential TODOs include:
3214 **
3215 ** - Commit allows an empty checkin only with a flag, but we
3216 ** currently disallow it entirely. Conform with commit?
3217 **
3218 ** Non-TODOs:
3219 **
3220 ** - Check for a commit lock would require auto-sync, which this
3221 ** code cannot do if it's going to be run via a web page.
3222 */
3223
3224 /*
3225 ** Confirm that pCI->zFilename can be found in pCI->pParent. If
3226 ** not, fail unless the CIMINI_ALLOW_NEW_FILE flag is set. This is
3227 ** admittedly an artificial limitation, not strictly necessary. We
3228 ** do it to hopefully reduce the chance of an "oops" where file
3229 ** X/Y/z gets committed as X/Y/Z or X/y/z due to a typo or
3230 ** case-sensitivity mismatch between the user/repo/filesystem, or
3231 ** some such.
3232 */
3233 manifest_file_rewind(pCI->pParent);
3234 zFilePrev = manifest_file_find(pCI->pParent, pCI->zFilename);
3235 if(!(CIMINI_ALLOW_NEW_FILE & pCI->flags)
3236 && (!zFilePrev
3237 || !zFilePrev->zUuid/*was removed from parent delta manifest*/)
3238 ){
3239 ci_err((pErr,"File [%s] not found in manifest [%S]. "
3240 "Adding new files is currently not permitted.",
3241 pCI->zFilename, pCI->zParentUuid));
3242 }else if(zFilePrev
3243 && manifest_file_mperm(zFilePrev)==PERM_LNK){
3244 ci_err((pErr,"Cannot save a symlink via a mini-checkin."));
3245 }
3246 if(zFilePrev){
3247 prevFRid = fast_uuid_to_rid(zFilePrev->zUuid);
3248 }
3249
3250 if(((CIMINI_CONVERT_EOL_INHERIT & pCI->flags)
3251 || (CIMINI_CONVERT_EOL_UNIX & pCI->flags)
3252 || (CIMINI_CONVERT_EOL_WINDOWS & pCI->flags))
3253 && blob_size(&pCI->fileContent)>0
3254 ){
3255 /* Convert to the requested EOL style. Note that this inherently
3256 ** runs a risk of breaking content, e.g. string literals which
3257 ** contain embedded newlines. Note that HTML5 specifies that
3258 ** form-submitted TEXTAREA content gets normalized to CRLF-style:
3259 **
3260 ** https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element
3261 */
3262 const int pseudoBinary = LOOK_LONG | LOOK_NUL;
3263 const int lookFlags = LOOK_CRLF | pseudoBinary;
3264 const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags );
3265 if(!(pseudoBinary & lookNew)){
3266 int rehash = 0;
3267 if(CIMINI_CONVERT_EOL_INHERIT & pCI->flags){
3268 Blob contentPrev = empty_blob;
3269 int lookOrig, nOrig;
3270 content_get(prevFRid, &contentPrev);
3271 lookOrig = looks_like_utf8(&contentPrev, lookFlags);
3272 nOrig = blob_size(&contentPrev);
3273 blob_reset(&contentPrev);
3274 if(nOrig>0 && lookOrig!=lookNew){
3275 /* If there is a newline-style mismatch, adjust the new
3276 ** content version to the previous style, then re-hash the
3277 ** content. Note that this means that what we insert is NOT
3278 ** what's in the filesystem.
3279 */
3280 if(!(lookOrig & LOOK_CRLF) && (lookNew & LOOK_CRLF)){
3281 /* Old has Unix-style, new has Windows-style. */
3282 blob_to_lf_only(&pCI->fileContent);
3283 rehash = 1;
3284 }else if((lookOrig & LOOK_CRLF) && !(lookNew & LOOK_CRLF)){
3285 /* Old has Windows-style, new has Unix-style. */
3286 blob_add_cr(&pCI->fileContent);
3287 rehash = 1;
3288 }
3289 }
3290 }else{
3291 const int oldSize = blob_size(&pCI->fileContent);
3292 if(CIMINI_CONVERT_EOL_UNIX & pCI->flags){
3293 blob_to_lf_only(&pCI->fileContent);
3294 }else{
3295 assert(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags);
3296 blob_add_cr(&pCI->fileContent);
3297 }
3298 if(blob_size(&pCI->fileContent)!=oldSize){
3299 rehash = 1;
3300 }
3301 }
3302 if(rehash!=0){
3303 hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
3304 }
3305 }
3306 }/* end EOL conversion */
3307
3308 if(blob_size(&pCI->fileHash)==0){
3309 /* Hash the content if it's not done already... */
3310 hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
3311 assert(blob_size(&pCI->fileHash)>0);
3312 }
3313 if(zFilePrev){
3314 /* Has this file been changed since its previous commit? Note
3315 ** that we have to delay this check until after the potentially
3316 ** expensive EOL conversion. */
3317 assert(blob_size(&pCI->fileHash));
3318 if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash))
3319 && manifest_file_mperm(zFilePrev)==pCI->filePerm){
3320 ci_err((pErr,"File is unchanged. Not saving."));
3321 }
3322 }
3323 #if 1
3324 /* Do we really want to normalize comment EOLs? Web-posting will
3325 ** submit them in CRLF format. */
3326 blob_to_lf_only(&pCI->comment);
3327 #endif
3328 /* Create, save, deltify, and crosslink the manifest... */
3329 if(create_manifest_mini(&mf, pCI, pErr)==0){
3330 return 0;
3331 }
3332 isPrivate = content_is_private(pCI->pParent->rid);
3333 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
3334 if(pCI->flags & CIMINI_DUMP_MANIFEST){
3335 fossil_print("%b", &mf);
3336 }
3337 if(pCI->pMfOut!=0){
3338 /* Cross-linking clears mf, so we have to copy it,
3339 ** instead of taking over its memory. */
3340 blob_reset(pCI->pMfOut);
3341 blob_append(pCI->pMfOut, blob_buffer(&mf), blob_size(&mf));
3342 }
3343 content_deltify(rid, &pCI->pParent->rid, 1, 0);
3344 manifest_crosslink(rid, &mf, 0);
3345 blob_reset(&mf);
3346 /* Save and deltify the file content... */
3347 frid = content_put_ex(&pCI->fileContent, blob_str(&pCI->fileHash),
3348 0, 0, isPrivate);
3349 if(zFilePrev!=0){
3350 assert(prevFRid>0);
3351 content_deltify(frid, &prevFRid, 1, 0);
3352 }
3353 db_end_transaction((CIMINI_DRY_RUN & pCI->flags) ? 1 : 0);
3354 if(pRid!=0){
3355 *pRid = rid;
3356 }
3357 return 1;
3358 ci_error:
3359 assert(db_transaction_nesting_depth()>0);
3360 db_end_transaction(1);
3361 return 0;
3362 #undef ci_err
3363 }
3364
3365 /*
3366 ** COMMAND: test-ci-mini
3367 **
3368 ** This is an on-going experiment, subject to change or removal at
3369 ** any time.
3370 **
3371 ** Usage: %fossil test-ci-mini ?OPTIONS? FILENAME
3372 **
3373 ** where FILENAME is a repo-relative name as it would appear in the
3374 ** vfile table.
3375 **
3376 ** Options:
3377 **
3378 ** --repository|-R REPO The repository file to commit to.
3379 ** --as FILENAME The repository-side name of the input
3380 ** file, relative to the top of the
3381 ** repository. Default is the same as the
3382 ** input file name.
3383 ** --comment|-m COMMENT Required checkin comment.
3384 ** --comment-file|-M FILE Reads checkin comment from the given file.
3385 ** --revision|-r VERSION Commit from this version. Default is
3386 ** the checkout version (if available) or
3387 ** trunk (if used without a checkout).
3388 ** --allow-fork Allows the commit to be made against a
3389 ** non-leaf parent. Note that no autosync
3390 ** is performed beforehand.
3391 ** --allow-merge-conflict Allows checkin of a file even if it
3392 ** appears to contain a fossil merge conflict
3393 ** marker.
3394 ** --user-override USER USER to use instead of the current
3395 ** default.
3396 ** --date-override DATETIME DATE to use instead of 'now'.
3397 ** --allow-older Allow a commit to be older than its
3398 ** ancestor.
3399 ** --convert-eol Convert EOL style of the checkin to match
3400 ** the previous version's content. Does not
3401 ** modify the input file, only the checked-in
3402 ** content.
3403 ** --delta Prefer to generate a delta manifest, if
3404 ** able. The forbid-delta-manifests repo
3405 ** config option trumps this, as do certain
3406 ** heuristics.
3407 ** --allow-new-file Allow addition of a new file this way.
3408 ** Disabled by default to avoid that case-
3409 ** sensitivity errors inadvertently lead to
3410 ** adding a new file where an update is
3411 ** intended.
3412 ** --dump-manifest|-d Dumps the generated manifest to stdout
3413 ** immediately after it's generated.
3414 ** --save-manifest FILE Saves the generated manifest to a file
3415 ** after successfully processing it.
3416 ** --wet-run Disables the default dry-run mode.
3417 **
3418 ** Example:
3419 **
3420 ** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c
3421 **
3422 */
3423 void test_ci_mini_cmd(){
3424 CheckinMiniInfo cimi; /* checkin state */
3425 int newRid = 0; /* RID of new version */
3426 const char * zFilename; /* argv[2] */
3427 const char * zComment; /* -m comment */
3428 const char * zCommentFile; /* -M FILE */
3429 const char * zAsFilename; /* --as filename */
3430 const char * zRevision; /* --revision|-r [=trunk|checkout] */
3431 const char * zUser; /* --user-override */
3432 const char * zDate; /* --date-override */
3433 char const * zManifestFile = 0;/* --save-manifest FILE */
3434
3435 /* This function should perform only the minimal "business logic" it
3436 ** needs in order to fully/properly populate the CheckinMiniInfo and
3437 ** then pass it on to checkin_mini() to do most of the validation
3438 ** and work. The point of this is to avoid duplicate code when a web
3439 ** front-end is added for checkin_mini().
3440 */
3441 CheckinMiniInfo_init(&cimi);
3442 zComment = find_option("comment","m",1);
3443 zCommentFile = find_option("comment-file","M",1);
3444 zAsFilename = find_option("as",0,1);
3445 zRevision = find_option("revision","r",1);
3446 zUser = find_option("user-override",0,1);
3447 zDate = find_option("date-override",0,1);
3448 zManifestFile = find_option("save-manifest",0,1);
3449 if(find_option("wet-run",0,0)==0){
3450 cimi.flags |= CIMINI_DRY_RUN;
3451 }
3452 if(find_option("allow-fork",0,0)!=0){
3453 cimi.flags |= CIMINI_ALLOW_FORK;
3454 }
3455 if(find_option("dump-manifest","d",0)!=0){
3456 cimi.flags |= CIMINI_DUMP_MANIFEST;
3457 }
3458 if(find_option("allow-merge-conflict",0,0)!=0){
3459 cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
3460 }
3461 if(find_option("allow-older",0,0)!=0){
3462 cimi.flags |= CIMINI_ALLOW_OLDER;
3463 }
3464 if(find_option("convert-eol-prev",0,0)!=0){
3465 cimi.flags |= CIMINI_CONVERT_EOL_INHERIT;
3466 }
3467 if(find_option("delta",0,0)!=0){
3468 cimi.flags |= CIMINI_PREFER_DELTA;
3469 }
3470 if(find_option("delta2",0,0)!=0){
3471 /* Undocumented. For testing only. */
3472 cimi.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA;
3473 }
3474 if(find_option("allow-new-file",0,0)!=0){
3475 cimi.flags |= CIMINI_ALLOW_NEW_FILE;
3476 }
3477 db_find_and_open_repository(0, 0);
3478 verify_all_options();
3479 user_select();
3480 if(g.argc!=3){
3481 usage("INFILE");
3482 }
3483 if(zComment && zCommentFile){
3484 fossil_fatal("Only one of -m or -M, not both, may be used.");
3485 }else{
3486 if(zCommentFile && *zCommentFile){
3487 blob_read_from_file(&cimi.comment, zCommentFile, ExtFILE);
3488 }else if(zComment && *zComment){
3489 blob_append(&cimi.comment, zComment, -1);
3490 }
3491 if(!blob_size(&cimi.comment)){
3492 fossil_fatal("Non-empty checkin comment is required.");
3493 }
3494 }
3495 db_begin_transaction();
3496 zFilename = g.argv[2];
3497 cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3498 cimi.filePerm = file_perm(zFilename, ExtFILE);
3499 cimi.zUser = mprintf("%s", zUser ? zUser : login_name());
3500 if(zDate){
3501 cimi.zDate = mprintf("%s", zDate);
3502 }
3503 if(zRevision==0 || zRevision[0]==0){
3504 if(g.localOpen/*checkout*/){
3505 zRevision = db_lget("checkout-hash", 0)/*leak*/;
3506 }else{
3507 zRevision = "trunk";
3508 }
3509 }
3510 name_to_uuid2(zRevision, "ci", &cimi.zParentUuid);
3511 if(cimi.zParentUuid==0){
3512 fossil_fatal("Cannot determine version to commit to.");
3513 }
3514 blob_read_from_file(&cimi.fileContent, zFilename, ExtFILE);
3515 {
3516 Blob theManifest = empty_blob; /* --save-manifest target */
3517 Blob errMsg = empty_blob;
3518 int rc;
3519 if(zManifestFile){
3520 cimi.pMfOut = &theManifest;
3521 }
3522 rc = checkin_mini(&cimi, &newRid, &errMsg);
3523 if(rc){
3524 assert(blob_size(&errMsg)==0);
3525 }else{
3526 assert(blob_size(&errMsg));
3527 fossil_fatal("%b", &errMsg);
3528 }
3529 if(zManifestFile){
3530 fossil_print("Writing manifest to: %s\n", zManifestFile);
3531 assert(blob_size(&theManifest)>0);
3532 blob_write_to_file(&theManifest, zManifestFile);
3533 blob_reset(&theManifest);
3534 }
3535 }
3536 if(newRid!=0){
3537 fossil_print("New version%s: %z\n",
3538 (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3539 rid_to_uuid(newRid));
3540 }
3541 db_end_transaction(0/*checkin_mini() will have triggered it to roll
3542 ** back in dry-run mode, but we need access to
3543 ** the transaction-written db state in this
3544 ** routine.*/);
3545 if(!(cimi.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){
3546 fossil_warning("The checkout state is now out of sync "
3547 "with regards to this commit. It needs to be "
3548 "'update'd or 'close'd and re-'open'ed.");
3549 }
3550 CheckinMiniInfo_cleanup(&cimi);
3551 }
3552
3553
3554 /*
3555 ** Returns true if the given filename qualifies for online editing by
3556 ** the current user, else returns false.
3557 **
3558 ** Editing requires that the user have the Write permission and that
3559 ** the filename match the glob defined by the fileedit-glob setting.
3560 ** A missing or empty value for that glob disables all editing.
3561 */
3562 int fileedit_is_editable(const char *zFilename){
3563 static Glob * pGlobs = 0;
3564 static int once = 0;
3565 if(0==g.perm.Write || zFilename==0 || *zFilename==0
3566 || (once!=0 && pGlobs==0)){
3567 return 0;
3568 }else if(0==pGlobs){
3569 char * zGlobs = db_get("fileedit-glob",0);
3570 once = 1;
3571 if(0==zGlobs) return 0;
3572 pGlobs = glob_create(zGlobs);
3573 fossil_free(zGlobs);
3574 }
3575 return glob_match(pGlobs, zFilename);
3576 }
3577
3578 static void fileedit_emit_script(int phase){
3579 if(0==phase){
3580 CX("<script nonce='%s'>", style_nonce());
3581 }else{
3582 CX("</script>\n");
3583 }
3584 }
3585
3586 #if 0
3587 /*
3588 ** This function is for potential TODO features for /fileedit.
3589 ** It's been tested with that code but is not currently used
3590 ** by it.
3591 */
3592 static void fileedit_emit_script_fetch(){
3593 fileedit_emit_script(0);
3594 CX("window.fossilFetch = function(path,opt){\n");
3595 CX(" if('function'===typeof opt){\n");
3596 CX(" opt={onload:opt};\n");
3597 CX(" }else{\n");
3598 CX(" opt=opt||{onload:function(r){console.debug('response:',r)}}\n");
3599 CX(" }\n");
3600 CX(" const url='%R/'+path, x=new XMLHttpRequest();\n");
3601 CX(" x.open(opt.method||'GET', url, true);\n");
3602 CX(" x.responseType=opt.responseType||'text';\n");
3603 CX(" if(opt.onload){\n");
3604 CX(" x.onload = function(e){\n");
3605 CX(" if(200!==this.status){\n");
3606 CX(" if(opt.onerror) opt.onerror(e);\n");
3607 CX(" return;\n");
3608 CX(" }\n");
3609 CX(" opt.onload(this.response);\n");
3610 CX(" }\n");
3611 CX(" }\n");
3612 CX(" x.send();");
3613 CX("};\n");
3614 fileedit_emit_script(1);
3615 };
3616 #endif /* fileedit_emit_script_fetch() */
3617
3618 /*
3619 ** Outputs a labeled checkbox element:
3620 **
3621 ** <span class='input-with-label' title={{zTip}}>
3622 ** <input type='checkbox' name={{zFieldName}} value={{zValue}}
3623 ** {{isChecked ? " checked : ""}}/>
3624 ** <span>{{zLabel}}</span>
3625 ** </span>
3626 **
3627 ** zFieldName, zLabel, and zValue are required. zTip is optional.
3628 */
3629 static void style_labeled_checkbox(const char *zFieldName,
3630 const char * zLabel,
3631 const char * zValue,
3632 const char * zTip,
3633 int isChecked){
3634 CX("<span class='input-with-label'");
3635 if(zTip && *zTip){
3636 CX(" title='%h'", zTip);
3637 }
3638 CX("><input type='checkbox' name='%s' value='%T'%s/>",
3639 zFieldName,
3640 zValue ? zValue : "", isChecked ? " checked" : "");
3641 CX("<span>%h</span></span>", zLabel);
3642 }
3643
3644 /*
3645 ** WEBPAGE: fileedit
3646 **
3647 ** EXPERIMENTAL and subject to change and removal at any time. The goal
3648 ** is to allow online edits of files.
3649 **
3650 ** Query parameters:
3651 **
3652 ** file=FILENAME Repo-relative path to the file.
3653 ** r=VERSION Checkin version, using any unambiguous
3654 ** supported symbolic version name.
3655 **
3656 ** All other parameters are for internal use only, submitted via the
3657 ** form-submission process, and may change with any given revision of
3658 ** this code.
3659 */
3660 void fileedit_page(){
3661 const char * zFilename = PD("file",P("name")); /* filename */
3662 const char * zRev = P("r"); /* checkin version */
3663 const char * zContent = P("content"); /* file content */
3664 const char * zComment = P("comment"); /* checkin comment */
3665 CheckinMiniInfo cimi; /* Checkin state */
3666 int submitMode = 0; /* See mapping below */
3667 int vid, newVid = 0; /* checkin rid */
3668 char * zFileUuid = 0; /* File content UUID */
3669 int frid = 0; /* File content rid */
3670 Blob err = empty_blob; /* Error report */
3671 const char * zFlagCheck = 0; /* Temp url flag holder */
3672 Blob endScript = empty_blob; /* Script code to run at the
3673 end. This content will be
3674 combined into a single JS
3675 function call, thus each
3676 entry must end with a
3677 semicolon. */
3678 Stmt stmt = empty_Stmt;
3679 #define fail(EXPR) blob_appendf EXPR; goto end_footer
3680
3681 login_check_credentials();
3682 if( !g.perm.Write ){
3683 login_needed(g.anon.Write);
3684 return;
3685 }
3686 db_begin_transaction();
3687 CheckinMiniInfo_init(&cimi);
3688 submitMode = atoi(PD("submit","0"))
3689 /* Submit modes: 0=initial request,
3690 ** 1=submit (save), 2=preview, 3=diff */;
3691 zFlagCheck = P("comment_mimetype");
3692 if(zFlagCheck){
3693 cimi.zMimetype = mprintf("%s",zFlagCheck);
3694 zFlagCheck = 0;
3695 }
3696 cimi.zUser = mprintf("%s",g.zLogin);
3697
3698 style_header("File Editor");
3699 /* As of this point, don't use return or fossil_fatal(), use
3700 ** fail((&err,...)) instead so that we can be sure to do any
3701 ** cleanup and end the transaction cleanly.
3702 */
3703 if(!zRev || !*zRev || !zFilename || !*zFilename){
3704 fail((&err,"Missing required URL parameters."));
3705 }
3706 if(0==fileedit_is_editable(zFilename)){
3707 fail((&err,"Filename <code>%h</code> is disallowed "
3708 "by the <code>fileedit-glob</code> repository "
3709 "setting.",
3710 zFilename));
3711 }
3712 vid = symbolic_name_to_rid(zRev, "ci");
3713 if(0==vid){
3714 fail((&err,"Could not resolve checkin version."));
3715 }
3716
3717 /* Find the repo-side file entry or fail... */
3718 cimi.zParentUuid = rid_to_uuid(vid);
3719 db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
3720 "WHERE filename=%Q %s AND checkinID=%d",
3721 zFilename, filename_collation(), vid);
3722 if(SQLITE_ROW==db_step(&stmt)){
3723 const char * zPerm = db_column_text(&stmt, 1);
3724 cimi.filePerm = mfile_permstr_int(zPerm);
3725 if(PERM_LNK==cimi.filePerm){
3726 fail((&err,"Editing symlinks is not permitted."));
3727 }
3728 zFileUuid = mprintf("%s",db_column_text(&stmt, 0));
3729 }
3730 db_finalize(&stmt);
3731 if(!zFileUuid){
3732 fail((&err,"Checkin [%S] does not contain file: "
3733 "<code>%h</code>",
3734 cimi.zParentUuid, zFilename));
3735 }
3736 frid = fast_uuid_to_rid(zFileUuid);
3737 assert(frid);
3738
3739 /* Read file content from submit request or repo... */
3740 if(zContent==0){
3741 content_get(frid, &cimi.fileContent);
3742 zContent = blob_size(&cimi.fileContent)
3743 ? blob_str(&cimi.fileContent) : NULL;
3744 }else{
3745 blob_init(&cimi.fileContent,zContent,-1);
3746 }
3747 if(looks_like_binary(&cimi.fileContent)){
3748 fail((&err,"File appears to be binary. Cannot edit: "
3749 "<code>%h</code>",zFilename));
3750 }
3751
3752 /* All set. Here we go... */
3753
3754 CX("<h1>Editing:</h1>");
3755 CX("<p class='fileedit-hint'>");
3756 CX("File: <code>%h</code><br>"
3757 "Checkin Version: <code id='r-label'>%s</code><br>",
3758 zFilename, cimi.zParentUuid);
3759 CX("Permalink: <code>"
3760 "<a id='permalink' href='%R/fileedit?file=%T&r=%!S'>"
3761 "/fileedit?file=%T&r=%!S</a></code><br>"
3762 "(Clicking the permalink will reload the page and discard "
3763 "all edits!)",
3764 zFilename, cimi.zParentUuid,
3765 zFilename, cimi.zParentUuid);
3766 CX("</p>");
3767 CX("<p>This page is <em>far from complete</em> and may still have "
3768 "significant bugs. USE AT YOUR OWN RISK, preferably on a test "
3769 "repo.</p>\n");
3770
3771 CX("<form action='%R/fileedit' method='POST' "
3772 "class='fileedit-form'>\n");
3773
3774 /******* Hidden fields *******/
3775 CX("<input type='hidden' name='r' value='%s'>",
3776 cimi.zParentUuid);
3777 CX("<input type='hidden' name='file' value='%T'>",
3778 zFilename);
3779
3780 /******* Comment *******/
3781 CX("<h3>Checkin Comment</h3>\n");
3782 CX("<textarea name='comment' rows='3' cols='80'>");
3783 if(zComment && *zComment){
3784 CX("%h"/*%h? %s?*/, zComment);
3785 }
3786 CX("</textarea>\n");
3787 CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup "
3788 "syntax.</div>"/*TODO: radiobuttons for fossil/me/plain text*/);
3789
3790 /******* Content *******/
3791 CX("<h3>File Content</h3>\n");
3792 CX("<textarea name='content' id='fileedit-content' "
3793 "rows='20' cols='80'>");
3794 CX("Loading...");
3795 CX("</textarea>\n");
3796 /******* Flags/options *******/
3797 CX("<fieldset class='fileedit-options'>"
3798 "<legend>Options</legend><div>"
3799 /* Chrome does not sanely lay out multiple
3800 ** fieldset children after the <legend>, so
3801 ** a containing div is necessary. */);
3802 /*
3803 ** TODO?: date-override date selection field. Maybe use
3804 ** an input[type=datetime-local].
3805 */
3806 if(0==submitMode || P("dry_run")!=0){
3807 cimi.flags |= CIMINI_DRY_RUN;
3808 }
3809 style_labeled_checkbox("dry_run", "Dry-run?", "1",
3810 "In dry-run mode, the Save button performs "
3811 "all work needed for saving but then rolls "
3812 "back the transaction, and thus does not "
3813 "really save.",
3814 cimi.flags & CIMINI_DRY_RUN);
3815 if(P("allow_fork")!=0){
3816 cimi.flags |= CIMINI_ALLOW_FORK;
3817 }
3818 style_labeled_checkbox("allow_fork", "Allow fork?", "1",
3819 "Allow saving to create a fork?",
3820 cimi.flags & CIMINI_ALLOW_FORK);
3821 if(P("allow_older")!=0){
3822 cimi.flags |= CIMINI_ALLOW_OLDER;
3823 }
3824 style_labeled_checkbox("allow_older", "Allow older?", "1",
3825 "Allow saving against a parent version "
3826 "which has a newer timestamp?",
3827 cimi.flags & CIMINI_ALLOW_OLDER);
3828 if(P("exec_bit")!=0){
3829 cimi.filePerm = PERM_EXE;
3830 }
3831 style_labeled_checkbox("exec_bit", "Executable?", "1",
3832 "Set the executable bit?",
3833 PERM_EXE==cimi.filePerm);
3834 if(P("allow_merge_conflict")!=0){
3835 cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
3836 }
3837 style_labeled_checkbox("allow_merge_conflict",
3838 "Allow merge conflict markers?", "1",
3839 "Allow saving even if the content contains "
3840 "what appear to be fossil merge conflict "
3841 "markers?",
3842 cimi.flags & CIMINI_ALLOW_MERGE_MARKER);
3843 if(P("prefer_delta")!=0){
3844 cimi.flags |= CIMINI_PREFER_DELTA;
3845 }
3846 style_labeled_checkbox("prefer_delta",
3847 "Prefer delta manifest?", "1",
3848 "Will create a delta manifest, instead of "
3849 "baseline, if conditions are favorable to do "
3850 "so. This option is only a suggestion.",
3851 cimi.flags & CIMINI_PREFER_DELTA);
3852 {/* EOL conversion policy... */
3853 const int eolMode = submitMode==0 ? 0 : atoi(PD("eol","0"));
3854 switch(eolMode){
3855 case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
3856 case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
3857 default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
3858 }
3859 CX("<select name='eol' "
3860 "title='EOL conversion policy, noting that form-processing "
3861 "may implicitly change the line endings of the input.'>");
3862 CX("<option value='0'%s>Inherit EOLs</option>",
3863 (eolMode!=1 && eolMode!=2) ? " selected" : "");
3864 CX("<option value='1'%s/>Unix EOLs</option>",
3865 eolMode==1 ? " selected" : "");
3866 CX("<option value='2'%s>Windows EOLs</option>",
3867 eolMode==2 ? " selected" : "");
3868 CX("</select>");
3869 }
3870
3871 CX("</div></fieldset>") /* end of checkboxes */;
3872
3873 /******* Buttons *******/
3874 CX("<fieldset class='fileedit-options'>"
3875 "<legend>Tell the server to...</legend><div>");
3876 CX("<button type='submit' name='submit' value='1'>"
3877 "Save</button>");
3878 CX("<button type='submit' name='submit' value='2'>"
3879 "Preview (TODO)</button>");
3880 CX("<button type='submit' name='submit' value='3'>"
3881 "Diff (TODO)</button>");
3882 CX("</div></fieldset>");
3883
3884 /******* End of form *******/
3885 CX("</form>\n");
3886
3887 {
3888 /* Populate the editor...
3889 **
3890 ** To avoid all escaping-related issues, we have to do this one of
3891 ** two ways:
3892 **
3893 ** 1) Fetch the content via AJAX. That only works if the content
3894 ** is already in the db, but not for edited versions.
3895 **
3896 ** 2) Store the content as JSON and feed it into the textarea
3897 ** using JavaScript.
3898 */
3899 char const * zQuoted = 0;
3900 if(blob_size(&cimi.fileContent)>0){
3901 db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
3902 db_step(&stmt);
3903 zQuoted = db_column_text(&stmt,0);
3904 }
3905 blob_appendf(&endScript,
3906 "/* populate editor form */\n"
3907 "document.getElementById('fileedit-content')"
3908 ".value=%s;", zQuoted ? zQuoted : "'';\n");
3909 if(stmt.pStmt){
3910 db_finalize(&stmt);
3911 }
3912 }
3913
3914 if(1==submitMode/*save*/){
3915 Blob manifest = empty_blob;
3916 char * zNewUuid = 0;
3917 /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
3918 if(zComment && *zComment){
3919 blob_append(&cimi.comment, zComment, -1);
3920 }else{
3921 fail((&err,"Empty comment is not permitted."));
3922 }
3923 /*cimi.pParent = manifest_get(vid, CFTYPE_MANIFEST, 0);
3924 assert(cimi.pParent && "We know vid is valid.");*/
3925 cimi.zFilename = mprintf("%s",zFilename);
3926 cimi.pMfOut = &manifest;
3927 checkin_mini(&cimi, &newVid, &err);
3928 if(newVid!=0){
3929 zNewUuid = rid_to_uuid(newVid);
3930 CX("<h3>Manifest%s: %S</h3><pre>"
3931 "<code class='fileedit-manifest'>%h</code>"
3932 "</pre>",
3933 (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3934 zNewUuid, blob_str(&manifest));
3935 if(!(CIMINI_DRY_RUN & cimi.flags)){
3936 /* We need to update certain form fields and UI elements so
3937 ** they're not left pointing to the previous version. While
3938 ** we're at it, we'll re-enable dry-run mode for sanity's
3939 ** sake.
3940 */
3941 blob_appendf(&endScript,
3942 "/* Toggle dry-run back on */\n"
3943 "document.querySelector('input[type=checkbox]"
3944 "[name=dry_run]').checked=true;\n");
3945 blob_appendf(&endScript,
3946 "/* Update version number */\n"
3947 "document.querySelector('input[name=r]')"
3948 ".value=%Q;\n"
3949 "document.querySelector('#r-label')"
3950 ".innerText=%Q;\n",
3951 zNewUuid, zNewUuid);
3952 blob_appendf(&endScript,
3953 "/* Update permalink */\n"
3954 "const urlFull='%R/fileedit?file=%T&r=%!S';\n"
3955 "const urlShort='/fileedit?file=%T&r=%!S';\n"
3956 "let link=document.querySelector('#permalink');\n"
3957 "link.innerText=urlShort;\n"
3958 "link.setAttribute('href',urlFull);\n",
3959 zFilename, zNewUuid, zFilename, zNewUuid);
3960 }
3961 fossil_free(zNewUuid);
3962 zNewUuid = 0;
3963 }
3964 /* On error, the error message is in the err blob and will
3965 ** be emitted below. */
3966 cimi.pMfOut = 0;
3967 blob_reset(&manifest);
3968 }else if(2==submitMode/*preview*/){
3969 /* TODO */
3970 fail((&err,"Preview mode is still TODO."));
3971 }else if(3==submitMode/*diff*/){
3972 fail((&err,"Diff mode is still TODO."));
3973 }else{
3974 /* Ignore invalid submitMode value */
3975 goto end_footer;
3976 }
3977
3978 end_footer:
3979 zContent = 0;
3980 fossil_free(zFileUuid);
3981 if(stmt.pStmt){
3982 db_finalize(&stmt);
3983 }
3984 if(blob_size(&err)){
3985 CX("<div class='fileedit-error-report'>%s</div>",
3986 blob_str(&err));
3987 }
3988 blob_reset(&err);
3989 CheckinMiniInfo_cleanup(&cimi);
3990 if(blob_size(&endScript)>0){
3991 fileedit_emit_script(0);
3992 CX("(function(){\n");
3993 CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
3994 &endScript);
3995 CX("})();");
3996 fileedit_emit_script(1);
3997 }
3998 db_end_transaction(0/*noting that dry-run mode will have already
3999 ** set this to rollback mode. */);
4000 style_footer();
4001 }
4002
4003 DDED src/fileedit.c
--- src/checkin.c
+++ src/checkin.c
@@ -1406,11 +1406,11 @@
1406 ** Returns true if the checkin identified by the first parameter is
1407 ** older than the given (valid) date/time string, else returns false.
1408 ** Also returns true if rid does not refer to a checkin, but it is not
1409 ** intended to be used for that case.
1410 */
1411 int checkin_is_younger(
1412 int rid, /* The record ID of the ancestor */
1413 const char *zDate /* Date & time of the current check-in */
1414 ){
1415 return db_exists(
1416 "SELECT 1 FROM event"
@@ -2694,1308 +2694,5 @@
2694 fossil_print("**** warning: a fork has occurred *****\n");
2695 }else{
2696 leaf_ambiguity_warning(nvid,nvid);
2697 }
2698 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2699
2700 DDED src/fileedit.c
--- a/src/fileedit.c
+++ b/src/fileedit.c
@@ -0,0 +1,476 @@
1
+ostostost-to(char *)/
2
+into a manifest_mstring(constp){
3
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
4
+"
5
+ ** We don't strictly need a FORM because we manually cherry-pick and
6
+ ** submit the form data, but it being in a form allows us to easily
7
+ ** use the FormData type for serialization.
8
+ **
9
+ ** TODO?: we can almost certainly replace this element with a plain
10
+ ** DIV, which would eliminate the event-handling hassles of trying
11
+ ** to suppress the submit... but it would also eliminate the option
12
+ ** of using HTML form field validation.
13
+ */
14
+ CX("<fo' "
15
+ "id='fileedit-form'>****** End of form **
16
+/*
17
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_s_page_script();
18
+);
19
+ }ostostost-to(char *)/
20
+into a manifest_mstring(constp){
21
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
22
+"
23
+ ** We don't strictly need a FORM because we manually cherry-pick and
24
+ ** submit the form data, but itag(0);
25
+ CX("%s\n", builtin_text("zMime ? zMime :pagem field validation.
26
+ */
27
+ CX("<fo' "
28
+ "id='fileedit-form'>****** End of form **
29
+/*
30
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
31
+);
32
+ }ostostost-to(char *)/
33
+into a manifest_mstring(constp){
34
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
35
+"
36
+ ** We don't strictly need a FORM because we manually cherry-pick and
37
+ ** submit the form data, but itag(0);
38
+ CX("%s\n", builtin_text("zMime ? zMime :ostostost-to(char *)/
39
+into a manifest_mstring(constp){
40
+ return mfile_perm0, &vidQueryfile=FILENAME content=text;
41
+ }file=FILENAME)ut(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
42
+);Mimetype; string(constp){
43
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
44
+"
45
+ ** We don't strictly need a FORM because we manually cherry-pick and
46
+ ** submit the form data, but itag(0);
47
+ CX("%s\n", builtin_text("zMostostemit_page_script(){fetch(tabs(/* ostostost-to(char *)/
48
+into a manifest_mstring(constp){
49
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
50
+"
51
+ ** We don't strictly need a FORM because we manually cherry-pick and
52
+ ** submit the form data, but it being in a form allows us to easily
53
+ ** use the FormData type for serialization.
54
+ **
55
+ ** TODO?: we can almost certainly replace this element with a plain
56
+ ** DIV, which would eliminate the event-handling hassles of trying
57
+ ** to suppress the submit... but it would also eliminate the option
58
+ ** of using HTML form field validation.
59
+ */
60
+ CX("<fo' "
61
+ "id='fileedit-form'>****** End of form **
62
+/*
63
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
64
+);
65
+ }ostostost-to(char *)/
66
+into a manifest_mstring(constp){
67
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
68
+"
69
+ ** We don't strictly need a FORM because we manually cherry-pick and
70
+ ** submit the form data, but itag(0);
71
+ CX("%s\n", builtin_text("zMime ? zMime :enum submit_modes {
72
+ SUBMIT_NONE = 0, SUBMIT_SAVE, SUBMIT_PREVIEW,
73
+ SUBMIT_DIFF_SBS, SUBMIT_DIFF_UNIFIED,
74
+ SUBMIT_end /* sentinel for range validation */
75
+ }; = P("r"); /* file cont = P("comment"); /*submitMode = SUBMIT_NONE;/* See mapping belo/*
76
+** Emits a script tag which defines window.fossil.fetch(), which works
77
+** similarly (not identically) to the not-quite-ubiquitous global
78
+** fetch().
79
+**
80
+** JS usages:
81
+**
82
+** fossilFetch( URI, onLoadCallback );
83
+**
84
+** fossilFetch( URI, optionsObject );
85
+**
86
+** Noting that URI must be repository and
87
+** must not start with a slash (if it does, it is stripped). It gets
88
+** %R/ prepended to it.
89
+**
90
+** The optionsObject may be an onload callback or an object with any
91
+** of these properties:
92
+**
93
+** - onload: callback(responseData) (default = output response to
94
+** console).
95
+**
96
+** - onerror: callback(XHR onload event) (default = console output)
97
+**
98
+** - method: 'POST' | 'GET' (default = 'GET')
99
+**
100
+** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
101
+** Document, FormData, Blob, File, ArrayBuffer), or a plain object
102
+** or array, either of which gets JSON.stringify()'d. If set then
103
+** the method is automatically set to 'POST'. If an object/array is
104
+** converted to JSON, the content-type is set to 'application/json'.
105
+** By default XHR2 will set the content type based on the payload
106
+** type.
107
+**
108
+** - contentType: Optional request content type when POSTing. Ignored
109
+** if the method is not 'POST'.
110
+**
111
+** - responseType: optional string. One of ("text", "arraybuffer",
112
+** "blob", or "document") (as specified by XHR2). Default = "text".
113
+**
114
+** - urlParams: string|object. If a string, it is assumed to be a
115
+** URI-encoded list of params in the form "key1=val1&key2=val2...",
116
+** with NO leading '?'. If it is an object, all of its properties
117
+** get converted to that form. Either way, the parameters get
118
+** appended to t{0);
119
+ CX("fossil.fetch = function(path,opt){\n");
120
+ CX(" if('/'===path[0]) path = path.substr(1);\n");
121
+ CX(" if(!opt){\n");
122
+ CX(" opt = {onload:(r)=>console.debug('response:',r)};\n");
123
+ CX(" }else if('function'===typeof opt){\n");
124
+ CX(" opt={onload:opt,\n");
125
+ CX(" onerror:(e)=>console.error('ajax error:',e)};\n");
126
+ CX(" }\n");
127
+ CX(" let payload = opt.payload;\n");
128
+ CX(" if(payload){\n");
129
+ CX(" opt.method = 'POST';\n");
130
+ CX(" if(!(payload instanceof FormData)\n");
131
+ CX(" && !(payload instanceof Document)\n");
132
+ CX(" && !(payload instanceof Blob)\n");
133
+ CX(" && !(payload instanceof File)\n");
134
+ CX(" && !(payload instanceof ArrayBuffer)){\n");
135
+ CX(" if('object'===typeof payload || payload instanceof Array){\n");
136
+ CX(" payload = JSON.stringify(payload);\n");
137
+ CX(" opt.contentType = 'application/json';\n");
138
+ CX(" }\n");
139
+ CX(" }\n");
140
+ CX(" }\n");
141
+ CX(" const url=['%R/'+path], x=new XMLHest();\n");
142
+ CX(" if(opt.urlParams){\n");
143
+ CX(" url.push('?');\n");
144
+ CX(" if('string'===typeof opt.urlParams){\n");
145
+ CX(" url.push(opt.urlParams);\n");
146
+ CX(" }else{/*assume object*/\n");
147
+ CX(" let k, i = 0;\n");
148
+ CX(" for( k in opt.urlParams ){\n");
149
+ CX(" if(i++) url.push('&');\n");
150
+ CX(" url.push(k,'=',encodeURIComponent(opt.urlParams[k]));\n");
151
+ CX(" }\n");
152
+ CX(" }\n");
153
+ CX(" }\n");
154
+ CX(" if('POST'===opt.method && 'string'===typeof opt.contentType){\n");
155
+ CX(" x.setRequestHeader('Content-Type',opt.contentType);\n");
156
+ CX(" }\n");
157
+ CX(" x.open(opt.method||'GET', url.join(''), true);\n");
158
+ CX(" x.responseType=opt.responseType||'text';\n");
159
+ CX(" if(opt.onload){\n");
160
+ CX(" x.onload = function(e){\n");
161
+ CX(" if(200!==this.status){\n");
162
+ CX(" if(opt.onerror) opt.onerror(e);\n");
163
+ CX(" return;\n");
164
+ CX(" }\n");
165
+ CX(" opt.onload(this.response);\n");
166
+ CX(" }\n");
167
+ CX(" }\n");
168
+ CX(" if(payload) x.send(payload);\n");
169
+ CX(" else x.send();\n");
170
+ CX("};\n";CX("<divpreview'>");
171
+static (int phase){
172
+ if(0==phase){
173
+ CX("<script nonce='%s'>", style_nonce(</script>\n");
174
+ }FWhere tbject with any of these
175
+**no-op. It getsODOs, if needed, include:
176
+**
177
+** optionsObject.params: object map of key/value pairs to append to the
178
+** URI.
179
+**
180
+** optionsObject.payload: string or JSON-able object to POST as the
181
+** payload.
182
+**
183
+*/
184
+static (0);
185
+ CX("window.fossilF=opt||{onload:function(r){}const url='%R/'+pathx.send();");
186
+ CX("};\n");
187
+ (1);
188
+};
189
+
190
+/*
191
+** Outputs a labeled checkbox element:
192
+**
193
+** <span class='input-with-label' title={{zTip}}>
194
+** <input type='checkbox' name={{zFieldName}} value={{zValue}}
195
+** {{isChecked ? " checked : ""}}/>
196
+** <span>{{zLabel}}</span>
197
+** </span>
198
+**
199
+** zFieldName, zLabel, and zValue are required. zTip is optional= {onload:(r)=>coconst char *zField>console.error('ajax eronst char * zLabelValueTip int isChecked){input-with-lab1234 = 1, SUBMIT_PREVIEW Mimetype!=0 && pCI->zspanspanMimetyp-formh3>Checkin Comm"/*TODO: radiobuttons CX("<select n"
200
+ "'>");
201
+ CX("<option value='0'%s>Inherit EOLs</option>",
202
+ (eolMode!=1 && eolMode!=2)CX("<option value='2'%s>Windows EOLs</option>",
203
+ eolMode==2CX("</select>"CX("<br>");
204
+ CX("<select name=''>\n" disabled>Preview Mode</optioGuess</option>",
205
+ FE_RENDER_GUESSWiki/Markdown</option>",
206
+ FE_RENDER_WIKIHTML (iframe)</option>",
207
+ Plain Text</option>",
208
+ CX("</select>\n"nt i;CX("<select name='' "
209
+ "title='HTML "
210
+ "preview.'>\n");
211
+ CX("<option disabled value='40'>HTML Preview Height (EMs)"
212
+ "</option>\n");
213
+ for( i = PLAIN_TEXT = 0int r-to(char *)/
214
+into a ma manifest_case:Options which depend on the current submitMode or
215
+ the file's preview rendeFileMime);
216
+ CX("<br>r == rmp; Reload"
217
+"
218
+ ** We don't strictly need a FORM because we manually cherry-pick and
219
+ ** submit the form data, but itag(0);
220
+ CX("%s\n", builtin_text("zMime ? zMime :pagem field validation.
221
+ */
222
+ CX("<fo' "
223
+ "id='fileedit-form'>****** End of form **
224
+/*
225
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
226
+);
227
+ }ostostost-to(char *)/
228
+into a manifest_mstring(constp){
229
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
230
+"
231
+ ** We don't strictly need a FORM because we manually cherry-pick and
232
+ ** submit the form data, but itag(0);
233
+ CX("%s\n", builtin_text("zMime ? zMime :ostostost-to(char *)/
234
+into a manifest_mstring(constp){
235
+ return mfile_perm0, &vidQueryfile=FILENAME content=text;
236
+ }file=FILENAME)ut(){ostostost-to(char *)/
237
+into a manifest_mstring(constp){
238
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
239
+"
240
+ ** We don't strictly need a FORM because we manually cherry-pick and
241
+ ** submit the form data, but it being in a form allows us to easily
242
+ ** use the FormData type for serialization.
243
+ **
244
+ ** TODO?: we can almost certainly replace this element with a plain
245
+ ** DIV, which would eliminate the event-handling hassles of trying
246
+ ** to suppress the submit... but it would also eliminate the option
247
+ ** of using HTML form field validation.
248
+ */
249
+ CX("<fo' "
250
+ "id='fileedit-form'>****** End of form **
251
+/*
252
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_s_page_script();
253
+);
254
+ }ostostost-to(char *)/
255
+into a manifest_mstring(constp){
256
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
257
+"
258
+ ** We don't strictly need a FORM because we manually cherry-pick and
259
+ ** submit the form data, but itag(0);
260
+ CX("%s\n", builtin_text("zMime ? zMime :pagem field validation.
261
+ */
262
+ CX("<fo' "
263
+ "id='fileedit-form'>****** End of form **
264
+/*
265
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
266
+);
267
+ }ostostost-to(char *)/
268
+into a manifest_mstring(constp){
269
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
270
+"
271
+ ** We don't strictly need a FORM because we manually cherry-pick and
272
+ ** submit the form data, but itag(0);
273
+ CX("%s\n", builtin_text("zMime ? zMime :ostostost-to(char *)/
274
+into a manifest_mstring(constp){
275
+ return mfile_perm0, &vidQueryfile=FILENAME content=text;
276
+ }file=FILENAME)ut(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
277
+);Mimetype; string(constp){
278
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
279
+"
280
+ ** We don't strictly need a FORM because we manually cherry-pick and
281
+ ** submit the form data, but itag(0);
282
+ CX("%s\n", builtin_text("zMostostemit_page_script(){fetch(tabs(/* ostostost-to(char *)/
283
+into a manifest_mstring(constp){
284
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
285
+"
286
+ ** We don't strictly need a FORM because we manually cherry-pick and
287
+ ** submit the form data, but it being in a form allows us to easily
288
+ ** use the FormData type for serialization.
289
+ **
290
+ ** TODO?: we can almost certainly replace this element with a plain
291
+ ** DIV, which would eliminate the event-handling hassles of trying
292
+ ** to suppress the submit... but it would also eliminate the option
293
+ ** of using HTML form field validation.
294
+ */
295
+ CX("<fo' "
296
+ "id='fileedit-form'>****** End of form **
297
+/*
298
+** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
299
+);
300
+ }ostostost-to(char *)/
301
+into a manifest_mstring(constp){
302
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
303
+"
304
+ ** We don't strictly need a FORM because we manually cherry-pick and
305
+ ** submit the form data, but itag(0);
306
+ CX("%s\n", builtin_text("zMime ? zMime :enum submit_modes {
307
+ SUBMIT_NONE = 0, SUBMIT_SAVE, SUBMIT_PREVIEW,
308
+ SUBMIT_DIFF_SBS, SUBMIT_DIFF_UNIFIED,
309
+ SUBMIT_end /* sentinel for range validation */
310
+ }; = P("r"); /* file cont = P("comment"); /*submitMode = SUBMIT_NONE;/* See mapping belo/*
311
+** Emits a script tag which defines window.fossil.fetch(), which works
312
+** similarly (not identically) to the not-quite-ubiquitous global
313
+** fetch().
314
+**
315
+** JS usages:
316
+**
317
+** fossilFetch( URI, onLoadCallback );
318
+**
319
+** fossilFetch( URI, optionsObject );
320
+**
321
+** Noting that URI must be repository and
322
+** must not start with a slash (if it does, it is stripped). It gets
323
+** %R/ prepended to it.
324
+**
325
+** The optionsObject may be an onload callback or an object with any
326
+** of these properties:
327
+**
328
+** - onload: callback(responseData) (default = output response to
329
+** console).
330
+**
331
+** - onerror: callback(XHR onload event) (default = console output)
332
+**
333
+** - method: 'POST' | 'GET' (default = 'GET')
334
+**
335
+** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
336
+** Document, FormData, Blob, File, ArrayBuffer), or a plain object
337
+** or array, either of which gets JSON.stringify()'d. If set then
338
+** the method is automatically set to 'POST'. If an object/array is
339
+** converted to JSON, the content-type is set to 'application/json'.
340
+** By default XHR2 will set the content type based on the payload
341
+** type.
342
+**
343
+** - contentType: Optional request content type when POSTing. Ignored
344
+** if the method is not 'POST'.
345
+**
346
+** - responseType: optional string. One of ("text", "arraybuffer",
347
+** "blob", or "document") (as specified by XHR2). Default = "text".
348
+**
349
+** - urlParams: string|object. If a string, it is assumed to be a
350
+** URI-encoded list of params in the form "key1=val1&key2=val2...",
351
+** with NO leading '?'. If it is an object, all of its properties
352
+** get converted to that form. Either way, the parameters get
353
+** appended to t{0);
354
+ CX("fossil.fetch = function(path,opt){\n");
355
+ CX(" if('/'===path[0]) path = path.substr(1);\n");
356
+ CX(" if(!opt){\n");
357
+ CX(" opt = {onload:(r)=>console.debug('response:',r)};\n");
358
+ CX(" }else if('function'===typeof opt){\n");
359
+ CX(" opt={onload:opt,\n");
360
+ CX(" onerror:(e)=>console.error('ajax error:',e)};\n");
361
+ CX(" }\n");
362
+ CX(" let payload = opt.payload;\n");
363
+ CX(" if(payload){\n");
364
+ CX(" opt.method = 'POST';\n");
365
+ CX(" if(!(payload instanceof FormData)\n");
366
+ CX(" && !(payload instanceof Document)\n");
367
+ CX(" && !(payload instanceof Blob)\n");
368
+ CX(" && !(payload instanceof File)\n");
369
+ CX(" && !(payload instanceof ArrayBuffer)){\n");
370
+ CX(" if('object'===typeof payload || payload instanceof Array){\n");
371
+ CX(" payload = JSON.stringify(payload);\n");
372
+ CX(" opt.contentType = 'application/json';\n");
373
+ CX(" }\n");
374
+ CX(" }\n");
375
+ CX(" }\n");
376
+ CX(" const url=['%R/'+path], x=new XMLHest();\n");
377
+ CX(" if(opt.urlParams){\n");
378
+ CX(" url.push('?');\n");
379
+ CX(" if('string'===typeof opt.urlParams){\n");
380
+ CX(" url.push(opt.urlParams);\n");
381
+ CX(" }else{/*assume object*/\n");
382
+ CX(" let k, i = 0;\n");
383
+ CX(" for( k in opt.urlParams ){\n");
384
+ CX(" if(i++) url.push('&');\n");
385
+ CX(" url.push(k,'=',encodeURIComponent(opt.urlParams[k]));\n");
386
+ CX(" }\n");
387
+ CX(" }\n");
388
+ CX(" }\n");
389
+ CX(" if('POST'===opt.method && 'string'===typeof opt.contentType){\n");
390
+ CX(" x.setRequestHeader('Content-Type',opt.contentType);\n");
391
+ CX(" }\n");
392
+ CX(" x.open(opt.method||'GET', url.join(''), true);\n");
393
+ CX(" x.responseType=opt.responseType||'text';\n");
394
+ CX(" if(opt.onload){\n");
395
+ CX(" x.onload = function(e){\n");
396
+ CX(" if(200!==this.status){\n");
397
+ CX(" if(opt.onerror) opt.onerror(e);\n");
398
+ CX(" return;\n");
399
+ CX(" }\n");
400
+ CX(" opt.onload(this.response);\n");
401
+ CX(" }\n");
402
+ CX(" }\n");
403
+ CX(" if(payload) x.send(payload);\n");
404
+ CX(" else x.send();\n");
405
+ CX("};\n";CX("<divpreview'>");
406
+static (int phase){
407
+ if(0==phase){
408
+ CX("<script nonce='%s'>", style_nonce(</script>\n");
409
+ }FWhere tbject with any of these
410
+**no-op. It getsODOs, if needed, include:
411
+**
412
+** optionsObject.params: object map of key/value pairs to append to the
413
+** URI.
414
+**
415
+** optionsObject.payload: string or JSON-able object to POST as the
416
+** payload.
417
+**
418
+*/
419
+static (0);
420
+ CX("window.fossilF=opt||{onload:function(r){}const url='%R/'+pathx.send();");
421
+ CX("};\n");
422
+ (1);
423
+};
424
+
425
+/*
426
+** Outputs a labeled checkbox element:
427
+**
428
+** <span class='input-with-label' title={{zTip}}>
429
+** <input type='checkbox' name={{zFieldName}} value={{zValue}}
430
+** {{isChecked ? " checked : ""}}/>
431
+** <span>{{zLabel}}</span>
432
+** </span>
433
+**
434
+** zFieldName, zLabel, and zValue are required. zTip is optional= {onload:(r)=>coconst char *zField>console.error('ajax eronst char * zLabelValueTip int isChecked){input-with-lab1234 = 1, SUBMIT_PREVIEW Mimetype!=0 && pCI->zspanspanMimetyp-formh3>Checkin Comm"/*TODO: radiobuttons CX("<select n"
435
+ "'>");
436
+ CX("<option value='0'%s>Inherit EOLs</option>",
437
+ (eolMode!=1 && eolMode!=2)CX("<option value='2'%s>Windows EOLs</option>",
438
+ eolMode==2CX("</select>"CX("<br>");
439
+ CX("<select name=''>\n" disabled>Preview Mode</optioGuess</option>",
440
+ FE_RENDER_GUESSWiki/Markdown</option>",
441
+ FE_RENDER_WIKIHTML (iframe)</option>",
442
+ Plain Text</option>",
443
+ CX("</select>\n"nt i;CX("<select name='' "
444
+ "title='HTML "
445
+ "preview.'>\n");
446
+ CX("<option disabled value='40'>HTML Preview Height (EMs)"
447
+ "</option>\n");
448
+ for( i = enum render_modes {PLA#if 0
449
+/*
450
+** This function is for potential TODO features for /fileedit.
451
+** It's been tested with that code but is not currently used
452
+** by i opt = {onload:#endif /* */ /* = P("r");version-to(char *)/
453
+into a manifest_mstring(constp){
454
+ return mfile_perostostost-to(char *)/
455
+into a manifest_mstring(constp){
456
+ return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
457
+"
458
+ ** We don't strictly need a FORM because we manually cherry-pick and
459
+ ** submit the form data, but it being in a form allows us to easily
460
+ ** use the FormData type for serialization.
461
+ **
462
+ ** TODO?: we can almost certainly replace this element with a plain
463
+ ** DIV, which would eliminate the event-handling hassles of trying
464
+ ** to suppress the submit... but it would also eliminate the option
465
+ ** of using HTML form.<code>%h</code><b d@AdL,_@AiW,7b@Aed,m@AmG,Af@Anb,H:CX("Loading...");Q@AtM,18@A~g,nY@B10,2: 3m@Bp0,S@Btz,e@Bqe,f@Btk,1F@Bz0,A:{
466
+ /* PL@B~V,5C:
467
+ **
468
+ ** To avoid all escaping-related issues, we have to do this one of
469
+ ** two ways:
470
+ **
471
+ ** 1) Fetch the content via AJAX. That only works if the content
472
+ ** is already in the db, but not for edited versions.
473
+ **
474
+ ** 2) Store the content as JSON and feed it into the textarea
475
+ ** using JavaScriptI@4YE,77@C0q,6W@CF0,a@AGC,J@7a0,G~@CLf,N@Cgl,8:zNewUuid1D@CiC,A: permalinkT@CZU,4y@Cle,L:zFilename, zNewUuid, 4A@CrQ,Y:/* TODO */
476
+ fail((&err,"PreviewW@Cyd,C8@Cxt,3QyXpB;
--- a/src/fileedit.c
+++ b/src/fileedit.c
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/fileedit.c
+++ b/src/fileedit.c
@@ -0,0 +1,476 @@
1 ostostost-to(char *)/
2 into a manifest_mstring(constp){
3 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
4 "
5 ** We don't strictly need a FORM because we manually cherry-pick and
6 ** submit the form data, but it being in a form allows us to easily
7 ** use the FormData type for serialization.
8 **
9 ** TODO?: we can almost certainly replace this element with a plain
10 ** DIV, which would eliminate the event-handling hassles of trying
11 ** to suppress the submit... but it would also eliminate the option
12 ** of using HTML form field validation.
13 */
14 CX("<fo' "
15 "id='fileedit-form'>****** End of form **
16 /*
17 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_s_page_script();
18 );
19 }ostostost-to(char *)/
20 into a manifest_mstring(constp){
21 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
22 "
23 ** We don't strictly need a FORM because we manually cherry-pick and
24 ** submit the form data, but itag(0);
25 CX("%s\n", builtin_text("zMime ? zMime :pagem field validation.
26 */
27 CX("<fo' "
28 "id='fileedit-form'>****** End of form **
29 /*
30 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
31 );
32 }ostostost-to(char *)/
33 into a manifest_mstring(constp){
34 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
35 "
36 ** We don't strictly need a FORM because we manually cherry-pick and
37 ** submit the form data, but itag(0);
38 CX("%s\n", builtin_text("zMime ? zMime :ostostost-to(char *)/
39 into a manifest_mstring(constp){
40 return mfile_perm0, &vidQueryfile=FILENAME content=text;
41 }file=FILENAME)ut(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
42 );Mimetype; string(constp){
43 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
44 "
45 ** We don't strictly need a FORM because we manually cherry-pick and
46 ** submit the form data, but itag(0);
47 CX("%s\n", builtin_text("zMostostemit_page_script(){fetch(tabs(/* ostostost-to(char *)/
48 into a manifest_mstring(constp){
49 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
50 "
51 ** We don't strictly need a FORM because we manually cherry-pick and
52 ** submit the form data, but it being in a form allows us to easily
53 ** use the FormData type for serialization.
54 **
55 ** TODO?: we can almost certainly replace this element with a plain
56 ** DIV, which would eliminate the event-handling hassles of trying
57 ** to suppress the submit... but it would also eliminate the option
58 ** of using HTML form field validation.
59 */
60 CX("<fo' "
61 "id='fileedit-form'>****** End of form **
62 /*
63 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
64 );
65 }ostostost-to(char *)/
66 into a manifest_mstring(constp){
67 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
68 "
69 ** We don't strictly need a FORM because we manually cherry-pick and
70 ** submit the form data, but itag(0);
71 CX("%s\n", builtin_text("zMime ? zMime :enum submit_modes {
72 SUBMIT_NONE = 0, SUBMIT_SAVE, SUBMIT_PREVIEW,
73 SUBMIT_DIFF_SBS, SUBMIT_DIFF_UNIFIED,
74 SUBMIT_end /* sentinel for range validation */
75 }; = P("r"); /* file cont = P("comment"); /*submitMode = SUBMIT_NONE;/* See mapping belo/*
76 ** Emits a script tag which defines window.fossil.fetch(), which works
77 ** similarly (not identically) to the not-quite-ubiquitous global
78 ** fetch().
79 **
80 ** JS usages:
81 **
82 ** fossilFetch( URI, onLoadCallback );
83 **
84 ** fossilFetch( URI, optionsObject );
85 **
86 ** Noting that URI must be repository and
87 ** must not start with a slash (if it does, it is stripped). It gets
88 ** %R/ prepended to it.
89 **
90 ** The optionsObject may be an onload callback or an object with any
91 ** of these properties:
92 **
93 ** - onload: callback(responseData) (default = output response to
94 ** console).
95 **
96 ** - onerror: callback(XHR onload event) (default = console output)
97 **
98 ** - method: 'POST' | 'GET' (default = 'GET')
99 **
100 ** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
101 ** Document, FormData, Blob, File, ArrayBuffer), or a plain object
102 ** or array, either of which gets JSON.stringify()'d. If set then
103 ** the method is automatically set to 'POST'. If an object/array is
104 ** converted to JSON, the content-type is set to 'application/json'.
105 ** By default XHR2 will set the content type based on the payload
106 ** type.
107 **
108 ** - contentType: Optional request content type when POSTing. Ignored
109 ** if the method is not 'POST'.
110 **
111 ** - responseType: optional string. One of ("text", "arraybuffer",
112 ** "blob", or "document") (as specified by XHR2). Default = "text".
113 **
114 ** - urlParams: string|object. If a string, it is assumed to be a
115 ** URI-encoded list of params in the form "key1=val1&key2=val2...",
116 ** with NO leading '?'. If it is an object, all of its properties
117 ** get converted to that form. Either way, the parameters get
118 ** appended to t{0);
119 CX("fossil.fetch = function(path,opt){\n");
120 CX(" if('/'===path[0]) path = path.substr(1);\n");
121 CX(" if(!opt){\n");
122 CX(" opt = {onload:(r)=>console.debug('response:',r)};\n");
123 CX(" }else if('function'===typeof opt){\n");
124 CX(" opt={onload:opt,\n");
125 CX(" onerror:(e)=>console.error('ajax error:',e)};\n");
126 CX(" }\n");
127 CX(" let payload = opt.payload;\n");
128 CX(" if(payload){\n");
129 CX(" opt.method = 'POST';\n");
130 CX(" if(!(payload instanceof FormData)\n");
131 CX(" && !(payload instanceof Document)\n");
132 CX(" && !(payload instanceof Blob)\n");
133 CX(" && !(payload instanceof File)\n");
134 CX(" && !(payload instanceof ArrayBuffer)){\n");
135 CX(" if('object'===typeof payload || payload instanceof Array){\n");
136 CX(" payload = JSON.stringify(payload);\n");
137 CX(" opt.contentType = 'application/json';\n");
138 CX(" }\n");
139 CX(" }\n");
140 CX(" }\n");
141 CX(" const url=['%R/'+path], x=new XMLHest();\n");
142 CX(" if(opt.urlParams){\n");
143 CX(" url.push('?');\n");
144 CX(" if('string'===typeof opt.urlParams){\n");
145 CX(" url.push(opt.urlParams);\n");
146 CX(" }else{/*assume object*/\n");
147 CX(" let k, i = 0;\n");
148 CX(" for( k in opt.urlParams ){\n");
149 CX(" if(i++) url.push('&');\n");
150 CX(" url.push(k,'=',encodeURIComponent(opt.urlParams[k]));\n");
151 CX(" }\n");
152 CX(" }\n");
153 CX(" }\n");
154 CX(" if('POST'===opt.method && 'string'===typeof opt.contentType){\n");
155 CX(" x.setRequestHeader('Content-Type',opt.contentType);\n");
156 CX(" }\n");
157 CX(" x.open(opt.method||'GET', url.join(''), true);\n");
158 CX(" x.responseType=opt.responseType||'text';\n");
159 CX(" if(opt.onload){\n");
160 CX(" x.onload = function(e){\n");
161 CX(" if(200!==this.status){\n");
162 CX(" if(opt.onerror) opt.onerror(e);\n");
163 CX(" return;\n");
164 CX(" }\n");
165 CX(" opt.onload(this.response);\n");
166 CX(" }\n");
167 CX(" }\n");
168 CX(" if(payload) x.send(payload);\n");
169 CX(" else x.send();\n");
170 CX("};\n";CX("<divpreview'>");
171 static (int phase){
172 if(0==phase){
173 CX("<script nonce='%s'>", style_nonce(</script>\n");
174 }FWhere tbject with any of these
175 **no-op. It getsODOs, if needed, include:
176 **
177 ** optionsObject.params: object map of key/value pairs to append to the
178 ** URI.
179 **
180 ** optionsObject.payload: string or JSON-able object to POST as the
181 ** payload.
182 **
183 */
184 static (0);
185 CX("window.fossilF=opt||{onload:function(r){}const url='%R/'+pathx.send();");
186 CX("};\n");
187 (1);
188 };
189
190 /*
191 ** Outputs a labeled checkbox element:
192 **
193 ** <span class='input-with-label' title={{zTip}}>
194 ** <input type='checkbox' name={{zFieldName}} value={{zValue}}
195 ** {{isChecked ? " checked : ""}}/>
196 ** <span>{{zLabel}}</span>
197 ** </span>
198 **
199 ** zFieldName, zLabel, and zValue are required. zTip is optional= {onload:(r)=>coconst char *zField>console.error('ajax eronst char * zLabelValueTip int isChecked){input-with-lab1234 = 1, SUBMIT_PREVIEW Mimetype!=0 && pCI->zspanspanMimetyp-formh3>Checkin Comm"/*TODO: radiobuttons CX("<select n"
200 "'>");
201 CX("<option value='0'%s>Inherit EOLs</option>",
202 (eolMode!=1 && eolMode!=2)CX("<option value='2'%s>Windows EOLs</option>",
203 eolMode==2CX("</select>"CX("<br>");
204 CX("<select name=''>\n" disabled>Preview Mode</optioGuess</option>",
205 FE_RENDER_GUESSWiki/Markdown</option>",
206 FE_RENDER_WIKIHTML (iframe)</option>",
207 Plain Text</option>",
208 CX("</select>\n"nt i;CX("<select name='' "
209 "title='HTML "
210 "preview.'>\n");
211 CX("<option disabled value='40'>HTML Preview Height (EMs)"
212 "</option>\n");
213 for( i = PLAIN_TEXT = 0int r-to(char *)/
214 into a ma manifest_case:Options which depend on the current submitMode or
215 the file's preview rendeFileMime);
216 CX("<br>r == rmp; Reload"
217 "
218 ** We don't strictly need a FORM because we manually cherry-pick and
219 ** submit the form data, but itag(0);
220 CX("%s\n", builtin_text("zMime ? zMime :pagem field validation.
221 */
222 CX("<fo' "
223 "id='fileedit-form'>****** End of form **
224 /*
225 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
226 );
227 }ostostost-to(char *)/
228 into a manifest_mstring(constp){
229 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
230 "
231 ** We don't strictly need a FORM because we manually cherry-pick and
232 ** submit the form data, but itag(0);
233 CX("%s\n", builtin_text("zMime ? zMime :ostostost-to(char *)/
234 into a manifest_mstring(constp){
235 return mfile_perm0, &vidQueryfile=FILENAME content=text;
236 }file=FILENAME)ut(){ostostost-to(char *)/
237 into a manifest_mstring(constp){
238 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
239 "
240 ** We don't strictly need a FORM because we manually cherry-pick and
241 ** submit the form data, but it being in a form allows us to easily
242 ** use the FormData type for serialization.
243 **
244 ** TODO?: we can almost certainly replace this element with a plain
245 ** DIV, which would eliminate the event-handling hassles of trying
246 ** to suppress the submit... but it would also eliminate the option
247 ** of using HTML form field validation.
248 */
249 CX("<fo' "
250 "id='fileedit-form'>****** End of form **
251 /*
252 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_s_page_script();
253 );
254 }ostostost-to(char *)/
255 into a manifest_mstring(constp){
256 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
257 "
258 ** We don't strictly need a FORM because we manually cherry-pick and
259 ** submit the form data, but itag(0);
260 CX("%s\n", builtin_text("zMime ? zMime :pagem field validation.
261 */
262 CX("<fo' "
263 "id='fileedit-form'>****** End of form **
264 /*
265 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
266 );
267 }ostostost-to(char *)/
268 into a manifest_mstring(constp){
269 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
270 "
271 ** We don't strictly need a FORM because we manually cherry-pick and
272 ** submit the form data, but itag(0);
273 CX("%s\n", builtin_text("zMime ? zMime :ostostost-to(char *)/
274 into a manifest_mstring(constp){
275 return mfile_perm0, &vidQueryfile=FILENAME content=text;
276 }file=FILENAME)ut(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
277 );Mimetype; string(constp){
278 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
279 "
280 ** We don't strictly need a FORM because we manually cherry-pick and
281 ** submit the form data, but itag(0);
282 CX("%s\n", builtin_text("zMostostemit_page_script(){fetch(tabs(/* ostostost-to(char *)/
283 into a manifest_mstring(constp){
284 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
285 "
286 ** We don't strictly need a FORM because we manually cherry-pick and
287 ** submit the form data, but it being in a form allows us to easily
288 ** use the FormData type for serialization.
289 **
290 ** TODO?: we can almost certainly replace this element with a plain
291 ** DIV, which would eliminate the event-handling hassles of trying
292 ** to suppress the submit... but it would also eliminate the option
293 ** of using HTML form field validation.
294 */
295 CX("<fo' "
296 "id='fileedit-form'>****** End of form **
297 /*
298 ** Emits u pagfileedit_emit_page_script(){fetch(tabs(/* File content UUIDfileedit_emit_page_script();
299 );
300 }ostostost-to(char *)/
301 into a manifest_mstring(constp){
302 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
303 "
304 ** We don't strictly need a FORM because we manually cherry-pick and
305 ** submit the form data, but itag(0);
306 CX("%s\n", builtin_text("zMime ? zMime :enum submit_modes {
307 SUBMIT_NONE = 0, SUBMIT_SAVE, SUBMIT_PREVIEW,
308 SUBMIT_DIFF_SBS, SUBMIT_DIFF_UNIFIED,
309 SUBMIT_end /* sentinel for range validation */
310 }; = P("r"); /* file cont = P("comment"); /*submitMode = SUBMIT_NONE;/* See mapping belo/*
311 ** Emits a script tag which defines window.fossil.fetch(), which works
312 ** similarly (not identically) to the not-quite-ubiquitous global
313 ** fetch().
314 **
315 ** JS usages:
316 **
317 ** fossilFetch( URI, onLoadCallback );
318 **
319 ** fossilFetch( URI, optionsObject );
320 **
321 ** Noting that URI must be repository and
322 ** must not start with a slash (if it does, it is stripped). It gets
323 ** %R/ prepended to it.
324 **
325 ** The optionsObject may be an onload callback or an object with any
326 ** of these properties:
327 **
328 ** - onload: callback(responseData) (default = output response to
329 ** console).
330 **
331 ** - onerror: callback(XHR onload event) (default = console output)
332 **
333 ** - method: 'POST' | 'GET' (default = 'GET')
334 **
335 ** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
336 ** Document, FormData, Blob, File, ArrayBuffer), or a plain object
337 ** or array, either of which gets JSON.stringify()'d. If set then
338 ** the method is automatically set to 'POST'. If an object/array is
339 ** converted to JSON, the content-type is set to 'application/json'.
340 ** By default XHR2 will set the content type based on the payload
341 ** type.
342 **
343 ** - contentType: Optional request content type when POSTing. Ignored
344 ** if the method is not 'POST'.
345 **
346 ** - responseType: optional string. One of ("text", "arraybuffer",
347 ** "blob", or "document") (as specified by XHR2). Default = "text".
348 **
349 ** - urlParams: string|object. If a string, it is assumed to be a
350 ** URI-encoded list of params in the form "key1=val1&key2=val2...",
351 ** with NO leading '?'. If it is an object, all of its properties
352 ** get converted to that form. Either way, the parameters get
353 ** appended to t{0);
354 CX("fossil.fetch = function(path,opt){\n");
355 CX(" if('/'===path[0]) path = path.substr(1);\n");
356 CX(" if(!opt){\n");
357 CX(" opt = {onload:(r)=>console.debug('response:',r)};\n");
358 CX(" }else if('function'===typeof opt){\n");
359 CX(" opt={onload:opt,\n");
360 CX(" onerror:(e)=>console.error('ajax error:',e)};\n");
361 CX(" }\n");
362 CX(" let payload = opt.payload;\n");
363 CX(" if(payload){\n");
364 CX(" opt.method = 'POST';\n");
365 CX(" if(!(payload instanceof FormData)\n");
366 CX(" && !(payload instanceof Document)\n");
367 CX(" && !(payload instanceof Blob)\n");
368 CX(" && !(payload instanceof File)\n");
369 CX(" && !(payload instanceof ArrayBuffer)){\n");
370 CX(" if('object'===typeof payload || payload instanceof Array){\n");
371 CX(" payload = JSON.stringify(payload);\n");
372 CX(" opt.contentType = 'application/json';\n");
373 CX(" }\n");
374 CX(" }\n");
375 CX(" }\n");
376 CX(" const url=['%R/'+path], x=new XMLHest();\n");
377 CX(" if(opt.urlParams){\n");
378 CX(" url.push('?');\n");
379 CX(" if('string'===typeof opt.urlParams){\n");
380 CX(" url.push(opt.urlParams);\n");
381 CX(" }else{/*assume object*/\n");
382 CX(" let k, i = 0;\n");
383 CX(" for( k in opt.urlParams ){\n");
384 CX(" if(i++) url.push('&');\n");
385 CX(" url.push(k,'=',encodeURIComponent(opt.urlParams[k]));\n");
386 CX(" }\n");
387 CX(" }\n");
388 CX(" }\n");
389 CX(" if('POST'===opt.method && 'string'===typeof opt.contentType){\n");
390 CX(" x.setRequestHeader('Content-Type',opt.contentType);\n");
391 CX(" }\n");
392 CX(" x.open(opt.method||'GET', url.join(''), true);\n");
393 CX(" x.responseType=opt.responseType||'text';\n");
394 CX(" if(opt.onload){\n");
395 CX(" x.onload = function(e){\n");
396 CX(" if(200!==this.status){\n");
397 CX(" if(opt.onerror) opt.onerror(e);\n");
398 CX(" return;\n");
399 CX(" }\n");
400 CX(" opt.onload(this.response);\n");
401 CX(" }\n");
402 CX(" }\n");
403 CX(" if(payload) x.send(payload);\n");
404 CX(" else x.send();\n");
405 CX("};\n";CX("<divpreview'>");
406 static (int phase){
407 if(0==phase){
408 CX("<script nonce='%s'>", style_nonce(</script>\n");
409 }FWhere tbject with any of these
410 **no-op. It getsODOs, if needed, include:
411 **
412 ** optionsObject.params: object map of key/value pairs to append to the
413 ** URI.
414 **
415 ** optionsObject.payload: string or JSON-able object to POST as the
416 ** payload.
417 **
418 */
419 static (0);
420 CX("window.fossilF=opt||{onload:function(r){}const url='%R/'+pathx.send();");
421 CX("};\n");
422 (1);
423 };
424
425 /*
426 ** Outputs a labeled checkbox element:
427 **
428 ** <span class='input-with-label' title={{zTip}}>
429 ** <input type='checkbox' name={{zFieldName}} value={{zValue}}
430 ** {{isChecked ? " checked : ""}}/>
431 ** <span>{{zLabel}}</span>
432 ** </span>
433 **
434 ** zFieldName, zLabel, and zValue are required. zTip is optional= {onload:(r)=>coconst char *zField>console.error('ajax eronst char * zLabelValueTip int isChecked){input-with-lab1234 = 1, SUBMIT_PREVIEW Mimetype!=0 && pCI->zspanspanMimetyp-formh3>Checkin Comm"/*TODO: radiobuttons CX("<select n"
435 "'>");
436 CX("<option value='0'%s>Inherit EOLs</option>",
437 (eolMode!=1 && eolMode!=2)CX("<option value='2'%s>Windows EOLs</option>",
438 eolMode==2CX("</select>"CX("<br>");
439 CX("<select name=''>\n" disabled>Preview Mode</optioGuess</option>",
440 FE_RENDER_GUESSWiki/Markdown</option>",
441 FE_RENDER_WIKIHTML (iframe)</option>",
442 Plain Text</option>",
443 CX("</select>\n"nt i;CX("<select name='' "
444 "title='HTML "
445 "preview.'>\n");
446 CX("<option disabled value='40'>HTML Preview Height (EMs)"
447 "</option>\n");
448 for( i = enum render_modes {PLA#if 0
449 /*
450 ** This function is for potential TODO features for /fileedit.
451 ** It's been tested with that code but is not currently used
452 ** by i opt = {onload:#endif /* */ /* = P("r");version-to(char *)/
453 into a manifest_mstring(constp){
454 return mfile_perostostost-to(char *)/
455 into a manifest_mstring(constp){
456 return mfile_perm_mstring(pFencatch(e){0'>Discard &amp; Reload"
457 "
458 ** We don't strictly need a FORM because we manually cherry-pick and
459 ** submit the form data, but it being in a form allows us to easily
460 ** use the FormData type for serialization.
461 **
462 ** TODO?: we can almost certainly replace this element with a plain
463 ** DIV, which would eliminate the event-handling hassles of trying
464 ** to suppress the submit... but it would also eliminate the option
465 ** of using HTML form.<code>%h</code><b d@AdL,_@AiW,7b@Aed,m@AmG,Af@Anb,H:CX("Loading...");Q@AtM,18@A~g,nY@B10,2: 3m@Bp0,S@Btz,e@Bqe,f@Btk,1F@Bz0,A:{
466 /* PL@B~V,5C:
467 **
468 ** To avoid all escaping-related issues, we have to do this one of
469 ** two ways:
470 **
471 ** 1) Fetch the content via AJAX. That only works if the content
472 ** is already in the db, but not for edited versions.
473 **
474 ** 2) Store the content as JSON and feed it into the textarea
475 ** using JavaScriptI@4YE,77@C0q,6W@CF0,a@AGC,J@7a0,G~@CLf,N@Cgl,8:zNewUuid1D@CiC,A: permalinkT@CZU,4y@Cle,L:zFilename, zNewUuid, 4A@CrQ,Y:/* TODO */
476 fail((&err,"PreviewW@Cyd,C8@Cxt,3QyXpB;
+12
--- src/main.mk
+++ src/main.mk
@@ -54,10 +54,11 @@
5454
$(SRCDIR)/etag.c \
5555
$(SRCDIR)/event.c \
5656
$(SRCDIR)/export.c \
5757
$(SRCDIR)/extcgi.c \
5858
$(SRCDIR)/file.c \
59
+ $(SRCDIR)/fileedit.c \
5960
$(SRCDIR)/finfo.c \
6061
$(SRCDIR)/foci.c \
6162
$(SRCDIR)/forum.c \
6263
$(SRCDIR)/fshell.c \
6364
$(SRCDIR)/fusefs.c \
@@ -287,10 +288,11 @@
287288
$(OBJDIR)/etag_.c \
288289
$(OBJDIR)/event_.c \
289290
$(OBJDIR)/export_.c \
290291
$(OBJDIR)/extcgi_.c \
291292
$(OBJDIR)/file_.c \
293
+ $(OBJDIR)/fileedit_.c \
292294
$(OBJDIR)/finfo_.c \
293295
$(OBJDIR)/foci_.c \
294296
$(OBJDIR)/forum_.c \
295297
$(OBJDIR)/fshell_.c \
296298
$(OBJDIR)/fusefs_.c \
@@ -429,10 +431,11 @@
429431
$(OBJDIR)/etag.o \
430432
$(OBJDIR)/event.o \
431433
$(OBJDIR)/export.o \
432434
$(OBJDIR)/extcgi.o \
433435
$(OBJDIR)/file.o \
436
+ $(OBJDIR)/fileedit.o \
434437
$(OBJDIR)/finfo.o \
435438
$(OBJDIR)/foci.o \
436439
$(OBJDIR)/forum.o \
437440
$(OBJDIR)/fshell.o \
438441
$(OBJDIR)/fusefs.o \
@@ -766,10 +769,11 @@
766769
$(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
767770
$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
768771
$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
769772
$(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \
770773
$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
774
+ $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \
771775
$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
772776
$(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
773777
$(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \
774778
$(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
775779
$(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \
@@ -1192,10 +1196,18 @@
11921196
11931197
$(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h
11941198
$(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
11951199
11961200
$(OBJDIR)/file.h: $(OBJDIR)/headers
1201
+
1202
+$(OBJDIR)/fileedit_.c: $(SRCDIR)/fileedit.c $(OBJDIR)/translate
1203
+ $(OBJDIR)/translate $(SRCDIR)/fileedit.c >$@
1204
+
1205
+$(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h
1206
+ $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c
1207
+
1208
+$(OBJDIR)/fileedit.h: $(OBJDIR)/headers
11971209
11981210
$(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(OBJDIR)/translate
11991211
$(OBJDIR)/translate $(SRCDIR)/finfo.c >$@
12001212
12011213
$(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h
12021214
--- src/main.mk
+++ src/main.mk
@@ -54,10 +54,11 @@
54 $(SRCDIR)/etag.c \
55 $(SRCDIR)/event.c \
56 $(SRCDIR)/export.c \
57 $(SRCDIR)/extcgi.c \
58 $(SRCDIR)/file.c \
 
59 $(SRCDIR)/finfo.c \
60 $(SRCDIR)/foci.c \
61 $(SRCDIR)/forum.c \
62 $(SRCDIR)/fshell.c \
63 $(SRCDIR)/fusefs.c \
@@ -287,10 +288,11 @@
287 $(OBJDIR)/etag_.c \
288 $(OBJDIR)/event_.c \
289 $(OBJDIR)/export_.c \
290 $(OBJDIR)/extcgi_.c \
291 $(OBJDIR)/file_.c \
 
292 $(OBJDIR)/finfo_.c \
293 $(OBJDIR)/foci_.c \
294 $(OBJDIR)/forum_.c \
295 $(OBJDIR)/fshell_.c \
296 $(OBJDIR)/fusefs_.c \
@@ -429,10 +431,11 @@
429 $(OBJDIR)/etag.o \
430 $(OBJDIR)/event.o \
431 $(OBJDIR)/export.o \
432 $(OBJDIR)/extcgi.o \
433 $(OBJDIR)/file.o \
 
434 $(OBJDIR)/finfo.o \
435 $(OBJDIR)/foci.o \
436 $(OBJDIR)/forum.o \
437 $(OBJDIR)/fshell.o \
438 $(OBJDIR)/fusefs.o \
@@ -766,10 +769,11 @@
766 $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
767 $(OBJDIR)/event_.c:$(OBJDIR)/event.h \
768 $(OBJDIR)/export_.c:$(OBJDIR)/export.h \
769 $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \
770 $(OBJDIR)/file_.c:$(OBJDIR)/file.h \
 
771 $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
772 $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
773 $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \
774 $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
775 $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \
@@ -1192,10 +1196,18 @@
1192
1193 $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h
1194 $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
1195
1196 $(OBJDIR)/file.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1197
1198 $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(OBJDIR)/translate
1199 $(OBJDIR)/translate $(SRCDIR)/finfo.c >$@
1200
1201 $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h
1202
--- src/main.mk
+++ src/main.mk
@@ -54,10 +54,11 @@
54 $(SRCDIR)/etag.c \
55 $(SRCDIR)/event.c \
56 $(SRCDIR)/export.c \
57 $(SRCDIR)/extcgi.c \
58 $(SRCDIR)/file.c \
59 $(SRCDIR)/fileedit.c \
60 $(SRCDIR)/finfo.c \
61 $(SRCDIR)/foci.c \
62 $(SRCDIR)/forum.c \
63 $(SRCDIR)/fshell.c \
64 $(SRCDIR)/fusefs.c \
@@ -287,10 +288,11 @@
288 $(OBJDIR)/etag_.c \
289 $(OBJDIR)/event_.c \
290 $(OBJDIR)/export_.c \
291 $(OBJDIR)/extcgi_.c \
292 $(OBJDIR)/file_.c \
293 $(OBJDIR)/fileedit_.c \
294 $(OBJDIR)/finfo_.c \
295 $(OBJDIR)/foci_.c \
296 $(OBJDIR)/forum_.c \
297 $(OBJDIR)/fshell_.c \
298 $(OBJDIR)/fusefs_.c \
@@ -429,10 +431,11 @@
431 $(OBJDIR)/etag.o \
432 $(OBJDIR)/event.o \
433 $(OBJDIR)/export.o \
434 $(OBJDIR)/extcgi.o \
435 $(OBJDIR)/file.o \
436 $(OBJDIR)/fileedit.o \
437 $(OBJDIR)/finfo.o \
438 $(OBJDIR)/foci.o \
439 $(OBJDIR)/forum.o \
440 $(OBJDIR)/fshell.o \
441 $(OBJDIR)/fusefs.o \
@@ -766,10 +769,11 @@
769 $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
770 $(OBJDIR)/event_.c:$(OBJDIR)/event.h \
771 $(OBJDIR)/export_.c:$(OBJDIR)/export.h \
772 $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \
773 $(OBJDIR)/file_.c:$(OBJDIR)/file.h \
774 $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \
775 $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
776 $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
777 $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \
778 $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
779 $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \
@@ -1192,10 +1196,18 @@
1196
1197 $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h
1198 $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
1199
1200 $(OBJDIR)/file.h: $(OBJDIR)/headers
1201
1202 $(OBJDIR)/fileedit_.c: $(SRCDIR)/fileedit.c $(OBJDIR)/translate
1203 $(OBJDIR)/translate $(SRCDIR)/fileedit.c >$@
1204
1205 $(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h
1206 $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c
1207
1208 $(OBJDIR)/fileedit.h: $(OBJDIR)/headers
1209
1210 $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(OBJDIR)/translate
1211 $(OBJDIR)/translate $(SRCDIR)/finfo.c >$@
1212
1213 $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h
1214
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -65,10 +65,11 @@
6565
etag
6666
event
6767
extcgi
6868
export
6969
file
70
+ fileedit
7071
finfo
7172
foci
7273
forum
7374
fshell
7475
fusefs
7576
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -65,10 +65,11 @@
65 etag
66 event
67 extcgi
68 export
69 file
 
70 finfo
71 foci
72 forum
73 fshell
74 fusefs
75
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -65,10 +65,11 @@
65 etag
66 event
67 extcgi
68 export
69 file
70 fileedit
71 finfo
72 foci
73 forum
74 fshell
75 fusefs
76
+10 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
2828
2929
SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
3030
3131
SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3232
33
-SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
33
+SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
3434
35
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
35
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3636
3737
3838
RC=$(DMDIR)\bin\rcc
3939
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4040
@@ -49,11 +49,11 @@
4949
5050
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5151
$(RC) $(RCFLAGS) -o$@ $**
5252
5353
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54
- +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
54
+ +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
5555
+echo fossil >> $@
5656
+echo fossil >> $@
5757
+echo $(LIBS) >> $@
5858
+echo. >> $@
5959
+echo fossil >> $@
@@ -368,10 +368,16 @@
368368
$(OBJDIR)\file$O : file_.c file.h
369369
$(TCC) -o$@ -c file_.c
370370
371371
file_.c : $(SRCDIR)\file.c
372372
+translate$E $** > $@
373
+
374
+$(OBJDIR)\fileedit$O : fileedit_.c fileedit.h
375
+ $(TCC) -o$@ -c fileedit_.c
376
+
377
+fileedit_.c : $(SRCDIR)\fileedit.c
378
+ +translate$E $** > $@
373379
374380
$(OBJDIR)\finfo$O : finfo_.c finfo.h
375381
$(TCC) -o$@ -c finfo_.c
376382
377383
finfo_.c : $(SRCDIR)\finfo.c
@@ -970,7 +976,7 @@
970976
971977
zip_.c : $(SRCDIR)\zip.c
972978
+translate$E $** > $@
973979
974980
headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
975
- +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
981
+ +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
976982
@copy /Y nul: headers
977983
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
30
31 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -368,10 +368,16 @@
368 $(OBJDIR)\file$O : file_.c file.h
369 $(TCC) -o$@ -c file_.c
370
371 file_.c : $(SRCDIR)\file.c
372 +translate$E $** > $@
 
 
 
 
 
 
373
374 $(OBJDIR)\finfo$O : finfo_.c finfo.h
375 $(TCC) -o$@ -c finfo_.c
376
377 finfo_.c : $(SRCDIR)\finfo.c
@@ -970,7 +976,7 @@
970
971 zip_.c : $(SRCDIR)\zip.c
972 +translate$E $** > $@
973
974 headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
975 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
976 @copy /Y nul: headers
977
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
30
31 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -368,10 +368,16 @@
368 $(OBJDIR)\file$O : file_.c file.h
369 $(TCC) -o$@ -c file_.c
370
371 file_.c : $(SRCDIR)\file.c
372 +translate$E $** > $@
373
374 $(OBJDIR)\fileedit$O : fileedit_.c fileedit.h
375 $(TCC) -o$@ -c fileedit_.c
376
377 fileedit_.c : $(SRCDIR)\fileedit.c
378 +translate$E $** > $@
379
380 $(OBJDIR)\finfo$O : finfo_.c finfo.h
381 $(TCC) -o$@ -c finfo_.c
382
383 finfo_.c : $(SRCDIR)\finfo.c
@@ -970,7 +976,7 @@
976
977 zip_.c : $(SRCDIR)\zip.c
978 +translate$E $** > $@
979
980 headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
981 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
982 @copy /Y nul: headers
983
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -476,10 +476,11 @@
476476
$(SRCDIR)/etag.c \
477477
$(SRCDIR)/event.c \
478478
$(SRCDIR)/export.c \
479479
$(SRCDIR)/extcgi.c \
480480
$(SRCDIR)/file.c \
481
+ $(SRCDIR)/fileedit.c \
481482
$(SRCDIR)/finfo.c \
482483
$(SRCDIR)/foci.c \
483484
$(SRCDIR)/forum.c \
484485
$(SRCDIR)/fshell.c \
485486
$(SRCDIR)/fusefs.c \
@@ -709,10 +710,11 @@
709710
$(OBJDIR)/etag_.c \
710711
$(OBJDIR)/event_.c \
711712
$(OBJDIR)/export_.c \
712713
$(OBJDIR)/extcgi_.c \
713714
$(OBJDIR)/file_.c \
715
+ $(OBJDIR)/fileedit_.c \
714716
$(OBJDIR)/finfo_.c \
715717
$(OBJDIR)/foci_.c \
716718
$(OBJDIR)/forum_.c \
717719
$(OBJDIR)/fshell_.c \
718720
$(OBJDIR)/fusefs_.c \
@@ -851,10 +853,11 @@
851853
$(OBJDIR)/etag.o \
852854
$(OBJDIR)/event.o \
853855
$(OBJDIR)/export.o \
854856
$(OBJDIR)/extcgi.o \
855857
$(OBJDIR)/file.o \
858
+ $(OBJDIR)/fileedit.o \
856859
$(OBJDIR)/finfo.o \
857860
$(OBJDIR)/foci.o \
858861
$(OBJDIR)/forum.o \
859862
$(OBJDIR)/fshell.o \
860863
$(OBJDIR)/fusefs.o \
@@ -1213,10 +1216,11 @@
12131216
$(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
12141217
$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
12151218
$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
12161219
$(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \
12171220
$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
1221
+ $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \
12181222
$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
12191223
$(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
12201224
$(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \
12211225
$(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
12221226
$(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \
@@ -1641,10 +1645,18 @@
16411645
16421646
$(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h
16431647
$(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
16441648
16451649
$(OBJDIR)/file.h: $(OBJDIR)/headers
1650
+
1651
+$(OBJDIR)/fileedit_.c: $(SRCDIR)/fileedit.c $(TRANSLATE)
1652
+ $(TRANSLATE) $(SRCDIR)/fileedit.c >$@
1653
+
1654
+$(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h
1655
+ $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c
1656
+
1657
+$(OBJDIR)/fileedit.h: $(OBJDIR)/headers
16461658
16471659
$(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(TRANSLATE)
16481660
$(TRANSLATE) $(SRCDIR)/finfo.c >$@
16491661
16501662
$(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h
16511663
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -476,10 +476,11 @@
476 $(SRCDIR)/etag.c \
477 $(SRCDIR)/event.c \
478 $(SRCDIR)/export.c \
479 $(SRCDIR)/extcgi.c \
480 $(SRCDIR)/file.c \
 
481 $(SRCDIR)/finfo.c \
482 $(SRCDIR)/foci.c \
483 $(SRCDIR)/forum.c \
484 $(SRCDIR)/fshell.c \
485 $(SRCDIR)/fusefs.c \
@@ -709,10 +710,11 @@
709 $(OBJDIR)/etag_.c \
710 $(OBJDIR)/event_.c \
711 $(OBJDIR)/export_.c \
712 $(OBJDIR)/extcgi_.c \
713 $(OBJDIR)/file_.c \
 
714 $(OBJDIR)/finfo_.c \
715 $(OBJDIR)/foci_.c \
716 $(OBJDIR)/forum_.c \
717 $(OBJDIR)/fshell_.c \
718 $(OBJDIR)/fusefs_.c \
@@ -851,10 +853,11 @@
851 $(OBJDIR)/etag.o \
852 $(OBJDIR)/event.o \
853 $(OBJDIR)/export.o \
854 $(OBJDIR)/extcgi.o \
855 $(OBJDIR)/file.o \
 
856 $(OBJDIR)/finfo.o \
857 $(OBJDIR)/foci.o \
858 $(OBJDIR)/forum.o \
859 $(OBJDIR)/fshell.o \
860 $(OBJDIR)/fusefs.o \
@@ -1213,10 +1216,11 @@
1213 $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
1214 $(OBJDIR)/event_.c:$(OBJDIR)/event.h \
1215 $(OBJDIR)/export_.c:$(OBJDIR)/export.h \
1216 $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \
1217 $(OBJDIR)/file_.c:$(OBJDIR)/file.h \
 
1218 $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
1219 $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
1220 $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \
1221 $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
1222 $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \
@@ -1641,10 +1645,18 @@
1641
1642 $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h
1643 $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
1644
1645 $(OBJDIR)/file.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1646
1647 $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(TRANSLATE)
1648 $(TRANSLATE) $(SRCDIR)/finfo.c >$@
1649
1650 $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h
1651
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -476,10 +476,11 @@
476 $(SRCDIR)/etag.c \
477 $(SRCDIR)/event.c \
478 $(SRCDIR)/export.c \
479 $(SRCDIR)/extcgi.c \
480 $(SRCDIR)/file.c \
481 $(SRCDIR)/fileedit.c \
482 $(SRCDIR)/finfo.c \
483 $(SRCDIR)/foci.c \
484 $(SRCDIR)/forum.c \
485 $(SRCDIR)/fshell.c \
486 $(SRCDIR)/fusefs.c \
@@ -709,10 +710,11 @@
710 $(OBJDIR)/etag_.c \
711 $(OBJDIR)/event_.c \
712 $(OBJDIR)/export_.c \
713 $(OBJDIR)/extcgi_.c \
714 $(OBJDIR)/file_.c \
715 $(OBJDIR)/fileedit_.c \
716 $(OBJDIR)/finfo_.c \
717 $(OBJDIR)/foci_.c \
718 $(OBJDIR)/forum_.c \
719 $(OBJDIR)/fshell_.c \
720 $(OBJDIR)/fusefs_.c \
@@ -851,10 +853,11 @@
853 $(OBJDIR)/etag.o \
854 $(OBJDIR)/event.o \
855 $(OBJDIR)/export.o \
856 $(OBJDIR)/extcgi.o \
857 $(OBJDIR)/file.o \
858 $(OBJDIR)/fileedit.o \
859 $(OBJDIR)/finfo.o \
860 $(OBJDIR)/foci.o \
861 $(OBJDIR)/forum.o \
862 $(OBJDIR)/fshell.o \
863 $(OBJDIR)/fusefs.o \
@@ -1213,10 +1216,11 @@
1216 $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
1217 $(OBJDIR)/event_.c:$(OBJDIR)/event.h \
1218 $(OBJDIR)/export_.c:$(OBJDIR)/export.h \
1219 $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \
1220 $(OBJDIR)/file_.c:$(OBJDIR)/file.h \
1221 $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \
1222 $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
1223 $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
1224 $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \
1225 $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
1226 $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \
@@ -1641,10 +1645,18 @@
1645
1646 $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h
1647 $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c
1648
1649 $(OBJDIR)/file.h: $(OBJDIR)/headers
1650
1651 $(OBJDIR)/fileedit_.c: $(SRCDIR)/fileedit.c $(TRANSLATE)
1652 $(TRANSLATE) $(SRCDIR)/fileedit.c >$@
1653
1654 $(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h
1655 $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c
1656
1657 $(OBJDIR)/fileedit.h: $(OBJDIR)/headers
1658
1659 $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(TRANSLATE)
1660 $(TRANSLATE) $(SRCDIR)/finfo.c >$@
1661
1662 $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h
1663
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -384,10 +384,11 @@
384384
etag_.c \
385385
event_.c \
386386
export_.c \
387387
extcgi_.c \
388388
file_.c \
389
+ fileedit_.c \
389390
finfo_.c \
390391
foci_.c \
391392
forum_.c \
392393
fshell_.c \
393394
fusefs_.c \
@@ -616,10 +617,11 @@
616617
$(OX)\etag$O \
617618
$(OX)\event$O \
618619
$(OX)\export$O \
619620
$(OX)\extcgi$O \
620621
$(OX)\file$O \
622
+ $(OX)\fileedit$O \
621623
$(OX)\finfo$O \
622624
$(OX)\foci$O \
623625
$(OX)\forum$O \
624626
$(OX)\fshell$O \
625627
$(OX)\fusefs$O \
@@ -820,10 +822,11 @@
820822
echo $(OX)\etag.obj >> $@
821823
echo $(OX)\event.obj >> $@
822824
echo $(OX)\export.obj >> $@
823825
echo $(OX)\extcgi.obj >> $@
824826
echo $(OX)\file.obj >> $@
827
+ echo $(OX)\fileedit.obj >> $@
825828
echo $(OX)\finfo.obj >> $@
826829
echo $(OX)\foci.obj >> $@
827830
echo $(OX)\forum.obj >> $@
828831
echo $(OX)\fshell.obj >> $@
829832
echo $(OX)\fusefs.obj >> $@
@@ -1282,10 +1285,16 @@
12821285
$(OX)\file$O : file_.c file.h
12831286
$(TCC) /Fo$@ -c file_.c
12841287
12851288
file_.c : $(SRCDIR)\file.c
12861289
translate$E $** > $@
1290
+
1291
+$(OX)\fileedit$O : fileedit_.c fileedit.h
1292
+ $(TCC) /Fo$@ -c fileedit_.c
1293
+
1294
+fileedit_.c : $(SRCDIR)\fileedit.c
1295
+ translate$E $** > $@
12871296
12881297
$(OX)\finfo$O : finfo_.c finfo.h
12891298
$(TCC) /Fo$@ -c finfo_.c
12901299
12911300
finfo_.c : $(SRCDIR)\finfo.c
@@ -1927,10 +1936,11 @@
19271936
etag_.c:etag.h \
19281937
event_.c:event.h \
19291938
export_.c:export.h \
19301939
extcgi_.c:extcgi.h \
19311940
file_.c:file.h \
1941
+ fileedit_.c:fileedit.h \
19321942
finfo_.c:finfo.h \
19331943
foci_.c:foci.h \
19341944
forum_.c:forum.h \
19351945
fshell_.c:fshell.h \
19361946
fusefs_.c:fusefs.h \
19371947
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -384,10 +384,11 @@
384 etag_.c \
385 event_.c \
386 export_.c \
387 extcgi_.c \
388 file_.c \
 
389 finfo_.c \
390 foci_.c \
391 forum_.c \
392 fshell_.c \
393 fusefs_.c \
@@ -616,10 +617,11 @@
616 $(OX)\etag$O \
617 $(OX)\event$O \
618 $(OX)\export$O \
619 $(OX)\extcgi$O \
620 $(OX)\file$O \
 
621 $(OX)\finfo$O \
622 $(OX)\foci$O \
623 $(OX)\forum$O \
624 $(OX)\fshell$O \
625 $(OX)\fusefs$O \
@@ -820,10 +822,11 @@
820 echo $(OX)\etag.obj >> $@
821 echo $(OX)\event.obj >> $@
822 echo $(OX)\export.obj >> $@
823 echo $(OX)\extcgi.obj >> $@
824 echo $(OX)\file.obj >> $@
 
825 echo $(OX)\finfo.obj >> $@
826 echo $(OX)\foci.obj >> $@
827 echo $(OX)\forum.obj >> $@
828 echo $(OX)\fshell.obj >> $@
829 echo $(OX)\fusefs.obj >> $@
@@ -1282,10 +1285,16 @@
1282 $(OX)\file$O : file_.c file.h
1283 $(TCC) /Fo$@ -c file_.c
1284
1285 file_.c : $(SRCDIR)\file.c
1286 translate$E $** > $@
 
 
 
 
 
 
1287
1288 $(OX)\finfo$O : finfo_.c finfo.h
1289 $(TCC) /Fo$@ -c finfo_.c
1290
1291 finfo_.c : $(SRCDIR)\finfo.c
@@ -1927,10 +1936,11 @@
1927 etag_.c:etag.h \
1928 event_.c:event.h \
1929 export_.c:export.h \
1930 extcgi_.c:extcgi.h \
1931 file_.c:file.h \
 
1932 finfo_.c:finfo.h \
1933 foci_.c:foci.h \
1934 forum_.c:forum.h \
1935 fshell_.c:fshell.h \
1936 fusefs_.c:fusefs.h \
1937
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -384,10 +384,11 @@
384 etag_.c \
385 event_.c \
386 export_.c \
387 extcgi_.c \
388 file_.c \
389 fileedit_.c \
390 finfo_.c \
391 foci_.c \
392 forum_.c \
393 fshell_.c \
394 fusefs_.c \
@@ -616,10 +617,11 @@
617 $(OX)\etag$O \
618 $(OX)\event$O \
619 $(OX)\export$O \
620 $(OX)\extcgi$O \
621 $(OX)\file$O \
622 $(OX)\fileedit$O \
623 $(OX)\finfo$O \
624 $(OX)\foci$O \
625 $(OX)\forum$O \
626 $(OX)\fshell$O \
627 $(OX)\fusefs$O \
@@ -820,10 +822,11 @@
822 echo $(OX)\etag.obj >> $@
823 echo $(OX)\event.obj >> $@
824 echo $(OX)\export.obj >> $@
825 echo $(OX)\extcgi.obj >> $@
826 echo $(OX)\file.obj >> $@
827 echo $(OX)\fileedit.obj >> $@
828 echo $(OX)\finfo.obj >> $@
829 echo $(OX)\foci.obj >> $@
830 echo $(OX)\forum.obj >> $@
831 echo $(OX)\fshell.obj >> $@
832 echo $(OX)\fusefs.obj >> $@
@@ -1282,10 +1285,16 @@
1285 $(OX)\file$O : file_.c file.h
1286 $(TCC) /Fo$@ -c file_.c
1287
1288 file_.c : $(SRCDIR)\file.c
1289 translate$E $** > $@
1290
1291 $(OX)\fileedit$O : fileedit_.c fileedit.h
1292 $(TCC) /Fo$@ -c fileedit_.c
1293
1294 fileedit_.c : $(SRCDIR)\fileedit.c
1295 translate$E $** > $@
1296
1297 $(OX)\finfo$O : finfo_.c finfo.h
1298 $(TCC) /Fo$@ -c finfo_.c
1299
1300 finfo_.c : $(SRCDIR)\finfo.c
@@ -1927,10 +1936,11 @@
1936 etag_.c:etag.h \
1937 event_.c:event.h \
1938 export_.c:export.h \
1939 extcgi_.c:extcgi.h \
1940 file_.c:file.h \
1941 fileedit_.c:fileedit.h \
1942 finfo_.c:finfo.h \
1943 foci_.c:foci.h \
1944 forum_.c:forum.h \
1945 fshell_.c:fshell.h \
1946 fusefs_.c:fusefs.h \
1947

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button