Fossil SCM

Revise the COMMAND: and WEBSITE: parsing to add the ability to specify flag options after the command or website name.

drh 2016-09-13 19:59 trunk
Commit 555ddfecfa9aae065c2b87eee573049ac972ffe8
+1 -1
--- src/content.c
+++ src/content.c
@@ -854,11 +854,11 @@
854854
if( z[n-1]!='\n' ) return 0;
855855
return 1;
856856
}
857857
858858
/*
859
-** COMMAND: test-integrity ?OPTIONS?
859
+** COMMAND: test-integrity
860860
**
861861
** Verify that all content can be extracted from the BLOB table correctly.
862862
** If the BLOB table is correct, then the repository can always be
863863
** successfully reconstructed using "fossil rebuild".
864864
**
865865
--- src/content.c
+++ src/content.c
@@ -854,11 +854,11 @@
854 if( z[n-1]!='\n' ) return 0;
855 return 1;
856 }
857
858 /*
859 ** COMMAND: test-integrity ?OPTIONS?
860 **
861 ** Verify that all content can be extracted from the BLOB table correctly.
862 ** If the BLOB table is correct, then the repository can always be
863 ** successfully reconstructed using "fossil rebuild".
864 **
865
--- src/content.c
+++ src/content.c
@@ -854,11 +854,11 @@
854 if( z[n-1]!='\n' ) return 0;
855 return 1;
856 }
857
858 /*
859 ** COMMAND: test-integrity
860 **
861 ** Verify that all content can be extracted from the BLOB table correctly.
862 ** If the BLOB table is correct, then the repository can always be
863 ** successfully reconstructed using "fossil rebuild".
864 **
865
+18 -8
--- src/dispatch.c
+++ src/dispatch.c
@@ -35,18 +35,28 @@
3535
void (*xFunc)(void); /* Function that implements the command or webpage */
3636
const char *zHelp; /* Raw help text */
3737
unsigned eCmdFlags; /* Flags */
3838
};
3939
40
-/* Allowed values for CmdOrPage.eCmdFlags. */
41
-#define CMDFLAG_1ST_TIER 0x01 /* Most important commands */
42
-#define CMDFLAG_2ND_TIER 0x02 /* Obscure and seldom used commands */
43
-#define CMDFLAG_TEST 0x04 /* Commands for testing only */
44
-#define CMDFLAG_WEBPAGE 0x08 /* Web pages */
45
-#define CMDFLAG_COMMAND 0x10 /* A command */
46
-#define CMDFLAG_ANY 0x18 /* Match anything */
47
-#define CMDFLAG_PREFIX 0x20 /* Prefix match is ok */
40
+/***************************************************************************
41
+** These macros must match similar macros in mkindex.c
42
+** Allowed values for CmdOrPage.eCmdFlags.
43
+*/
44
+#define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */
45
+#define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */
46
+#define CMDFLAG_TEST 0x0004 /* Commands for testing only */
47
+#define CMDFLAG_WEBPAGE 0x0008 /* Web pages */
48
+#define CMDFLAG_COMMAND 0x0010 /* A command */
49
+/**************************************************************************/
50
+
51
+/* Only the bits above that are part of CMDFLAG_TH_MASK are passed into
52
+** the TH1 hook procedures. */
53
+#define CMDFLAG_TH_MASK 0x000f /* Legacy flags only */
54
+
55
+/* Values for the 2nd parameter to dispatch_name_search() */
56
+#define CMDFLAG_ANY 0x0018 /* Match anything */
57
+#define CMDFLAG_PREFIX 0x0020 /* Prefix match is ok */
4858
4959
#endif /* INTERFACE */
5060
5161
/*
5262
** The page_index.h file contains the definition for aCommand[] - an array
5363
--- src/dispatch.c
+++ src/dispatch.c
@@ -35,18 +35,28 @@
35 void (*xFunc)(void); /* Function that implements the command or webpage */
36 const char *zHelp; /* Raw help text */
37 unsigned eCmdFlags; /* Flags */
38 };
39
40 /* Allowed values for CmdOrPage.eCmdFlags. */
41 #define CMDFLAG_1ST_TIER 0x01 /* Most important commands */
42 #define CMDFLAG_2ND_TIER 0x02 /* Obscure and seldom used commands */
43 #define CMDFLAG_TEST 0x04 /* Commands for testing only */
44 #define CMDFLAG_WEBPAGE 0x08 /* Web pages */
45 #define CMDFLAG_COMMAND 0x10 /* A command */
46 #define CMDFLAG_ANY 0x18 /* Match anything */
47 #define CMDFLAG_PREFIX 0x20 /* Prefix match is ok */
 
 
 
 
 
 
 
 
 
 
48
49 #endif /* INTERFACE */
50
51 /*
52 ** The page_index.h file contains the definition for aCommand[] - an array
53
--- src/dispatch.c
+++ src/dispatch.c
@@ -35,18 +35,28 @@
35 void (*xFunc)(void); /* Function that implements the command or webpage */
36 const char *zHelp; /* Raw help text */
37 unsigned eCmdFlags; /* Flags */
38 };
39
40 /***************************************************************************
41 ** These macros must match similar macros in mkindex.c
42 ** Allowed values for CmdOrPage.eCmdFlags.
43 */
44 #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */
45 #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */
46 #define CMDFLAG_TEST 0x0004 /* Commands for testing only */
47 #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */
48 #define CMDFLAG_COMMAND 0x0010 /* A command */
49 /**************************************************************************/
50
51 /* Only the bits above that are part of CMDFLAG_TH_MASK are passed into
52 ** the TH1 hook procedures. */
53 #define CMDFLAG_TH_MASK 0x000f /* Legacy flags only */
54
55 /* Values for the 2nd parameter to dispatch_name_search() */
56 #define CMDFLAG_ANY 0x0018 /* Match anything */
57 #define CMDFLAG_PREFIX 0x0020 /* Prefix match is ok */
58
59 #endif /* INTERFACE */
60
61 /*
62 ** The page_index.h file contains the definition for aCommand[] - an array
63
+4 -4
--- src/main.c
+++ src/main.c
@@ -731,11 +731,11 @@
731731
**
732732
** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
733733
** executed.
734734
*/
735735
if( !g.isHTTP && !g.fNoThHook ){
736
- rc = Th_CommandHook(pCmd->zName, pCmd->eCmdFlags);
736
+ rc = Th_CommandHook(pCmd->zName, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
737737
}else{
738738
rc = TH_OK;
739739
}
740740
if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
741741
if( rc==TH_OK || rc==TH_RETURN ){
@@ -742,11 +742,11 @@
742742
#endif
743743
pCmd->xFunc();
744744
#ifdef FOSSIL_ENABLE_TH1_HOOKS
745745
}
746746
if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
747
- Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags);
747
+ Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
748748
}
749749
}
750750
#endif
751751
fossil_exit(0);
752752
/*NOT_REACHED*/
@@ -1546,11 +1546,11 @@
15461546
** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
15471547
** executed.
15481548
*/
15491549
int rc;
15501550
if( !g.fNoThHook ){
1551
- rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags);
1551
+ rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
15521552
}else{
15531553
rc = TH_OK;
15541554
}
15551555
if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
15561556
if( rc==TH_OK || rc==TH_RETURN ){
@@ -1557,11 +1557,11 @@
15571557
#endif
15581558
pCmd->xFunc();
15591559
#ifdef FOSSIL_ENABLE_TH1_HOOKS
15601560
}
15611561
if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
1562
- Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags);
1562
+ Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
15631563
}
15641564
}
15651565
#endif
15661566
}
15671567
15681568
--- src/main.c
+++ src/main.c
@@ -731,11 +731,11 @@
731 **
732 ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
733 ** executed.
734 */
735 if( !g.isHTTP && !g.fNoThHook ){
736 rc = Th_CommandHook(pCmd->zName, pCmd->eCmdFlags);
737 }else{
738 rc = TH_OK;
739 }
740 if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
741 if( rc==TH_OK || rc==TH_RETURN ){
@@ -742,11 +742,11 @@
742 #endif
743 pCmd->xFunc();
744 #ifdef FOSSIL_ENABLE_TH1_HOOKS
745 }
746 if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
747 Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags);
748 }
749 }
750 #endif
751 fossil_exit(0);
752 /*NOT_REACHED*/
@@ -1546,11 +1546,11 @@
1546 ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
1547 ** executed.
1548 */
1549 int rc;
1550 if( !g.fNoThHook ){
1551 rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags);
1552 }else{
1553 rc = TH_OK;
1554 }
1555 if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
1556 if( rc==TH_OK || rc==TH_RETURN ){
@@ -1557,11 +1557,11 @@
1557 #endif
1558 pCmd->xFunc();
1559 #ifdef FOSSIL_ENABLE_TH1_HOOKS
1560 }
1561 if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
1562 Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags);
1563 }
1564 }
1565 #endif
1566 }
1567
1568
--- src/main.c
+++ src/main.c
@@ -731,11 +731,11 @@
731 **
732 ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
733 ** executed.
734 */
735 if( !g.isHTTP && !g.fNoThHook ){
736 rc = Th_CommandHook(pCmd->zName, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
737 }else{
738 rc = TH_OK;
739 }
740 if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
741 if( rc==TH_OK || rc==TH_RETURN ){
@@ -742,11 +742,11 @@
742 #endif
743 pCmd->xFunc();
744 #ifdef FOSSIL_ENABLE_TH1_HOOKS
745 }
746 if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
747 Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
748 }
749 }
750 #endif
751 fossil_exit(0);
752 /*NOT_REACHED*/
@@ -1546,11 +1546,11 @@
1546 ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
1547 ** executed.
1548 */
1549 int rc;
1550 if( !g.fNoThHook ){
1551 rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
1552 }else{
1553 rc = TH_OK;
1554 }
1555 if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
1556 if( rc==TH_OK || rc==TH_RETURN ){
@@ -1557,11 +1557,11 @@
1557 #endif
1558 pCmd->xFunc();
1559 #ifdef FOSSIL_ENABLE_TH1_HOOKS
1560 }
1561 if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
1562 Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags & CMDFLAG_TH_MASK);
1563 }
1564 }
1565 #endif
1566 }
1567
1568
+129 -58
--- src/mkindex.c
+++ src/mkindex.c
@@ -22,49 +22,68 @@
2222
** to those entry points.
2323
**
2424
** The source code is scanned for comment lines of the form:
2525
**
2626
** WEBPAGE: /abc/xyz
27
+** COMMAND: cmdname
2728
**
28
-** This comment should be followed by a function definition of the
29
+** These comment should be followed by a function definition of the
2930
** form:
3031
**
3132
** void function_name(void){
3233
**
3334
** This routine creates C source code for a constant table that maps
34
-** webpage name into pointers to the function.
35
-**
36
-** We also scan for comments lines of this form:
37
-**
38
-** COMMAND: cmdname
39
-**
40
-** These entries build a constant table used to map command names into
41
-** functions. If cmdname ends with "*" then the command is a second-tier
42
-** command that is not displayed by the "fossil help" command. The
43
-** final "*" is not considered to be part of the command name.
44
-**
45
-** Comment text following COMMAND: through the end of the comment is
46
-** understood to be help text for the command specified. This help
47
-** text is accumulated and a table containing the text for each command
48
-** is generated. That table is used implement the "fossil help" command
49
-** and the "/help" HTTP method.
50
-**
51
-** Multiple occurrences of WEBPAGE: or COMMAND: (but not both) can appear
52
-** before each function name. In this way, webpages and commands can
53
-** have aliases.
35
+** command and webpage name into pointers to the function.
36
+**
37
+** Command names can divided into three classes: 1st-tier, 2nd-tier,
38
+** and test. 1st-tier commands are the most frequently used and the
39
+** ones that show up with "fossil help". 2nd-tier are seldom-used and/or
40
+** legacy command. Test commands are unsupported commands used for testing
41
+** and analysis only.
42
+**
43
+** Commands are 1st-tier by default. If the command name begins with
44
+** "test-" or if the command name as a "test" argument, then it becomes
45
+** a test command. If the command name has a "2nd-tier" argument or ends
46
+** with a "*" character, it is second tier. Examples:
47
+**
48
+** COMMAND: abcde*
49
+** COMMAND: fghij 2nd-tier
50
+** COMMAND: test-xyzzy
51
+** COMMAND: xyzzy test
52
+**
53
+** New arguments may be added in future releases that set additional
54
+** bits in the eCmdFlags field.
55
+**
56
+** Additional lines of comment after the COMMAND: or WEBPAGE: become
57
+** the built-in help text for that command or webpage.
58
+**
59
+** Multiple COMMAND: entries can be attached to the same command, thus
60
+** creating multiple aliases for that command. Similarly, multiple
61
+** WEBPAGE: entries can be attached to the same webpage function, to give
62
+** that page aliases.
5463
*/
5564
#include <stdio.h>
5665
#include <stdlib.h>
57
-#include <ctype.h>
5866
#include <assert.h>
5967
#include <string.h>
68
+
69
+/***************************************************************************
70
+** These macros must match similar macros in dispatch.c.
71
+**
72
+** Allowed values for CmdOrPage.eCmdFlags. */
73
+#define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */
74
+#define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */
75
+#define CMDFLAG_TEST 0x0004 /* Commands for testing only */
76
+#define CMDFLAG_WEBPAGE 0x0008 /* Web pages */
77
+#define CMDFLAG_COMMAND 0x0010 /* A command */
78
+/**************************************************************************/
6079
6180
/*
6281
** Each entry looks like this:
6382
*/
6483
typedef struct Entry {
65
- int eType; /* 0: webpage, 1: command */
84
+ int eType; /* CMDFLAG_* values */
6685
char *zIf; /* Enclose in #if */
6786
char *zFunc; /* Name of implementation */
6887
char *zPath; /* Webpage or command name */
6988
char *zHelp; /* Help text */
7089
int iHelp; /* Index of Help text */
@@ -106,10 +125,15 @@
106125
** Current filename and line number
107126
*/
108127
char *zFile;
109128
int nLine;
110129
130
+/*
131
+** Number of errors
132
+*/
133
+int nErr = 0;
134
+
111135
/*
112136
** Duplicate N characters of a string.
113137
*/
114138
char *string_dup(const char *zSrc, int n){
115139
char *z;
@@ -118,32 +142,92 @@
118142
if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); }
119143
strncpy(z, zSrc, n);
120144
z[n] = 0;
121145
return z;
122146
}
147
+
148
+/*
149
+** Safe isspace macro. Works with signed characters.
150
+*/
151
+int fossil_isspace(char c){
152
+ return c==' ' || (c<='\r' && c>='\t');
153
+}
154
+
155
+/*
156
+** Safe isident macro. Works with signed characters.
157
+*/
158
+int fossil_isident(char c){
159
+ if( c>='a' && c<='z' ) return 1;
160
+ if( c>='A' && c<='Z' ) return 1;
161
+ if( c>='0' && c<='9' ) return 1;
162
+ if( c=='_' ) return 1;
163
+ return 0;
164
+}
123165
124166
/*
125167
** Scan a line looking for comments containing zLabel. Make
126168
** new entries if found.
127169
*/
128170
void scan_for_label(const char *zLabel, char *zLine, int eType){
129171
int i, j;
130172
int len = strlen(zLabel);
131173
if( nUsed>=N_ENTRY ) return;
132
- for(i=0; isspace(zLine[i]) || zLine[i]=='*'; i++){}
174
+ for(i=0; fossil_isspace(zLine[i]) || zLine[i]=='*'; i++){}
133175
if( zLine[i]!=zLabel[0] ) return;
134176
if( strncmp(&zLine[i],zLabel, len)==0 ){
135177
i += len;
136178
}else{
137179
return;
138180
}
139
- while( isspace(zLine[i]) ){ i++; }
181
+ while( fossil_isspace(zLine[i]) ){ i++; }
140182
if( zLine[i]=='/' ) i++;
141
- for(j=0; zLine[i+j] && !isspace(zLine[i+j]); j++){}
183
+ for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
142184
aEntry[nUsed].eType = eType;
143
- aEntry[nUsed].zPath = string_dup(&zLine[i], j);
185
+ if( eType & CMDFLAG_WEBPAGE ){
186
+ aEntry[nUsed].zPath = string_dup(&zLine[i-1], j+1);
187
+ aEntry[nUsed].zPath[0] = '/';
188
+ }else{
189
+ aEntry[nUsed].zPath = string_dup(&zLine[i], j);
190
+ }
144191
aEntry[nUsed].zFunc = 0;
192
+ if( (eType & CMDFLAG_COMMAND)!=0 ){
193
+ if( strncmp(&zLine[i], "test-", 5)==0 ){
194
+ /* Commands that start with "test-" are test-commands */
195
+ aEntry[nUsed].eType |= CMDFLAG_TEST;
196
+ }else if( zLine[i+j-1]=='*' ){
197
+ /* If the command name ends in '*', remove the '*' from the name
198
+ ** but move the command into the second tier */
199
+ aEntry[nUsed].zPath[j-1] = 0;
200
+ aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
201
+ }else{
202
+ /* Otherwise, this is a first-tier command */
203
+ aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
204
+ }
205
+ }
206
+
207
+ /* Processing additional flags that might following the command name */
208
+ while( zLine[i+j]!=0 ){
209
+ i += j;
210
+ while( fossil_isspace(zLine[i]) ){ i++; }
211
+ if( zLine[i]==0 ) break;
212
+ for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
213
+ if( j==8 && strncmp(&zLine[i], "1st-tier", j)==0 ){
214
+ aEntry[nUsed].eType &= ~(CMDFLAG_2ND_TIER|CMDFLAG_TEST);
215
+ aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
216
+ }else if( j==8 && strncmp(&zLine[i], "2nd-tier", j)==0 ){
217
+ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_TEST);
218
+ aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
219
+ }else if( j==4 && strncmp(&zLine[i], "test", j)==0 ){
220
+ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER);
221
+ aEntry[nUsed].eType |= CMDFLAG_TEST;
222
+ }else{
223
+ fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
224
+ zFile, nLine, j, &zLine[i]);
225
+ nErr++;
226
+ }
227
+ }
228
+
145229
nUsed++;
146230
}
147231
148232
/*
149233
** Check to see if the current line is an #if and if it is, add it to
@@ -152,11 +236,11 @@
152236
*/
153237
void scan_for_if(const char *zLine){
154238
int i;
155239
int len;
156240
if( zLine[0]!='#' ) return;
157
- for(i=1; isspace(zLine[i]); i++){}
241
+ for(i=1; fossil_isspace(zLine[i]); i++){}
158242
if( zLine[i]==0 ) return;
159243
len = strlen(&zLine[i]);
160244
if( strncmp(&zLine[i],"if",2)==0 ){
161245
zIf[0] = '#';
162246
memcpy(&zIf[1], &zLine[i], len+1);
@@ -171,11 +255,11 @@
171255
void scan_for_func(char *zLine){
172256
int i,j,k;
173257
char *z;
174258
if( nUsed<=nFixed ) return;
175259
if( strncmp(zLine, "**", 2)==0
176
- && isspace(zLine[2])
260
+ && fossil_isspace(zLine[2])
177261
&& strlen(zLine)<sizeof(zHelp)-nHelp-1
178262
&& nUsed>nFixed
179263
&& strncmp(zLine,"** COMMAND:",11)!=0
180264
&& strncmp(zLine,"** WEBPAGE:",11)!=0
181265
){
@@ -186,25 +270,25 @@
186270
strcpy(&zHelp[nHelp], &zLine[3]);
187271
nHelp += strlen(&zHelp[nHelp]);
188272
}
189273
return;
190274
}
191
- for(i=0; isspace(zLine[i]); i++){}
275
+ for(i=0; fossil_isspace(zLine[i]); i++){}
192276
if( zLine[i]==0 ) return;
193277
if( strncmp(&zLine[i],"void",4)!=0 ){
194278
if( zLine[i]!='*' ) goto page_skip;
195279
return;
196280
}
197281
i += 4;
198
- if( !isspace(zLine[i]) ) goto page_skip;
199
- while( isspace(zLine[i]) ){ i++; }
200
- for(j=0; isalnum(zLine[i+j]) || zLine[i+j]=='_'; j++){}
282
+ if( !fossil_isspace(zLine[i]) ) goto page_skip;
283
+ while( fossil_isspace(zLine[i]) ){ i++; }
284
+ for(j=0; fossil_isident(zLine[i+j]); j++){}
201285
if( j==0 ) goto page_skip;
202
- for(k=nHelp-1; k>=0 && isspace(zHelp[k]); k--){}
286
+ for(k=nHelp-1; k>=0 && fossil_isspace(zHelp[k]); k--){}
203287
nHelp = k+1;
204288
zHelp[nHelp] = 0;
205
- for(k=0; k<nHelp && isspace(zHelp[k]); k++){}
289
+ for(k=0; k<nHelp && fossil_isspace(zHelp[k]); k++){}
206290
if( k<nHelp ){
207291
z = string_dup(&zHelp[k], nHelp-k);
208292
}else{
209293
z = "";
210294
}
@@ -214,11 +298,11 @@
214298
aEntry[k].zHelp = z;
215299
z = 0;
216300
aEntry[k].iHelp = nFixed;
217301
}
218302
i+=j;
219
- while( isspace(zLine[i]) ){ i++; }
303
+ while( fossil_isspace(zLine[i]) ){ i++; }
220304
if( zLine[i]!='(' ) goto page_skip;
221305
nFixed = nUsed;
222306
nHelp = 0;
223307
return;
224308
@@ -234,15 +318,11 @@
234318
** Compare two entries
235319
*/
236320
int e_compare(const void *a, const void *b){
237321
const Entry *pA = (const Entry*)a;
238322
const Entry *pB = (const Entry*)b;
239
- int x = pA->eType - pB->eType;
240
- if( x==0 ){
241
- x = strcmp(pA->zPath, pB->zPath);
242
- }
243
- return x;
323
+ return strcmp(pA->zPath, pB->zPath);
244324
}
245325
246326
/*
247327
** Build the binary search table.
248328
*/
@@ -292,31 +372,22 @@
292372
/* Generate the aCommand[] table */
293373
printf("static const CmdOrPage aCommand[] = {\n");
294374
for(i=0; i<nFixed; i++){
295375
const char *z = aEntry[i].zPath;
296376
int n = strlen(z);
297
- int cmdFlags = (1==aEntry[i].eType) ? 0x01 : 0x08;
298
- if( 0x01==cmdFlags ){
299
- if( z[n-1]=='*' ){
300
- n--;
301
- cmdFlags = 0x02;
302
- }else if( strncmp(z, "test-", 5)==0 ){
303
- cmdFlags = 0x04;
304
- }
305
- }
306377
if( aEntry[i].zIf ){
307378
printf("%s", aEntry[i].zIf);
308
- }else if( aEntry[i].eType==0 ){
379
+ }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){
309380
nWeb++;
310381
}
311
- printf(" { \"%s%.*s\",%*s%s,%*szHelp%03d, %d },\n",
312
- (0x08 & cmdFlags) ? "/" : "", n, z,
313
- 25-n-((0x8&cmdFlags)!=0), "",
382
+ printf(" { \"%.*s\",%*s%s,%*szHelp%03d, 0x%02x },\n",
383
+ n, z,
384
+ 25-n, "",
314385
aEntry[i].zFunc,
315386
(int)(30-strlen(aEntry[i].zFunc)), "",
316387
aEntry[i].iHelp,
317
- cmdFlags
388
+ aEntry[i].eType
318389
);
319390
if( aEntry[i].zIf ) printf("#endif\n");
320391
}
321392
printf("};\n");
322393
printf("#define FOSSIL_FIRST_CMD %d\n", nWeb);
@@ -334,12 +405,12 @@
334405
}
335406
nLine = 0;
336407
while( fgets(zLine, sizeof(zLine), in) ){
337408
nLine++;
338409
scan_for_if(zLine);
339
- scan_for_label("WEBPAGE:",zLine,0);
340
- scan_for_label("COMMAND:",zLine,1);
410
+ scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE);
411
+ scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND);
341412
scan_for_func(zLine);
342413
}
343414
fclose(in);
344415
nUsed = nFixed;
345416
}
@@ -349,7 +420,7 @@
349420
for(i=1; i<argc; i++){
350421
zFile = argv[i];
351422
process_file();
352423
}
353424
build_table();
354
- return 0;
425
+ return nErr;
355426
}
356427
--- src/mkindex.c
+++ src/mkindex.c
@@ -22,49 +22,68 @@
22 ** to those entry points.
23 **
24 ** The source code is scanned for comment lines of the form:
25 **
26 ** WEBPAGE: /abc/xyz
 
