Fossil SCM

Added the "e" capability for viewing ticket submitter email addresses. Additional tinkering toward the design of tickets. This check-in is only thinly tested.

drh 2007-11-05 02:42 trunk
Commit 929d28e358248b19de0186fe0e82519ad124d26f
+37 -1
--- src/login.c
+++ src/login.c
@@ -291,11 +291,11 @@
291291
switch( zCap[i] ){
292292
case 's': g.okSetup = 1;
293293
case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
294294
g.okRdWiki = g.okWrWiki = g.okNewWiki =
295295
g.okApndWiki = g.okHistory = g.okClone =
296
- g.okNewTkt = g.okPassword = 1;
296
+ g.okNewTkt = g.okPassword = g.okRdAddr = 1;
297297
case 'i': g.okRead = g.okWrite = 1; break;
298298
case 'o': g.okRead = 1; break;
299299
300300
case 'd': g.okDelete = 1; break;
301301
case 'h': g.okHistory = 1; break;
@@ -306,18 +306,54 @@
306306
case 'j': g.okRdWiki = 1; break;
307307
case 'k': g.okWrWiki = g.okRdWiki = g.okApndWiki =1; break;
308308
case 'm': g.okApndWiki = 1; break;
309309
case 'f': g.okNewWiki = 1; break;
310310
311
+ case 'e': g.okRdAddr = 1; break;
311312
case 'r': g.okRdTkt = 1; break;
312313
case 'n': g.okNewTkt = 1; break;
313314
case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
314315
g.okApndTkt = 1; break;
315316
case 'c': g.okApndTkt = 1; break;
316317
}
317318
}
318319
}
320
+
321
+/*
322
+** If the current login lacks any of the capabilities listed in
323
+** the input, then return 0. If all capabilities are present, then
324
+** return 1.
325
+*/
326
+int login_has_capability(const char *zCap, int nCap){
327
+ int i;
328
+ int rc = 1;
329
+ if( nCap<0 ) nCap = strlen(zCap);
330
+ for(i=0; i<nCap && rc && zCap[i]; i++){
331
+ switch( zCap[i] ){
332
+ case 'a': rc = g.okAdmin; break;
333
+ case 'c': rc = g.okApndTkt; break;
334
+ case 'd': rc = g.okDelete; break;
335
+ case 'e': rc = g.okRdAddr; break;
336
+ case 'f': rc = g.okNewWiki; break;
337
+ case 'g': rc = g.okClone; break;
338
+ case 'h': rc = g.okHistory; break;
339
+ case 'i': rc = g.okWrite; break;
340
+ case 'j': rc = g.okRdWiki; break;
341
+ case 'k': rc = g.okWrWiki; break;
342
+ case 'm': rc = g.okApndWiki; break;
343
+ case 'n': rc = g.okNewTkt; break;
344
+ case 'o': rc = g.okRead; break;
345
+ case 'p': rc = g.okPassword; break;
346
+ case 'q': rc = g.okQuery; break;
347
+ case 'r': rc = g.okRdTkt; break;
348
+ case 's': rc = g.okSetup; break;
349
+ case 'w': rc = g.okWrTkt; break;
350
+ default: rc = 0; break;
351
+ }
352
+ }
353
+ return rc;
354
+}
319355
320356
/*
321357
** Call this routine when the credential check fails. It causes
322358
** a redirect to the "login" page.
323359
*/
324360
--- src/login.c
+++ src/login.c
@@ -291,11 +291,11 @@
291 switch( zCap[i] ){
292 case 's': g.okSetup = 1;
293 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
294 g.okRdWiki = g.okWrWiki = g.okNewWiki =
295 g.okApndWiki = g.okHistory = g.okClone =
296 g.okNewTkt = g.okPassword = 1;
297 case 'i': g.okRead = g.okWrite = 1; break;
298 case 'o': g.okRead = 1; break;
299
300 case 'd': g.okDelete = 1; break;
301 case 'h': g.okHistory = 1; break;
@@ -306,18 +306,54 @@
306 case 'j': g.okRdWiki = 1; break;
307 case 'k': g.okWrWiki = g.okRdWiki = g.okApndWiki =1; break;
308 case 'm': g.okApndWiki = 1; break;
309 case 'f': g.okNewWiki = 1; break;
310
 
311 case 'r': g.okRdTkt = 1; break;
312 case 'n': g.okNewTkt = 1; break;
313 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
314 g.okApndTkt = 1; break;
315 case 'c': g.okApndTkt = 1; break;
316 }
317 }
318 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
320 /*
321 ** Call this routine when the credential check fails. It causes
322 ** a redirect to the "login" page.
323 */
324
--- src/login.c
+++ src/login.c
@@ -291,11 +291,11 @@
291 switch( zCap[i] ){
292 case 's': g.okSetup = 1;
293 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
294 g.okRdWiki = g.okWrWiki = g.okNewWiki =
295 g.okApndWiki = g.okHistory = g.okClone =
296 g.okNewTkt = g.okPassword = g.okRdAddr = 1;
297 case 'i': g.okRead = g.okWrite = 1; break;
298 case 'o': g.okRead = 1; break;
299
300 case 'd': g.okDelete = 1; break;
301 case 'h': g.okHistory = 1; break;
@@ -306,18 +306,54 @@
306 case 'j': g.okRdWiki = 1; break;
307 case 'k': g.okWrWiki = g.okRdWiki = g.okApndWiki =1; break;
308 case 'm': g.okApndWiki = 1; break;
309 case 'f': g.okNewWiki = 1; break;
310
311 case 'e': g.okRdAddr = 1; break;
312 case 'r': g.okRdTkt = 1; break;
313 case 'n': g.okNewTkt = 1; break;
314 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
315 g.okApndTkt = 1; break;
316 case 'c': g.okApndTkt = 1; break;
317 }
318 }
319 }
320
321 /*
322 ** If the current login lacks any of the capabilities listed in
323 ** the input, then return 0. If all capabilities are present, then
324 ** return 1.
325 */
326 int login_has_capability(const char *zCap, int nCap){
327 int i;
328 int rc = 1;
329 if( nCap<0 ) nCap = strlen(zCap);
330 for(i=0; i<nCap && rc && zCap[i]; i++){
331 switch( zCap[i] ){
332 case 'a': rc = g.okAdmin; break;
333 case 'c': rc = g.okApndTkt; break;
334 case 'd': rc = g.okDelete; break;
335 case 'e': rc = g.okRdAddr; break;
336 case 'f': rc = g.okNewWiki; break;
337 case 'g': rc = g.okClone; break;
338 case 'h': rc = g.okHistory; break;
339 case 'i': rc = g.okWrite; break;
340 case 'j': rc = g.okRdWiki; break;
341 case 'k': rc = g.okWrWiki; break;
342 case 'm': rc = g.okApndWiki; break;
343 case 'n': rc = g.okNewTkt; break;
344 case 'o': rc = g.okRead; break;
345 case 'p': rc = g.okPassword; break;
346 case 'q': rc = g.okQuery; break;
347 case 'r': rc = g.okRdTkt; break;
348 case 's': rc = g.okSetup; break;
349 case 'w': rc = g.okWrTkt; break;
350 default: rc = 0; break;
351 }
352 }
353 return rc;
354 }
355
356 /*
357 ** Call this routine when the credential check fails. It causes
358 ** a redirect to the "login" page.
359 */
360
+1
--- src/main.c
+++ src/main.c
@@ -103,10 +103,11 @@
103103
int okWrWiki; /* k: edit wiki via web */
104104
int okRdTkt; /* r: view tickets via web */
105105
int okNewTkt; /* n: create new tickets */
106106
int okApndTkt; /* c: append to tickets via the web */
107107
int okWrTkt; /* w: make changes to tickets via web */
108
+ int okRdAddr; /* e: read email addresses on tickets */
108109
109110
FILE *fDebug; /* Write debug information here, if the file exists */
110111
};
111112
112113
/*
113114
--- src/main.c
+++ src/main.c
@@ -103,10 +103,11 @@
103 int okWrWiki; /* k: edit wiki via web */
104 int okRdTkt; /* r: view tickets via web */
105 int okNewTkt; /* n: create new tickets */
106 int okApndTkt; /* c: append to tickets via the web */
107 int okWrTkt; /* w: make changes to tickets via web */
 