27 **
28 ** This comment should be followed by a function definition of the
29 ** form:
30 **
31 ** void function_name(void){
32 **
33 ** This routine creates C source code for a constant table that maps
34 ** webpage name into pointers to the function.
35 **
36 ** We also scan for comments lines of this form:
37 **
38 ** COMMAND: cmdname
39 **
40 ** These entries build a constant table used to map command names into
41 ** functions. If cmdname ends with "*" then the command is a second-tier
42 ** command that is not displayed by the "fossil help" command. The
43 ** final "*" is not considered to be part of the command name.
44 **
45 ** Comment text following COMMAND: through the end of the comment is
46 ** understood to be help text for the command specified. This help
47 ** text is accumulated and a table containing the text for each command
48 ** is generated. That table is used implement the "fossil help" command
49 ** and the "/help" HTTP method.
50 **
51 ** Multiple occurrences of WEBPAGE: or COMMAND: (but not both) can appear
52 ** before each function name. In this way, webpages and commands can
53 ** have aliases.
 
 
 
 
 
 
 
 
54 */
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <ctype.h>
58 #include <assert.h>
59 #include <string.h>
 
 
 
 
 
 
 
 
 
 
 
60
61 /*
62 ** Each entry looks like this:
63 */
64 typedef struct Entry {
65 int eType; /* 0: webpage, 1: command */
66 char *zIf; /* Enclose in #if */
67 char *zFunc; /* Name of implementation */
68 char *zPath; /* Webpage or command name */
69 char *zHelp; /* Help text */
70 int iHelp; /* Index of Help text */
@@ -106,10 +125,15 @@
106 ** Current filename and line number
107 */
108 char *zFile;
109 int nLine;
110
 
 
 
 
 
111 /*
112 ** Duplicate N characters of a string.
113 */
114 char *string_dup(const char *zSrc, int n){
115 char *z;
@@ -118,32 +142,92 @@
118 if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); }
119 strncpy(z, zSrc, n);
120 z[n] = 0;
121 return z;
122 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
124 /*
125 ** Scan a line looking for comments containing zLabel. Make
126 ** new entries if found.
127 */
128 void scan_for_label(const char *zLabel, char *zLine, int eType){
129 int i, j;
130 int len = strlen(zLabel);
131 if( nUsed>=N_ENTRY ) return;
132 for(i=0; isspace(zLine[i]) || zLine[i]=='*'; i++){}
133 if( zLine[i]!=zLabel[0] ) return;
134 if( strncmp(&zLine[i],zLabel, len)==0 ){
135 i += len;
136 }else{
137 return;
138 }
139 while( isspace(zLine[i]) ){ i++; }
140 if( zLine[i]=='/' ) i++;
141 for(j=0; zLine[i+j] && !isspace(zLine[i+j]); j++){}
142 aEntry[nUsed].eType = eType;
143 aEntry[nUsed].zPath = string_dup(&zLine[i], j);
 
 
 
 
 
144 aEntry[nUsed].zFunc = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 nUsed++;
146 }
147
148 /*
149 ** Check to see if the current line is an #if and if it is, add it to
@@ -152,11 +236,11 @@
152 */
153 void scan_for_if(const char *zLine){
154 int i;
155 int len;
156 if( zLine[0]!='#' ) return;
157 for(i=1; isspace(zLine[i]); i++){}
158 if( zLine[i]==0 ) return;
159 len = strlen(&zLine[i]);
160 if( strncmp(&zLine[i],"if",2)==0 ){
161 zIf[0] = '#';
162 memcpy(&zIf[1], &zLine[i], len+1);
@@ -171,11 +255,11 @@
171 void scan_for_func(char *zLine){
172 int i,j,k;
173 char *z;
174 if( nUsed<=nFixed ) return;
175 if( strncmp(zLine, "**", 2)==0
176 && isspace(zLine[2])
177 && strlen(zLine)<sizeof(zHelp)-nHelp-1
178 && nUsed>nFixed
179 && strncmp(zLine,"** COMMAND:",11)!=0
180 && strncmp(zLine,"** WEBPAGE:",11)!=0
181 ){
@@ -186,25 +270,25 @@
186 strcpy(&zHelp[nHelp], &zLine[3]);
187 nHelp += strlen(&zHelp[nHelp]);
188 }
189 return;
190 }
191 for(i=0; isspace(zLine[i]); i++){}
192 if( zLine[i]==0 ) return;
193 if( strncmp(&zLine[i],"void",4)!=0 ){
194 if( zLine[i]!='*' ) goto page_skip;
195 return;
196 }
197 i += 4;
198 if( !isspace(zLine[i]) ) goto page_skip;
199 while( isspace(zLine[i]) ){ i++; }
200 for(j=0; isalnum(zLine[i+j]) || zLine[i+j]=='_'; j++){}
201 if( j==0 ) goto page_skip;
202 for(k=nHelp-1; k>=0 && isspace(zHelp[k]); k--){}
203 nHelp = k+1;
204 zHelp[nHelp] = 0;
205 for(k=0; k<nHelp && isspace(zHelp[k]); k++){}
206 if( k<nHelp ){
207 z = string_dup(&zHelp[k], nHelp-k);
208 }else{
209 z = "";
210 }
@@ -214,11 +298,11 @@
214 aEntry[k].zHelp = z;
215 z = 0;
216 aEntry[k].iHelp = nFixed;
217 }
218 i+=j;
219 while( isspace(zLine[i]) ){ i++; }
220 if( zLine[i]!='(' ) goto page_skip;
221 nFixed = nUsed;
222 nHelp = 0;
223 return;
224
@@ -234,15 +318,11 @@
234 ** Compare two entries
235 */
236 int e_compare(const void *a, const void *b){
237 const Entry *pA = (const Entry*)a;
238 const Entry *pB = (const Entry*)b;
239 int x = pA->eType - pB->eType;
240 if( x==0 ){
241 x = strcmp(pA->zPath, pB->zPath);
242 }
243 return x;
244 }
245
246 /*
247 ** Build the binary search table.
248 */
@@ -292,31 +372,22 @@
292 /* Generate the aCommand[] table */
293 printf("static const CmdOrPage aCommand[] = {\n");
294 for(i=0; i<nFixed; i++){
295 const char *z = aEntry[i].zPath;
296 int n = strlen(z);
297 int cmdFlags = (1==aEntry[i].eType) ? 0x01 : 0x08;
298 if( 0x01==cmdFlags ){
299 if( z[n-1]=='*' ){
300 n--;
301 cmdFlags = 0x02;
302 }else if( strncmp(z, "test-", 5)==0 ){
303 cmdFlags = 0x04;
304 }
305 }
306 if( aEntry[i].zIf ){
307 printf("%s", aEntry[i].zIf);
308 }else if( aEntry[i].eType==0 ){
309 nWeb++;
310 }
311 printf(" { \"%s%.*s\",%*s%s,%*szHelp%03d, %d },\n",
312 (0x08 & cmdFlags) ? "/" : "", n, z,
313 25-n-((0x8&cmdFlags)!=0), "",
314 aEntry[i].zFunc,
315 (int)(30-strlen(aEntry[i].zFunc)), "",
316 aEntry[i].iHelp,
317 cmdFlags
318 );
319 if( aEntry[i].zIf ) printf("#endif\n");
320 }
321 printf("};\n");
322 printf("#define FOSSIL_FIRST_CMD %d\n", nWeb);
@@ -334,12 +405,12 @@
334 }
335 nLine = 0;
336 while( fgets(zLine, sizeof(zLine), in) ){
337 nLine++;
338 scan_for_if(zLine);
339 scan_for_label("WEBPAGE:",zLine,0);
340 scan_for_label("COMMAND:",zLine,1);
341 scan_for_func(zLine);
342 }
343 fclose(in);
344 nUsed = nFixed;
345 }
@@ -349,7 +420,7 @@
349 for(i=1; i<argc; i++){
350 zFile = argv[i];
351 process_file();
352 }
353 build_table();
354 return 0;
355 }
356
--- src/mkindex.c
+++ src/mkindex.c
@@ -22,49 +22,68 @@
22 ** to those entry points.
23 **
24 ** The source code is scanned for comment lines of the form:
25 **
26 ** WEBPAGE: /abc/xyz
27 ** COMMAND: cmdname
28 **
29 ** These comment should be followed by a function definition of the
30 ** form:
31 **
32 ** void function_name(void){
33 **
34 ** This routine creates C source code for a constant table that maps
35 ** command and webpage name into pointers to the function.
36 **
37 ** Command names can divided into three classes: 1st-tier, 2nd-tier,
38 ** and test. 1st-tier commands are the most frequently used and the
39 ** ones that show up with "fossil help". 2nd-tier are seldom-used and/or
40 ** legacy command. Test commands are unsupported commands used for testing
41 ** and analysis only.
42 **
43 ** Commands are 1st-tier by default. If the command name begins with
44 ** "test-" or if the command name as a "test" argument, then it becomes
45 ** a test command. If the command name has a "2nd-tier" argument or ends
46 ** with a "*" character, it is second tier. Examples:
47 **
48 ** COMMAND: abcde*
49 ** COMMAND: fghij 2nd-tier
50 ** COMMAND: test-xyzzy
51 ** COMMAND: xyzzy test
52 **
53 ** New arguments may be added in future releases that set additional
54 ** bits in the eCmdFlags field.
55 **
56 ** Additional lines of comment after the COMMAND: or WEBPAGE: become
57 ** the built-in help text for that command or webpage.
58 **
59 ** Multiple COMMAND: entries can be attached to the same command, thus
60 ** creating multiple aliases for that command. Similarly, multiple
61 ** WEBPAGE: entries can be attached to the same webpage function, to give
62 ** that page aliases.
63 */
64 #include <stdio.h>
65 #include <stdlib.h>
 
66 #include <assert.h>
67 #include <string.h>
68
69 /***************************************************************************
70 ** These macros must match similar macros in dispatch.c.
71 **
72 ** Allowed values for CmdOrPage.eCmdFlags. */
73 #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */
74 #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */
75 #define CMDFLAG_TEST 0x0004 /* Commands for testing only */
76 #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */
77 #define CMDFLAG_COMMAND 0x0010 /* A command */
78 /**************************************************************************/
79
80 /*
81 ** Each entry looks like this:
82 */
83 typedef struct Entry {
84 int eType; /* CMDFLAG_* values */
85 char *zIf; /* Enclose in #if */
86 char *zFunc; /* Name of implementation */
87 char *zPath; /* Webpage or command name */
88 char *zHelp; /* Help text */
89 int iHelp; /* Index of Help text */
@@ -106,10 +125,15 @@
125 ** Current filename and line number
126 */
127 char *zFile;
128 int nLine;
129
130 /*
131 ** Number of errors
132 */
133 int nErr = 0;
134
135 /*
136 ** Duplicate N characters of a string.
137 */
138 char *string_dup(const char *zSrc, int n){
139 char *z;
@@ -118,32 +142,92 @@
142 if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); }
143 strncpy(z, zSrc, n);
144 z[n] = 0;
145 return z;
146 }
147
148 /*
149 ** Safe isspace macro. Works with signed characters.
150 */
151 int fossil_isspace(char c){
152 return c==' ' || (c<='\r' && c>='\t');
153 }
154
155 /*
156 ** Safe isident macro. Works with signed characters.
157 */
158 int fossil_isident(char c){
159 if( c>='a' && c<='z' ) return 1;
160 if( c>='A' && c<='Z' ) return 1;
161 if( c>='0' && c<='9' ) return 1;
162 if( c=='_' ) return 1;
163 return 0;
164 }
165
166 /*
167 ** Scan a line looking for comments containing zLabel. Make
168 ** new entries if found.
169 */
170 void scan_for_label(const char *zLabel, char *zLine, int eType){
171 int i, j;
172 int len = strlen(zLabel);
173 if( nUsed>=N_ENTRY ) return;
174 for(i=0; fossil_isspace(zLine[i]) || zLine[i]=='*'; i++){}
175 if( zLine[i]!=zLabel[0] ) return;
176 if( strncmp(&zLine[i],zLabel, len)==0 ){
177 i += len;
178 }else{
179 return;
180 }
181 while( fossil_isspace(zLine[i]) ){ i++; }
182 if( zLine[i]=='/' ) i++;
183 for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
184 aEntry[nUsed].eType = eType;
185 if( eType & CMDFLAG_WEBPAGE ){
186 aEntry[nUsed].zPath = string_dup(&zLine[i-1], j+1);
187 aEntry[nUsed].zPath[0] = '/';
188 }else{
189 aEntry[nUsed].zPath = string_dup(&zLine[i], j);
190 }
191 aEntry[nUsed].zFunc = 0;
192 if( (eType & CMDFLAG_COMMAND)!=0 ){
193 if( strncmp(&zLine[i], "test-", 5)==0 ){
194 /* Commands that start with "test-" are test-commands */
195 aEntry[nUsed].eType |= CMDFLAG_TEST;
196 }else if( zLine[i+j-1]=='*' ){
197 /* If the command name ends in '*', remove the '*' from the name
198 ** but move the command into the second tier */
199 aEntry[nUsed].zPath[j-1] = 0;
200 aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
201 }else{
202 /* Otherwise, this is a first-tier command */
203 aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
204 }
205 }
206
207 /* Processing additional flags that might following the command name */
208 while( zLine[i+j]!=0 ){
209 i += j;
210 while( fossil_isspace(zLine[i]) ){ i++; }
211 if( zLine[i]==0 ) break;
212 for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
213 if( j==8 && strncmp(&zLine[i], "1st-tier", j)==0 ){
214 aEntry[nUsed].eType &= ~(CMDFLAG_2ND_TIER|CMDFLAG_TEST);
215 aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
216 }else if( j==8 && strncmp(&zLine[i], "2nd-tier", j)==0 ){
217 aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_TEST);
218 aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
219 }else if( j==4 && strncmp(&zLine[i], "test", j)==0 ){
220 aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER);
221 aEntry[nUsed].eType |= CMDFLAG_TEST;
222 }else{
223 fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
224 zFile, nLine, j, &zLine[i]);
225 nErr++;
226 }
227 }
228
229 nUsed++;
230 }
231
232 /*
233 ** Check to see if the current line is an #if and if it is, add it to
@@ -152,11 +236,11 @@
236 */
237 void scan_for_if(const char *zLine){
238 int i;
239 int len;
240 if( zLine[0]!='#' ) return;
241 for(i=1; fossil_isspace(zLine[i]); i++){}
242 if( zLine[i]==0 ) return;
243 len = strlen(&zLine[i]);
244 if( strncmp(&zLine[i],"if",2)==0 ){
245 zIf[0] = '#';
246 memcpy(&zIf[1], &zLine[i], len+1);
@@ -171,11 +255,11 @@
255 void scan_for_func(char *zLine){
256 int i,j,k;
257 char *z;
258 if( nUsed<=nFixed ) return;
259 if( strncmp(zLine, "**", 2)==0
260 && fossil_isspace(zLine[2])
261 && strlen(zLine)<sizeof(zHelp)-nHelp-1
262 && nUsed>nFixed
263 && strncmp(zLine,"** COMMAND:",11)!=0
264 && strncmp(zLine,"** WEBPAGE:",11)!=0
265 ){
@@ -186,25 +270,25 @@
270 strcpy(&zHelp[nHelp], &zLine[3]);
271 nHelp += strlen(&zHelp[nHelp]);
272 }
273 return;
274 }
275 for(i=0; fossil_isspace(zLine[i]); i++){}
276 if( zLine[i]==0 ) return;
277 if( strncmp(&zLine[i],"void",4)!=0 ){
278 if( zLine[i]!='*' ) goto page_skip;
279 return;
280 }
281 i += 4;
282 if( !fossil_isspace(zLine[i]) ) goto page_skip;
283 while( fossil_isspace(zLine[i]) ){ i++; }
284 for(j=0; fossil_isident(zLine[i+j]); j++){}
285 if( j==0 ) goto page_skip;
286 for(k=nHelp-1; k>=0 && fossil_isspace(zHelp[k]); k--){}
287 nHelp = k+1;
288 zHelp[nHelp] = 0;
289 for(k=0; k<nHelp && fossil_isspace(zHelp[k]); k++){}
290 if( k<nHelp ){
291 z = string_dup(&zHelp[k], nHelp-k);
292 }else{
293 z = "";
294 }
@@ -214,11 +298,11 @@
298 aEntry[k].zHelp = z;
299 z = 0;
300 aEntry[k].iHelp = nFixed;
301 }
302 i+=j;
303 while( fossil_isspace(zLine[i]) ){ i++; }
304 if( zLine[i]!='(' ) goto page_skip;
305 nFixed = nUsed;
306 nHelp = 0;
307 return;
308
@@ -234,15 +318,11 @@
318 ** Compare two entries
319 */
320 int e_compare(const void *a, const void *b){
321 const Entry *pA = (const Entry*)a;
322 const Entry *pB = (const Entry*)b;
323 return strcmp(pA->zPath, pB->zPath);
 
 
 
 
324 }
325
326 /*
327 ** Build the binary search table.
328 */
@@ -292,31 +372,22 @@
372 /* Generate the aCommand[] table */
373 printf("static const CmdOrPage aCommand[] = {\n");
374 for(i=0; i<nFixed; i++){
375 const char *z = aEntry[i].zPath;
376 int n = strlen(z);
 
 
 
 
 
 
 
 
 
377 if( aEntry[i].zIf ){
378 printf("%s", aEntry[i].zIf);
379 }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){
380 nWeb++;
381 }
382 printf(" { \"%.*s\",%*s%s,%*szHelp%03d, 0x%02x },\n",
383 n, z,
384 25-n, "",
385 aEntry[i].zFunc,
386 (int)(30-strlen(aEntry[i].zFunc)), "",
387 aEntry[i].iHelp,
388 aEntry[i].eType
389 );
390 if( aEntry[i].zIf ) printf("#endif\n");
391 }
392 printf("};\n");
393 printf("#define FOSSIL_FIRST_CMD %d\n", nWeb);
@@ -334,12 +405,12 @@
405 }
406 nLine = 0;
407 while( fgets(zLine, sizeof(zLine), in) ){
408 nLine++;
409 scan_for_if(zLine);
410 scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE);
411 scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND);
412 scan_for_func(zLine);
413 }
414 fclose(in);
415 nUsed = nFixed;
416 }
@@ -349,7 +420,7 @@
420 for(i=1; i<argc; i++){
421 zFile = argv[i];
422 process_file();
423 }
424 build_table();
425 return nErr;
426 }
427

Keyboard Shortcuts

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