108
109 FILE *fDebug; /* Write debug information here, if the file exists */
110 };
111
112 /*
113
--- src/main.c
+++ src/main.c
@@ -103,10 +103,11 @@
103 int okWrWiki; /* k: edit wiki via web */
104 int okRdTkt; /* r: view tickets via web */
105 int okNewTkt; /* n: create new tickets */
106 int okApndTkt; /* c: append to tickets via the web */
107 int okWrTkt; /* w: make changes to tickets via web */
108 int okRdAddr; /* e: read email addresses on tickets */
109
110 FILE *fDebug; /* Write debug information here, if the file exists */
111 };
112
113 /*
114
--- src/sample-config1.txt
+++ src/sample-config1.txt
@@ -79,49 +79,88 @@
7979
<td>After filling in the information above, press this button to create
8080
the new ticket</td>
8181
</tr>
8282
</table>
8383
[/status /Open default_value]
84
-} /new_template set
84
+} /new setpage
8585
######################################################################
8686
{
87
+ [
88
+ # Extract the current information from the ticket table
89
+ {SELECT type, status, subsystem, priority,
90
+ severity, contact, title, comment
91
+ FROM ticket
92
+ WHERE tktid=:name} db_prepare
93
+ /name {} cgi_parameter {:name} db_bind
94
+ db_exec
95
+ if /title exists not {
96
+ {<i>No such ticket: } puts
97
+ /name {} cgi_parameter htmlize puts
98
+ {</i>} puts
99
+ return
100
+ } endif
101
+ /title title cgiparam /vtitle store
102
+ /status status cgiparam /vstatus store
103
+ /type type cgiparam /vtype store
104
+
105
+ if /submit cgiexists {
106
+ /name {} cgi_parameter login ticketchng_begin
107
+ if /apndcom cgiexists {
108
+ {+comment}
109
+ {<hr><i>Added by }
110
+ login htmlize concat
111
+ { on } concat
112
+ datetime concat
113
+ {:</i><br} concat
114
+ /apndcom {} cgi_parameter htmlize concat
115
+ ticketchng_field
116
+ } else {
117
+ if vcomment comment eq not {/comment vcomment ticketchng_field} endif
118
+ } endif
119
+ if vtitle title eq not {/title vtitle ticketchng_field} endif
120
+ ticketchng_submit
121
+ baseurl /tktview?name= concat /name {} cgi_parameter concat redirect
122
+ } endif
123
+ ]
124
+ <form method="POST" action="[baseurl]/tktedit">
87125
<table cellpadding="5">
88126
<tr><td align="right">Title:</td><td>
89
- [/title 60 textedit]
127
+ <input type="text" name="title" value="[vtitle htmlize puts] size=60">
90128
</td></tr>
91129
<tr><td align="right">Status:</td><td>
92
- [/status status_choices 20 combobox]
130
+ [vstatus /status status_choices 20 combobox]
93131
</td></tr>
94132
<tr><td align="right">Type:</td><td>
95
- [/type type_choices 20 combobox]
133
+ [vtype /type type_choices 20 combobox]
96134
</td></tr>
97135
<tr><td align="right">Severity:</td><td>
98
- [/severity {High Medium Low} 10 combobox]
136
+ [vseverity /severity {High Medium Low} 10 combobox]
99137
</td></tr>
100138
<tr><td align="right">Priority:</td><td>
101
- [/priority {High Medium Low} 10 combobox]
139
+ [vpriority /priority {High Medium Low} 10 combobox]
102140
</td></tr>
103141
<tr><td align="right">Resolution:</td><td>
104
- [/resolution resolution_choices 20 combobox]
142
+ [vresolution /resolution resolution_choices 20 combobox]
105143
</td></tr>
106144
<tr><td align="right">Subsystem:</td><td>
107
- [/subsystem subsystem_choices 30 combobox]
145
+ [vsubsystem /subsystem subsystem_choices 30 combobox]
108146
</td></tr>
109
- [is_anon not enable_output]
147
+ [{e} hascap enable_output]
110148
<tr><td align="right">Contact:</td><td>
111
- [/contact 40 textedit]
149
+ <input type="text" name="contact" size="40" value="[vcontact htmlize puts]">
112150
</td></tr>
113151
[1 enable_output]
114152
<tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
115
- [/foundin 50 textedit]
153
+ <input type="text" name="foundin" size="50" value="[foundin htmlize puts]">
116154
</td></tr>
117155
<tr><td colspan="2">
118
- [ok_wrtkt /eall 0 paramget and /eall store]
156
+ [w hascap /eall 0 paramget and /eall store]
119157
[eall enable_output]
120158
Description And Comments:<br>
121
- [/comment 70 /comment linecount 15 max multilineedit]<br>
122
- [/aonly {Append Remark} auxbutton]
159
+ <textarea name="comment" cols="80" rows="[comment linecount 15 max 10 min]"
160
+ wrap="virtual" class="wikiedit">[comment htmlize puts]</textarea><br>
161
+ <input type="submit" name="aonly" value="Append Remark">
123162
[eall not enable_output]
124163
Append Remark:<br>
125164
[/comment /cmappnd 70 /cmappnd linecount 10 max multilineappend]<br>
126165
[ok_wrtkt enable_output /eall {Edit All} auxbutton]
127166
[1 enable_output]
@@ -128,16 +167,16 @@
128167
</td></tr>
129168
<tr><td align="right"></td><td>
130169
[{Submit Changes} submitbutton]
131170
</td></tr>
132171
</table>
133
-} /edit_template set
172
+} /edit setpage
134173
######################################################################
135174
{
136175
<table cellpadding="5">
137176
<tr><td align="right">Title:</td><td>
138
- [/title textview]
177
+ [/title htmlize]
139178
</td></tr>
140179
<tr><td align="right">Status:</td><td>
141180
[/status textview]
142181
</td></tr>
143182
<tr><td align="right">Type:</td><td>
@@ -153,11 +192,11 @@
153192
[/priority textview]
154193
</td></tr>
155194
<tr><td align="right">Subsystem:</td><td>
156195
[/subsystem textview]
157196
</td></tr>
158
- [is_anon not enable_output]
197
+ [{e} hascap enable_output]
159198
<tr><td align="right">Contact:</td><td>
160199
[/contact textview]
161200
</td></tr>
162201
[1 enable_output]
163202
<tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
@@ -166,11 +205,11 @@
166205
<tr><td colspan="2">
167206
Description And Comments:<br>
168207
[/comment wikiview]
169208
</td></tr>
170209
</table>
171
-} /view_template set
210
+} /view setpage
172211
173212
##############
174213
# Verb list:
175214
#
176215
# CNEV
177216
--- src/sample-config1.txt
+++ src/sample-config1.txt
@@ -79,49 +79,88 @@
79 <td>After filling in the information above, press this button to create
80 the new ticket</td>
81 </tr>
82 </table>
83 [/status /Open default_value]
84 } /new_template set
85 ######################################################################
86 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87 <table cellpadding="5">
88 <tr><td align="right">Title:</td><td>
89 [/title 60 textedit]
90 </td></tr>
91 <tr><td align="right">Status:</td><td>
92 [/status status_choices 20 combobox]
93 </td></tr>
94 <tr><td align="right">Type:</td><td>
95 [/type type_choices 20 combobox]
96 </td></tr>
97 <tr><td align="right">Severity:</td><td>
98 [/severity {High Medium Low} 10 combobox]
99 </td></tr>
100 <tr><td align="right">Priority:</td><td>
101 [/priority {High Medium Low} 10 combobox]
102 </td></tr>
103 <tr><td align="right">Resolution:</td><td>
104 [/resolution resolution_choices 20 combobox]
105 </td></tr>
106 <tr><td align="right">Subsystem:</td><td>
107 [/subsystem subsystem_choices 30 combobox]
108 </td></tr>
109 [is_anon not enable_output]
110 <tr><td align="right">Contact:</td><td>
111 [/contact 40 textedit]
112 </td></tr>
113 [1 enable_output]
114 <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
115 [/foundin 50 textedit]
116 </td></tr>
117 <tr><td colspan="2">
118 [ok_wrtkt /eall 0 paramget and /eall store]
119 [eall enable_output]
120 Description And Comments:<br>
121 [/comment 70 /comment linecount 15 max multilineedit]<br>
122 [/aonly {Append Remark} auxbutton]
 
123 [eall not enable_output]
124 Append Remark:<br>
125 [/comment /cmappnd 70 /cmappnd linecount 10 max multilineappend]<br>
126 [ok_wrtkt enable_output /eall {Edit All} auxbutton]
127 [1 enable_output]
@@ -128,16 +167,16 @@
128 </td></tr>
129 <tr><td align="right"></td><td>
130 [{Submit Changes} submitbutton]
131 </td></tr>
132 </table>
133 } /edit_template set
134 ######################################################################
135 {
136 <table cellpadding="5">
137 <tr><td align="right">Title:</td><td>
138 [/title textview]
139 </td></tr>
140 <tr><td align="right">Status:</td><td>
141 [/status textview]
142 </td></tr>
143 <tr><td align="right">Type:</td><td>
@@ -153,11 +192,11 @@
153 [/priority textview]
154 </td></tr>
155 <tr><td align="right">Subsystem:</td><td>
156 [/subsystem textview]
157 </td></tr>
158 [is_anon not enable_output]
159 <tr><td align="right">Contact:</td><td>
160 [/contact textview]
161 </td></tr>
162 [1 enable_output]
163 <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
@@ -166,11 +205,11 @@
166 <tr><td colspan="2">
167 Description And Comments:<br>
168 [/comment wikiview]
169 </td></tr>
170 </table>
171 } /view_template set
172
173 ##############
174 # Verb list:
175 #
176 # CNEV
177
--- src/sample-config1.txt
+++ src/sample-config1.txt
@@ -79,49 +79,88 @@
79 <td>After filling in the information above, press this button to create
80 the new ticket</td>
81 </tr>
82 </table>
83 [/status /Open default_value]
84 } /new setpage
85 ######################################################################
86 {
87 [
88 # Extract the current information from the ticket table
89 {SELECT type, status, subsystem, priority,
90 severity, contact, title, comment
91 FROM ticket
92 WHERE tktid=:name} db_prepare
93 /name {} cgi_parameter {:name} db_bind
94 db_exec
95 if /title exists not {
96 {<i>No such ticket: } puts
97 /name {} cgi_parameter htmlize puts
98 {</i>} puts
99 return
100 } endif
101 /title title cgiparam /vtitle store
102 /status status cgiparam /vstatus store
103 /type type cgiparam /vtype store
104
105 if /submit cgiexists {
106 /name {} cgi_parameter login ticketchng_begin
107 if /apndcom cgiexists {
108 {+comment}
109 {<hr><i>Added by }
110 login htmlize concat
111 { on } concat
112 datetime concat
113 {:</i><br} concat
114 /apndcom {} cgi_parameter htmlize concat
115 ticketchng_field
116 } else {
117 if vcomment comment eq not {/comment vcomment ticketchng_field} endif
118 } endif
119 if vtitle title eq not {/title vtitle ticketchng_field} endif
120 ticketchng_submit
121 baseurl /tktview?name= concat /name {} cgi_parameter concat redirect
122 } endif
123 ]
124 <form method="POST" action="[baseurl]/tktedit">
125 <table cellpadding="5">
126 <tr><td align="right">Title:</td><td>
127 <input type="text" name="title" value="[vtitle htmlize puts] size=60">
128 </td></tr>
129 <tr><td align="right">Status:</td><td>
130 [vstatus /status status_choices 20 combobox]
131 </td></tr>
132 <tr><td align="right">Type:</td><td>
133 [vtype /type type_choices 20 combobox]
134 </td></tr>
135 <tr><td align="right">Severity:</td><td>
136 [vseverity /severity {High Medium Low} 10 combobox]
137 </td></tr>
138 <tr><td align="right">Priority:</td><td>
139 [vpriority /priority {High Medium Low} 10 combobox]
140 </td></tr>
141 <tr><td align="right">Resolution:</td><td>
142 [vresolution /resolution resolution_choices 20 combobox]
143 </td></tr>
144 <tr><td align="right">Subsystem:</td><td>
145 [vsubsystem /subsystem subsystem_choices 30 combobox]
146 </td></tr>
147 [{e} hascap enable_output]
148 <tr><td align="right">Contact:</td><td>
149 <input type="text" name="contact" size="40" value="[vcontact htmlize puts]">
150 </td></tr>
151 [1 enable_output]
152 <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
153 <input type="text" name="foundin" size="50" value="[foundin htmlize puts]">
154 </td></tr>
155 <tr><td colspan="2">
156 [w hascap /eall 0 paramget and /eall store]
157 [eall enable_output]
158 Description And Comments:<br>
159 <textarea name="comment" cols="80" rows="[comment linecount 15 max 10 min]"
160 wrap="virtual" class="wikiedit">[comment htmlize puts]</textarea><br>
161 <input type="submit" name="aonly" value="Append Remark">
162 [eall not enable_output]
163 Append Remark:<br>
164 [/comment /cmappnd 70 /cmappnd linecount 10 max multilineappend]<br>
165 [ok_wrtkt enable_output /eall {Edit All} auxbutton]
166 [1 enable_output]
@@ -128,16 +167,16 @@
167 </td></tr>
168 <tr><td align="right"></td><td>
169 [{Submit Changes} submitbutton]
170 </td></tr>
171 </table>
172 } /edit setpage
173 ######################################################################
174 {
175 <table cellpadding="5">
176 <tr><td align="right">Title:</td><td>
177 [/title htmlize]
178 </td></tr>
179 <tr><td align="right">Status:</td><td>
180 [/status textview]
181 </td></tr>
182 <tr><td align="right">Type:</td><td>
@@ -153,11 +192,11 @@
192 [/priority textview]
193 </td></tr>
194 <tr><td align="right">Subsystem:</td><td>
195 [/subsystem textview]
196 </td></tr>
197 [{e} hascap enable_output]
198 <tr><td align="right">Contact:</td><td>
199 [/contact textview]
200 </td></tr>
201 [1 enable_output]
202 <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
@@ -166,11 +205,11 @@
205 <tr><td colspan="2">
206 Description And Comments:<br>
207 [/comment wikiview]
208 </td></tr>
209 </table>
210 } /view setpage
211
212 ##############
213 # Verb list:
214 #
215 # CNEV
216
+8 -3
--- src/setup.c
+++ src/setup.c
@@ -126,10 +126,11 @@
126126
@ <li><p>The permission flags are as follows:</p>
127127
@ <ol type="a">
128128
@ <li value="1"><b>Admin</b>: Create and delete users</li>
129129
@ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
130130
@ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
131
+ @ <li value="5"><b>Email</b>: View EMail addresses on tickets</li>
131132
@ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
132133
@ <li value="7"><b>Clone</b>: Clone the repository</li>
133134
@ <li value="8"><b>History</b>: View detail repository history</li>
134135
@ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
135136
@ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
@@ -160,12 +161,12 @@
160161
/*
161162
** WEBPAGE: /setup_uedit
162163
*/
163164
void user_edit(void){
164165
const char *zId, *zLogin, *zInfo, *zCap;
165
- char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
166
- char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag;
166
+ char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
167
+ char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag, *oae;
167168
int doWrite;
168169
int uid;
169170
int higherUser = 0; /* True if user being edited is SETUP and the */
170171
/* user doing the editing is ADMIN. Disallow editing */
171172
@@ -200,10 +201,11 @@
200201
const char *zLogin;
201202
char zCap[30];
202203
int i = 0;
203204
int aa = P("aa")!=0;
204205
int ad = P("ad")!=0;
206
+ int ae = P("ae")!=0;
205207
int ai = P("ai")!=0;
206208
int aj = P("aj")!=0;
207209
int ak = P("ak")!=0;
208210
int an = P("an")!=0;
209211
int ao = P("ao")!=0;
@@ -218,10 +220,11 @@
218220
int ah = P("ah")!=0;
219221
int ag = P("ag")!=0;
220222
if( aa ){ zCap[i++] = 'a'; }
221223
if( ac ){ zCap[i++] = 'c'; }
222224
if( ad ){ zCap[i++] = 'd'; }
225
+ if( ae ){ zCap[i++] = 'e'; }
223226
if( af ){ zCap[i++] = 'f'; }
224227
if( ah ){ zCap[i++] = 'h'; }
225228
if( ag ){ zCap[i++] = 'g'; }
226229
if( ai ){ zCap[i++] = 'i'; }
227230
if( aj ){ zCap[i++] = 'j'; }
@@ -264,19 +267,20 @@
264267
/* Load the existing information about the user, if any
265268
*/
266269
zLogin = "";
267270
zInfo = "";
268271
zCap = "";
269
- oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oam =
272
+ oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
270273
oan = oao = oap = oaq = oar = oas = oaw = "";
271274
if( uid ){
272275
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
273276
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
274277
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
275278
if( strchr(zCap, 'a') ) oaa = " checked";
276279
if( strchr(zCap, 'c') ) oac = " checked";
277280
if( strchr(zCap, 'd') ) oad = " checked";
281
+ if( strchr(zCap, 'e') ) oae = " checked";
278282
if( strchr(zCap, 'f') ) oaf = " checked";
279283
if( strchr(zCap, 'g') ) oag = " checked";
280284
if( strchr(zCap, 'h') ) oah = " checked";
281285
if( strchr(zCap, 'i') ) oai = " checked";
282286
if( strchr(zCap, 'j') ) oaj = " checked";
@@ -324,10 +328,11 @@
324328
if( g.okSetup ){
325329
@ <input type="checkbox" name="as"%s(oas)>Setup</input><br>
326330
}
327331
@ <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
328332
@ <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
333
+ @ <input type="checkbox" name="ae"%s(oad)>Email</input><br>
329334
@ <input type="checkbox" name="ap"%s(oap)>Password</input><br>
330335
@ <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
331336
@ <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
332337
@ <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
333338
@ <input type="checkbox" name="ah"%s(oah)>History</input><br>
334339
--- src/setup.c
+++ src/setup.c
@@ -126,10 +126,11 @@
126 @ <li><p>The permission flags are as follows:</p>
127 @ <ol type="a">
128 @ <li value="1"><b>Admin</b>: Create and delete users</li>
129 @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
130 @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
 
131 @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
132 @ <li value="7"><b>Clone</b>: Clone the repository</li>
133 @ <li value="8"><b>History</b>: View detail repository history</li>
134 @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
135 @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
@@ -160,12 +161,12 @@
160 /*
161 ** WEBPAGE: /setup_uedit
162 */
163 void user_edit(void){
164 const char *zId, *zLogin, *zInfo, *zCap;
165 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
166 char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag;
167 int doWrite;
168 int uid;
169 int higherUser = 0; /* True if user being edited is SETUP and the */
170 /* user doing the editing is ADMIN. Disallow editing */
171
@@ -200,10 +201,11 @@
200 const char *zLogin;
201 char zCap[30];
202 int i = 0;
203 int aa = P("aa")!=0;
204 int ad = P("ad")!=0;
 
205 int ai = P("ai")!=0;
206 int aj = P("aj")!=0;
207 int ak = P("ak")!=0;
208 int an = P("an")!=0;
209 int ao = P("ao")!=0;
@@ -218,10 +220,11 @@
218 int ah = P("ah")!=0;
219 int ag = P("ag")!=0;
220 if( aa ){ zCap[i++] = 'a'; }
221 if( ac ){ zCap[i++] = 'c'; }
222 if( ad ){ zCap[i++] = 'd'; }
 
223 if( af ){ zCap[i++] = 'f'; }
224 if( ah ){ zCap[i++] = 'h'; }
225 if( ag ){ zCap[i++] = 'g'; }
226 if( ai ){ zCap[i++] = 'i'; }
227 if( aj ){ zCap[i++] = 'j'; }
@@ -264,19 +267,20 @@
264 /* Load the existing information about the user, if any
265 */
266 zLogin = "";
267 zInfo = "";
268 zCap = "";
269 oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oam =
270 oan = oao = oap = oaq = oar = oas = oaw = "";
271 if( uid ){
272 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
273 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
274 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
275 if( strchr(zCap, 'a') ) oaa = " checked";
276 if( strchr(zCap, 'c') ) oac = " checked";
277 if( strchr(zCap, 'd') ) oad = " checked";
 
278 if( strchr(zCap, 'f') ) oaf = " checked";
279 if( strchr(zCap, 'g') ) oag = " checked";
280 if( strchr(zCap, 'h') ) oah = " checked";
281 if( strchr(zCap, 'i') ) oai = " checked";
282 if( strchr(zCap, 'j') ) oaj = " checked";
@@ -324,10 +328,11 @@
324 if( g.okSetup ){
325 @ <input type="checkbox" name="as"%s(oas)>Setup</input><br>
326 }
327 @ <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
328 @ <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
 
329 @ <input type="checkbox" name="ap"%s(oap)>Password</input><br>
330 @ <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
331 @ <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
332 @ <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
333 @ <input type="checkbox" name="ah"%s(oah)>History</input><br>
334
--- src/setup.c
+++ src/setup.c
@@ -126,10 +126,11 @@
126 @ <li><p>The permission flags are as follows:</p>
127 @ <ol type="a">
128 @ <li value="1"><b>Admin</b>: Create and delete users</li>
129 @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
130 @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
131 @ <li value="5"><b>Email</b>: View EMail addresses on tickets</li>
132 @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
133 @ <li value="7"><b>Clone</b>: Clone the repository</li>
134 @ <li value="8"><b>History</b>: View detail repository history</li>
135 @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
136 @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
@@ -160,12 +161,12 @@
161 /*
162 ** WEBPAGE: /setup_uedit
163 */
164 void user_edit(void){
165 const char *zId, *zLogin, *zInfo, *zCap;
166 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
167 char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag, *oae;
168 int doWrite;
169 int uid;
170 int higherUser = 0; /* True if user being edited is SETUP and the */
171 /* user doing the editing is ADMIN. Disallow editing */
172
@@ -200,10 +201,11 @@
201 const char *zLogin;
202 char zCap[30];
203 int i = 0;
204 int aa = P("aa")!=0;
205 int ad = P("ad")!=0;
206 int ae = P("ae")!=0;
207 int ai = P("ai")!=0;
208 int aj = P("aj")!=0;
209 int ak = P("ak")!=0;
210 int an = P("an")!=0;
211 int ao = P("ao")!=0;
@@ -218,10 +220,11 @@
220 int ah = P("ah")!=0;
221 int ag = P("ag")!=0;
222 if( aa ){ zCap[i++] = 'a'; }
223 if( ac ){ zCap[i++] = 'c'; }
224 if( ad ){ zCap[i++] = 'd'; }
225 if( ae ){ zCap[i++] = 'e'; }
226 if( af ){ zCap[i++] = 'f'; }
227 if( ah ){ zCap[i++] = 'h'; }
228 if( ag ){ zCap[i++] = 'g'; }
229 if( ai ){ zCap[i++] = 'i'; }
230 if( aj ){ zCap[i++] = 'j'; }
@@ -264,19 +267,20 @@
267 /* Load the existing information about the user, if any
268 */
269 zLogin = "";
270 zInfo = "";
271 zCap = "";
272 oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
273 oan = oao = oap = oaq = oar = oas = oaw = "";
274 if( uid ){
275 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
276 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
277 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
278 if( strchr(zCap, 'a') ) oaa = " checked";
279 if( strchr(zCap, 'c') ) oac = " checked";
280 if( strchr(zCap, 'd') ) oad = " checked";
281 if( strchr(zCap, 'e') ) oae = " checked";
282 if( strchr(zCap, 'f') ) oaf = " checked";
283 if( strchr(zCap, 'g') ) oag = " checked";
284 if( strchr(zCap, 'h') ) oah = " checked";
285 if( strchr(zCap, 'i') ) oai = " checked";
286 if( strchr(zCap, 'j') ) oaj = " checked";
@@ -324,10 +328,11 @@
328 if( g.okSetup ){
329 @ <input type="checkbox" name="as"%s(oas)>Setup</input><br>
330 }
331 @ <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
332 @ <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
333 @ <input type="checkbox" name="ae"%s(oad)>Email</input><br>
334 @ <input type="checkbox" name="ap"%s(oap)>Password</input><br>
335 @ <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
336 @ <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
337 @ <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
338 @ <input type="checkbox" name="ah"%s(oah)>History</input><br>
339
+115 -119
--- src/subscript.c
+++ src/subscript.c
@@ -80,10 +80,63 @@
8080
#define SBSTT_STRING 4 /* ex: {...} */
8181
#define SBSTT_INTEGER 5 /* Integer including option sign */
8282
#define SBSTT_INCOMPLETE 6 /* Unterminated string token */
8383
#define SBSTT_UNKNOWN 7 /* Unknown token */
8484
#define SBSTT_EOF 8 /* End of input */
85
+
86
+/*
87
+** Values are stored in the hash table as instances of the following
88
+** structure.
89
+*/
90
+typedef struct SbSValue SbSValue;
91
+struct SbSValue {
92
+ int flags; /* Bitmask of SBSVAL_* values */
93
+ union {
94
+ struct {
95
+ int size; /* Number of bytes in string, not counting final zero */
96
+ char *z; /* Pointer to string content */
97
+ } str; /* Value if SBSVAL_STR */
98
+ struct {
99
+ int (*xVerb)(Subscript*, void*); /* Function to do the work */
100
+ void *pArg; /* 2nd parameter to xVerb */
101
+ } verb; /* Value if SBSVAL_VERB */
102
+ } u;
103
+};
104
+#define SBSVAL_VERB 0x0001 /* Value stored in u.verb */
105
+#define SBSVAL_STR 0x0002 /* Value stored in u.str */
106
+#define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */
107
+#define SBSVAL_EXEC 0x0008 /* u.str.z is a script */
108
+
109
+/*
110
+** An entry in the hash table is an instance of this structure.
111
+*/
112
+typedef struct SbsHashEntry SbsHashEntry;
113
+struct SbsHashEntry {
114
+ SbsHashEntry *pNext; /* Next entry with the same hash on zKey */
115
+ SbSValue val; /* The payload */
116
+ int nKey; /* Length of the key */
117
+ char zKey[0]; /* The key */
118
+};
119
+
120
+/*
121
+** A hash table is an instance of the following structure.
122
+*/
123
+typedef struct SbsHashTab SbsHashTab;
124
+struct SbsHashTab {
125
+ SbsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */
126
+};
127
+
128
+/*
129
+** An instance of the Subscript interpreter
130
+*/
131
+struct Subscript {
132
+ int nStack; /* Number of entries on stack */
133
+ SbsHashTab symTab; /* The symbol table */
134
+ char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */
135
+ SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
136
+};
137
+
85138
86139
/*
87140
** Given an input string z of length n, identify the token that
88141
** starts at z[0]. Write the token type into *pTokenType and
89142
** return the length of the token.
@@ -149,33 +202,10 @@
149202
*pTokenType = SBSTT_UNKNOWN;
150203
return 1;
151204
}
152205
153206
154
-/*
155
-** Values are stored in the hash table as instances of the following
156
-** structure.
157
-*/
158
-typedef struct SbSValue SbSValue;
159
-struct SbSValue {
160
- int flags; /* Bitmask of SBSVAL_* values */
161
- union {
162
- struct {
163
- int size; /* Number of bytes in string, not counting final zero */
164
- char *z; /* Pointer to string content */
165
- } str; /* Value if SBSVAL_STR */
166
- struct {
167
- int (*xVerb)(Subscript*, void*); /* Function to do the work */
168
- void *pArg; /* 2nd parameter to xVerb */
169
- } verb; /* Value if SBSVAL_VERB */
170
- } u;
171
-};
172
-#define SBSVAL_VERB 0x0001 /* Value stored in u.verb */
173
-#define SBSVAL_STR 0x0002 /* Value stored in u.str */
174
-#define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */
175
-#define SBSVAL_EXEC 0x0008 /* u.str.z is a script */
176
-
177207
/*
178208
** Release any memory allocated by a value.
179209
*/
180210
static void sbs_value_reset(SbSValue *p){
181211
if( p->flags & SBSVAL_DYN ){
@@ -184,30 +214,10 @@
184214
p->u.str.z = "";
185215
p->u.str.size = 0;
186216
}
187217
}
188218
189
-
190
-/*
191
-** An entry in the hash table is an instance of this structure.
192
-*/
193
-typedef struct SbsHashEntry SbsHashEntry;
194
-struct SbsHashEntry {
195
- SbsHashEntry *pNext; /* Next entry with the same hash on zKey */
196
- SbSValue val; /* The payload */
197
- int nKey; /* Length of the key */
198
- char zKey[0]; /* The key */
199
-};
200
-
201
-/*
202
-** A hash table is an instance of the following structure.
203
-*/
204
-typedef struct SbsHashTab SbsHashTab;
205
-struct SbsHashTab {
206
- SbsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */
207
-};
208
-
209219
/*
210220
** Compute a hash on a string.
211221
*/
212222
static int sbs_hash(const char *z, int n){
213223
int h = 0;
@@ -292,20 +302,10 @@
292302
}
293303
}
294304
memset(pHash, 0, sizeof(*pHash));
295305
}
296306
297
-/*
298
-** An instance of the Subscript interpreter
299
-*/
300
-struct Subscript {
301
- int nStack; /* Number of entries on stack */
302
- SbsHashTab symTab; /* The symbol table */
303
- char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */
304
- SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
305
-};
306
-
307307
/*
308308
** Push a value onto the stack of an interpreter
309309
*/
310310
static int sbs_push(Subscript *p, SbSValue *pVal){
311311
if( p->nStack>=SBSCONFIG_NSTACK ){
@@ -313,10 +313,23 @@
313313
return SBS_ERROR;
314314
}
315315
p->aStack[p->nStack++] = *pVal;
316316
return SBS_OK;
317317
}
318
+
319
+/*
320
+** Create a new subscript interpreter. Return a pointer to the
321
+** new interpreter, or return NULL if malloc fails.
322
+*/
323
+struct Subscript *SbS_Create(void){
324
+ Subscript *p;
325
+ p = malloc( sizeof(*p) );
326
+ if( p ){
327
+ memset(p, 0, sizeof(*p));
328
+ }
329
+ return p;
330
+}
318331
319332
/*
320333
** Destroy an subscript interpreter
321334
*/
322335
void SbS_Destroy(struct Subscript *p){
@@ -570,28 +583,49 @@
570583
case SBSOP_ADD: c = a+b; break;
571584
case SBSOP_SUB: c = a-b; break;
572585
case SBSOP_MUL: c = a*b; break;
573586
case SBSOP_DIV: c = b!=0 ? a/b : 0; break;
574587
case SBSOP_AND: c = a && b; break;
575
- case SBSOP_OR: c = a || b; break;
588
+ case SBSOP_OR: c = a || b; break;
576589
case SBSOP_MIN: c = a<b ? a : b; break;
577590
case SBSOP_MAX: c = a<b ? b : a; break;
578591
}
579592
SbS_Pop(p, 2);
580593
SbS_PushInt(p, c);
581594
return 0;
582595
}
596
+
597
+/*
598
+** Subscript command: STRING hascap INTEGER
599
+**
600
+** Return true if the user has all of the capabilities listed.
601
+*/
602
+static int hascapCmd(struct Subscript *p, void *pNotUsed){
603
+ const char *z;
604
+ int i, n, a;
605
+ if( SbS_RequireStack(p, 1, "hascap") ) return 1;
606
+ z = SbS_StackValue(p, 0, &n);
607
+ a = login_has_capability(z, n);
608
+ SbS_Pop(p, 1);
609
+ SbS_PushInt(p, a);
610
+}
583611
584612
/*
585613
** Subscript command: STRING puts
586614
*/
587615
static int putsCmd(struct Subscript *p, void *pNotUsed){
588616
int size;
589617
const char *z;
590618
if( SbS_RequireStack(p, 1, "puts") ) return 1;
591619
z = SbS_StackValue(p, 0, &size);
592
- printf("%.*s\n", size, z);
620
+ if( g.cgiPanic ){
621
+ char *zCopy = mprintf("%.*s", size, z);
622
+ cgi_printf("%h", zCopy);
623
+ free(zCopy);
624
+ }else{
625
+ printf("%.*s\n", size, z);
626
+ }
593627
SbS_Pop(p, 1);
594628
return 0;
595629
}
596630
597631
@@ -598,53 +632,38 @@
598632
/*
599633
** A table of built-in commands
600634
*/
601635
static const struct {
602636
const char *zCmd;
603
- int nCmd;
604637
int (*xCmd)(Subscript*,void*);
605638
void *pArg;
606639
} aBuiltin[] = {
607
- { "add", 3, bopCmd, (void*)SBSOP_AND },
608
- { "and", 3, bopCmd, (void*)SBSOP_AND },
609
- { "div", 3, bopCmd, (void*)SBSOP_DIV },
610
- { "max", 3, bopCmd, (void*)SBSOP_MAX },
611
- { "min", 3, bopCmd, (void*)SBSOP_MIN },
612
- { "mul", 3, bopCmd, (void*)SBSOP_MUL },
613
- { "not", 3, notCmd, 0 },
614
- { "or", 2, bopCmd, (void*)SBSOP_OR },
615
- { "puts", 4, putsCmd, 0 },
616
- { "set", 3, setCmd, 0 },
617
- { "sub", 3, bopCmd, (void*)SBSOP_SUB },
618
-};
619
-
620
-/*
621
-** A table of built-in string and integer values
622
-*/
623
-static const struct {
624
- const char *zVar;
625
- int nVar;
626
- int *pI;
627
- char *z;
628
-} aVars[] = {
629
- { "okAdmin", 7, &g.okAdmin, 0 },
630
- { "okSetup", 7, &g.okSetup, 0 },
631
-};
632
-
633
-
634
-
635
-/*
636
-** Create a new subscript interpreter
637
-*/
638
-struct Subscript *SbS_Create(void){
639
- Subscript *p;
640
-
641
- p = malloc( sizeof(*p) );
642
- if( p ){
643
- memset(p, 0, sizeof(*p));
644
- }
645
- return p;
640
+ { "add", bopCmd, (void*)SBSOP_AND },
641
+ { "and", bopCmd, (void*)SBSOP_AND },
642
+ { "div", bopCmd, (void*)SBSOP_DIV },
643
+ { "hascap", hascapCmd, 0 },
644
+ { "max", bopCmd, (void*)SBSOP_MAX },
645
+ { "min", bopCmd, (void*)SBSOP_MIN },
646
+ { "mul", bopCmd, (void*)SBSOP_MUL },
647
+ { "not", notCmd, 0 },
648
+ { "or", bopCmd, (void*)SBSOP_OR },
649
+ { "puts", putsCmd, 0 },
650
+ { "set", setCmd, 0 },
651
+ { "sub", bopCmd, (void*)SBSOP_SUB },
652
+};
653
+
654
+
655
+/*
656
+** Compare a zero-terminated string zPattern against
657
+** an unterminated string zStr of length nStr.
658
+*/
659
+static int compare_cmd(const char *zPattern, const char *zStr, int nStr){
660
+ int c = strncmp(zPattern, zStr, nStr);
661
+ if( c==0 && zPattern[nStr]!=0 ){
662
+ c = -1;
663
+ }
664
+ return c;
646665
}
647666
648667
/*
649668
** Evaluate the script given by the first nScript bytes of zScript[].
650669
** Return 0 on success and non-zero for an error.
@@ -691,43 +710,20 @@
691710
int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1;
692711
int lwr = 0;
693712
rc = SBS_ERROR;
694713
while( upr>=lwr ){
695714
int i = (upr+lwr)/2;
696
- int c = strncmp(zScript, aBuiltin[i].zCmd, n);
715
+ int c = compare_cmd(aBuiltin[i].zCmd, zScript, n);
697716
if( c==0 ){
698717
rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg);
699718
break;
700719
}else if( c<0 ){
701720
upr = i-1;
702721
}else{
703722
lwr = i+1;
704723
}
705724
}
706
- if( upr<lwr ){
707
- /* If it is not a built-in command, look for a built-in
708
- ** variable */
709
- upr = sizeof(aVars)/sizeof(aVars[0]) - 1;
710
- lwr = 0;
711
- while( upr>=lwr ){
712
- int i = (upr+lwr)/2;
713
- int c = strncmp(zScript, aVars[i].zVar, n);
714
- if( c==0 ){
715
- if( aVars[i].pI ){
716
- SbS_PushInt(p, *aVars[i].pI);
717
- }else{
718
- SbS_Push(p, aVars[i].z, -1, 0);
719
- }
720
- rc = SBS_OK;
721
- break;
722
- }else if( c<0 ){
723
- upr = i-1;
724
- }else{
725
- lwr = i+1;
726
- }
727
- }
728
- }
729725
}else if( pVal->flags & SBSVAL_VERB ){
730726
rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg);
731727
}else if( pVal->flags & SBSVAL_EXEC ){
732728
rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size);
733729
}else{
734730
--- src/subscript.c
+++ src/subscript.c
@@ -80,10 +80,63 @@
80 #define SBSTT_STRING 4 /* ex: {...} */
81 #define SBSTT_INTEGER 5 /* Integer including option sign */
82 #define SBSTT_INCOMPLETE 6 /* Unterminated string token */
83 #define SBSTT_UNKNOWN 7 /* Unknown token */
84 #define SBSTT_EOF 8 /* End of input */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
86 /*
87 ** Given an input string z of length n, identify the token that
88 ** starts at z[0]. Write the token type into *pTokenType and
89 ** return the length of the token.
@@ -149,33 +202,10 @@
149 *pTokenType = SBSTT_UNKNOWN;
150 return 1;
151 }
152
153
154 /*
155 ** Values are stored in the hash table as instances of the following
156 ** structure.
157 */
158 typedef struct SbSValue SbSValue;
159 struct SbSValue {
160 int flags; /* Bitmask of SBSVAL_* values */
161 union {
162 struct {
163 int size; /* Number of bytes in string, not counting final zero */
164 char *z; /* Pointer to string content */
165 } str; /* Value if SBSVAL_STR */
166 struct {
167 int (*xVerb)(Subscript*, void*); /* Function to do the work */
168 void *pArg; /* 2nd parameter to xVerb */
169 } verb; /* Value if SBSVAL_VERB */
170 } u;
171 };
172 #define SBSVAL_VERB 0x0001 /* Value stored in u.verb */
173 #define SBSVAL_STR 0x0002 /* Value stored in u.str */
174 #define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */
175 #define SBSVAL_EXEC 0x0008 /* u.str.z is a script */
176
177 /*
178 ** Release any memory allocated by a value.
179 */
180 static void sbs_value_reset(SbSValue *p){
181 if( p->flags & SBSVAL_DYN ){
@@ -184,30 +214,10 @@
184 p->u.str.z = "";
185 p->u.str.size = 0;
186 }
187 }
188
189
190 /*
191 ** An entry in the hash table is an instance of this structure.
192 */
193 typedef struct SbsHashEntry SbsHashEntry;
194 struct SbsHashEntry {
195 SbsHashEntry *pNext; /* Next entry with the same hash on zKey */
196 SbSValue val; /* The payload */
197 int nKey; /* Length of the key */
198 char zKey[0]; /* The key */
199 };
200
201 /*
202 ** A hash table is an instance of the following structure.
203 */
204 typedef struct SbsHashTab SbsHashTab;
205 struct SbsHashTab {
206 SbsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */
207 };
208
209 /*
210 ** Compute a hash on a string.
211 */
212 static int sbs_hash(const char *z, int n){
213 int h = 0;
@@ -292,20 +302,10 @@
292 }
293 }
294 memset(pHash, 0, sizeof(*pHash));
295 }
296
297 /*
298 ** An instance of the Subscript interpreter
299 */
300 struct Subscript {
301 int nStack; /* Number of entries on stack */
302 SbsHashTab symTab; /* The symbol table */
303 char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */
304 SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
305 };
306
307 /*
308 ** Push a value onto the stack of an interpreter
309 */
310 static int sbs_push(Subscript *p, SbSValue *pVal){
311 if( p->nStack>=SBSCONFIG_NSTACK ){
@@ -313,10 +313,23 @@
313 return SBS_ERROR;
314 }
315 p->aStack[p->nStack++] = *pVal;
316 return SBS_OK;
317 }
 
 
 
 
 
 
 
 
 
 
 
 
 
318
319 /*
320 ** Destroy an subscript interpreter
321 */
322 void SbS_Destroy(struct Subscript *p){
@@ -570,28 +583,49 @@
570 case SBSOP_ADD: c = a+b; break;
571 case SBSOP_SUB: c = a-b; break;
572 case SBSOP_MUL: c = a*b; break;
573 case SBSOP_DIV: c = b!=0 ? a/b : 0; break;
574 case SBSOP_AND: c = a && b; break;
575 case SBSOP_OR: c = a || b; break;
576 case SBSOP_MIN: c = a<b ? a : b; break;
577 case SBSOP_MAX: c = a<b ? b : a; break;
578 }
579 SbS_Pop(p, 2);
580 SbS_PushInt(p, c);
581 return 0;
582 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
584 /*
585 ** Subscript command: STRING puts
586 */
587 static int putsCmd(struct Subscript *p, void *pNotUsed){
588 int size;
589 const char *z;
590 if( SbS_RequireStack(p, 1, "puts") ) return 1;
591 z = SbS_StackValue(p, 0, &size);
592 printf("%.*s\n", size, z);
 
 
 
 
 
 
593 SbS_Pop(p, 1);
594 return 0;
595 }
596
597
@@ -598,53 +632,38 @@
598 /*
599 ** A table of built-in commands
600 */
601 static const struct {
602 const char *zCmd;
603 int nCmd;
604 int (*xCmd)(Subscript*,void*);
605 void *pArg;
606 } aBuiltin[] = {
607 { "add", 3, bopCmd, (void*)SBSOP_AND },
608 { "and", 3, bopCmd, (void*)SBSOP_AND },
609 { "div", 3, bopCmd, (void*)SBSOP_DIV },
610 { "max", 3, bopCmd, (void*)SBSOP_MAX },
611 { "min", 3, bopCmd, (void*)SBSOP_MIN },
612 { "mul", 3, bopCmd, (void*)SBSOP_MUL },
613 { "not", 3, notCmd, 0 },
614 { "or", 2, bopCmd, (void*)SBSOP_OR },
615 { "puts", 4, putsCmd, 0 },
616 { "set", 3, setCmd, 0 },
617 { "sub", 3, bopCmd, (void*)SBSOP_SUB },
618 };
619
620 /*
621 ** A table of built-in string and integer values
622 */
623 static const struct {
624 const char *zVar;
625 int nVar;
626 int *pI;
627 char *z;
628 } aVars[] = {
629 { "okAdmin", 7, &g.okAdmin, 0 },
630 { "okSetup", 7, &g.okSetup, 0 },
631 };
632
633
634
635 /*
636 ** Create a new subscript interpreter
637 */
638 struct Subscript *SbS_Create(void){
639 Subscript *p;
640
641 p = malloc( sizeof(*p) );
642 if( p ){
643 memset(p, 0, sizeof(*p));
644 }
645 return p;
646 }
647
648 /*
649 ** Evaluate the script given by the first nScript bytes of zScript[].
650 ** Return 0 on success and non-zero for an error.
@@ -691,43 +710,20 @@
691 int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1;
692 int lwr = 0;
693 rc = SBS_ERROR;
694 while( upr>=lwr ){
695 int i = (upr+lwr)/2;
696 int c = strncmp(zScript, aBuiltin[i].zCmd, n);
697 if( c==0 ){
698 rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg);
699 break;
700 }else if( c<0 ){
701 upr = i-1;
702 }else{
703 lwr = i+1;
704 }
705 }
706 if( upr<lwr ){
707 /* If it is not a built-in command, look for a built-in
708 ** variable */
709 upr = sizeof(aVars)/sizeof(aVars[0]) - 1;
710 lwr = 0;
711 while( upr>=lwr ){
712 int i = (upr+lwr)/2;
713 int c = strncmp(zScript, aVars[i].zVar, n);
714 if( c==0 ){
715 if( aVars[i].pI ){
716 SbS_PushInt(p, *aVars[i].pI);
717 }else{
718 SbS_Push(p, aVars[i].z, -1, 0);
719 }
720 rc = SBS_OK;
721 break;
722 }else if( c<0 ){
723 upr = i-1;
724 }else{
725 lwr = i+1;
726 }
727 }
728 }
729 }else if( pVal->flags & SBSVAL_VERB ){
730 rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg);
731 }else if( pVal->flags & SBSVAL_EXEC ){
732 rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size);
733 }else{
734
--- src/subscript.c
+++ src/subscript.c
@@ -80,10 +80,63 @@
80 #define SBSTT_STRING 4 /* ex: {...} */
81 #define SBSTT_INTEGER 5 /* Integer including option sign */
82 #define SBSTT_INCOMPLETE 6 /* Unterminated string token */
83 #define SBSTT_UNKNOWN 7 /* Unknown token */
84 #define SBSTT_EOF 8 /* End of input */
85
86 /*
87 ** Values are stored in the hash table as instances of the following
88 ** structure.
89 */
90 typedef struct SbSValue SbSValue;
91 struct SbSValue {
92 int flags; /* Bitmask of SBSVAL_* values */
93 union {
94 struct {
95 int size; /* Number of bytes in string, not counting final zero */
96 char *z; /* Pointer to string content */
97 } str; /* Value if SBSVAL_STR */
98 struct {
99 int (*xVerb)(Subscript*, void*); /* Function to do the work */
100 void *pArg; /* 2nd parameter to xVerb */
101 } verb; /* Value if SBSVAL_VERB */
102 } u;
103 };
104 #define SBSVAL_VERB 0x0001 /* Value stored in u.verb */
105 #define SBSVAL_STR 0x0002 /* Value stored in u.str */
106 #define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */
107 #define SBSVAL_EXEC 0x0008 /* u.str.z is a script */
108
109 /*
110 ** An entry in the hash table is an instance of this structure.
111 */
112 typedef struct SbsHashEntry SbsHashEntry;
113 struct SbsHashEntry {
114 SbsHashEntry *pNext; /* Next entry with the same hash on zKey */
115 SbSValue val; /* The payload */
116 int nKey; /* Length of the key */
117 char zKey[0]; /* The key */
118 };
119
120 /*
121 ** A hash table is an instance of the following structure.
122 */
123 typedef struct SbsHashTab SbsHashTab;
124 struct SbsHashTab {
125 SbsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */
126 };
127
128 /*
129 ** An instance of the Subscript interpreter
130 */
131 struct Subscript {
132 int nStack; /* Number of entries on stack */
133 SbsHashTab symTab; /* The symbol table */
134 char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */
135 SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
136 };
137
138
139 /*
140 ** Given an input string z of length n, identify the token that
141 ** starts at z[0]. Write the token type into *pTokenType and
142 ** return the length of the token.
@@ -149,33 +202,10 @@
202 *pTokenType = SBSTT_UNKNOWN;
203 return 1;
204 }
205
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207 /*
208 ** Release any memory allocated by a value.
209 */
210 static void sbs_value_reset(SbSValue *p){
211 if( p->flags & SBSVAL_DYN ){
@@ -184,30 +214,10 @@
214 p->u.str.z = "";
215 p->u.str.size = 0;
216 }
217 }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219 /*
220 ** Compute a hash on a string.
221 */
222 static int sbs_hash(const char *z, int n){
223 int h = 0;
@@ -292,20 +302,10 @@
302 }
303 }
304 memset(pHash, 0, sizeof(*pHash));
305 }
306
 
 
 
 
 
 
 
 
 
 
307 /*
308 ** Push a value onto the stack of an interpreter
309 */
310 static int sbs_push(Subscript *p, SbSValue *pVal){
311 if( p->nStack>=SBSCONFIG_NSTACK ){
@@ -313,10 +313,23 @@
313 return SBS_ERROR;
314 }
315 p->aStack[p->nStack++] = *pVal;
316 return SBS_OK;
317 }
318
319 /*
320 ** Create a new subscript interpreter. Return a pointer to the
321 ** new interpreter, or return NULL if malloc fails.
322 */
323 struct Subscript *SbS_Create(void){
324 Subscript *p;
325 p = malloc( sizeof(*p) );
326 if( p ){
327 memset(p, 0, sizeof(*p));
328 }
329 return p;
330 }
331
332 /*
333 ** Destroy an subscript interpreter
334 */
335 void SbS_Destroy(struct Subscript *p){
@@ -570,28 +583,49 @@
583 case SBSOP_ADD: c = a+b; break;
584 case SBSOP_SUB: c = a-b; break;
585 case SBSOP_MUL: c = a*b; break;
586 case SBSOP_DIV: c = b!=0 ? a/b : 0; break;
587 case SBSOP_AND: c = a && b; break;
588 case SBSOP_OR: c = a || b; break;
589 case SBSOP_MIN: c = a<b ? a : b; break;
590 case SBSOP_MAX: c = a<b ? b : a; break;
591 }
592 SbS_Pop(p, 2);
593 SbS_PushInt(p, c);
594 return 0;
595 }
596
597 /*
598 ** Subscript command: STRING hascap INTEGER
599 **
600 ** Return true if the user has all of the capabilities listed.
601 */
602 static int hascapCmd(struct Subscript *p, void *pNotUsed){
603 const char *z;
604 int i, n, a;
605 if( SbS_RequireStack(p, 1, "hascap") ) return 1;
606 z = SbS_StackValue(p, 0, &n);
607 a = login_has_capability(z, n);
608 SbS_Pop(p, 1);
609 SbS_PushInt(p, a);
610 }
611
612 /*
613 ** Subscript command: STRING puts
614 */
615 static int putsCmd(struct Subscript *p, void *pNotUsed){
616 int size;
617 const char *z;
618 if( SbS_RequireStack(p, 1, "puts") ) return 1;
619 z = SbS_StackValue(p, 0, &size);
620 if( g.cgiPanic ){
621 char *zCopy = mprintf("%.*s", size, z);
622 cgi_printf("%h", zCopy);
623 free(zCopy);
624 }else{
625 printf("%.*s\n", size, z);
626 }
627 SbS_Pop(p, 1);
628 return 0;
629 }
630
631
@@ -598,53 +632,38 @@
632 /*
633 ** A table of built-in commands
634 */
635 static const struct {
636 const char *zCmd;
 
637 int (*xCmd)(Subscript*,void*);
638 void *pArg;
639 } aBuiltin[] = {
640 { "add", bopCmd, (void*)SBSOP_AND },
641 { "and", bopCmd, (void*)SBSOP_AND },
642 { "div", bopCmd, (void*)SBSOP_DIV },
643 { "hascap", hascapCmd, 0 },
644 { "max", bopCmd, (void*)SBSOP_MAX },
645 { "min", bopCmd, (void*)SBSOP_MIN },
646 { "mul", bopCmd, (void*)SBSOP_MUL },
647 { "not", notCmd, 0 },
648 { "or", bopCmd, (void*)SBSOP_OR },
649 { "puts", putsCmd, 0 },
650 { "set", setCmd, 0 },
651 { "sub", bopCmd, (void*)SBSOP_SUB },
652 };
653
654
655 /*
656 ** Compare a zero-terminated string zPattern against
657 ** an unterminated string zStr of length nStr.
658 */
659 static int compare_cmd(const char *zPattern, const char *zStr, int nStr){
660 int c = strncmp(zPattern, zStr, nStr);
661 if( c==0 && zPattern[nStr]!=0 ){
662 c = -1;
663 }
664 return c;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
665 }
666
667 /*
668 ** Evaluate the script given by the first nScript bytes of zScript[].
669 ** Return 0 on success and non-zero for an error.
@@ -691,43 +710,20 @@
710 int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1;
711 int lwr = 0;
712 rc = SBS_ERROR;
713 while( upr>=lwr ){
714 int i = (upr+lwr)/2;
715 int c = compare_cmd(aBuiltin[i].zCmd, zScript, n);
716 if( c==0 ){
717 rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg);
718 break;
719 }else if( c<0 ){
720 upr = i-1;
721 }else{
722 lwr = i+1;
723 }
724 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
725 }else if( pVal->flags & SBSVAL_VERB ){
726 rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg);
727 }else if( pVal->flags & SBSVAL_EXEC ){
728 rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size);
729 }else{
730

Keyboard Shortcuts

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