Merge commit 'release-4-0-0alpha1' into v4-0-test
[kai/samba.git] / source / scripting / ejs / smbcalls_string.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    provide access to string functions
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Jelmer Vernooij 2005 (substr)
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "scripting/ejs/smbcalls.h"
25 #include "lib/appweb/ejs/ejs.h"
26
27 /*
28   usage:
29       var len = strlen(str);
30 */
31 static int ejs_strlen(MprVarHandle eid, int argc, char **argv)
32 {
33         if (argc != 1) {
34                 ejsSetErrorMsg(eid, "strlen invalid arguments");
35                 return -1;
36         }
37         mpr_Return(eid, mprCreateIntegerVar(strlen_m(argv[0])));
38         return 0;
39 }
40
41 /*
42   usage:
43       var s = strlower("UPPER");
44 */
45 static int ejs_strlower(MprVarHandle eid, int argc, char **argv)
46 {
47         char *s;
48         if (argc != 1) {
49                 ejsSetErrorMsg(eid, "strlower invalid arguments");
50                 return -1;
51         }
52         s = strlower_talloc(mprMemCtx(), argv[0]);
53         mpr_Return(eid, mprString(s));
54         talloc_free(s);
55         return 0;
56 }
57
58 /*
59   usage:
60       var s = strupper("lower");
61 */
62 static int ejs_strupper(MprVarHandle eid, int argc, char **argv)
63 {
64         char *s;
65         if (argc != 1) {
66                 ejsSetErrorMsg(eid, "strupper invalid arguments");
67                 return -1;
68         }
69         s = strupper_talloc(mprMemCtx(), argv[0]);
70         mpr_Return(eid, mprString(s));
71         talloc_free(s);
72         return 0;
73 }
74
75 /*
76   usage:
77       var s = strstr(string, substring);
78 */
79 static int ejs_strstr(MprVarHandle eid, int argc, char **argv)
80 {
81         char *s;
82         if (argc != 2) {
83                 ejsSetErrorMsg(eid, "strstr invalid arguments");
84                 return -1;
85         }
86         s = strstr(argv[0], argv[1]);
87         mpr_Return(eid, mprString(s));
88         return 0;
89 }
90
91 /*
92   usage:
93       var s = strspn(string, legal_chars_string);
94 */
95 static int ejs_strspn(MprVarHandle eid, int argc, char **argv)
96 {
97         int len;
98         if (argc != 2) {
99                 ejsSetErrorMsg(eid, "strspn invalid arguments");
100                 return -1;
101         }
102         len = strspn(argv[0], argv[1]);
103         mpr_Return(eid, mprCreateIntegerVar(len));
104         return 0;
105 }
106
107 /*
108   usage:
109      list = split(".", "a.foo.bar");
110      list = split(".", "a.foo.bar", count);
111
112   count is an optional count of how many splits to make
113
114   NOTE: does not take a regular expression, unlike perl split()
115 */
116 static int ejs_split(MprVarHandle eid, int argc, struct MprVar **argv)
117 {
118         const char *separator, *s;
119         char *p;
120         struct MprVar ret;
121         int count = 0, maxcount=0;
122         TALLOC_CTX *tmp_ctx = talloc_new(mprMemCtx());
123         if (argc < 2 ||
124             argv[0]->type != MPR_TYPE_STRING ||
125             argv[1]->type != MPR_TYPE_STRING) {
126                 ejsSetErrorMsg(eid, "split invalid arguments");
127                 return -1;
128         }
129         separator = mprToString(argv[0]);
130         s         = mprToString(argv[1]);
131         if (argc == 3) {
132                 maxcount = mprToInt(argv[2]);
133         }
134
135         ret = mprArray("list");
136
137         while ((p = strstr(s, separator))) {
138                 char *s2 = talloc_strndup(tmp_ctx, s, (int)(p-s));
139                 mprAddArray(&ret, count++, mprString(s2));
140                 talloc_free(s2);
141                 s = p + strlen(separator);
142                 if (maxcount != 0 && count >= maxcount) {
143                         break;
144                 }
145         }
146         if (*s) {
147                 mprAddArray(&ret, count++, mprString(s));
148         }
149         talloc_free(tmp_ctx);
150         mpr_Return(eid, ret);
151         return 0;
152 }
153
154 /*
155   usage:
156     str = substr(orig[, start_offset[, length]]);
157
158         special cases:
159                 if start_offset < 0 then start_offset+=strlen(orig)
160                 if length < 0 then length+=strlen(orig)-start_offset
161
162         (as found in many other languages)
163 */
164 static int ejs_substr(MprVarHandle eid, int argc, struct MprVar **argv)
165 {
166         int start_offset = 0;
167         int length = 0;
168         const char *orig;
169         char *target;
170         
171         if (argc < 1 || argc > 3 ||
172             argv[0]->type != MPR_TYPE_STRING) {
173                 ejsSetErrorMsg(eid, "substr invalid arguments");
174                 return -1;
175         }
176
177         if (argc == 1) {
178                 mpr_Return(eid, *argv[0]);
179                 return 0;
180         }
181
182         orig = mprToString(argv[0]);
183         start_offset = mprToInt(argv[1]);
184         length = strlen(orig);
185         if (start_offset < 0) start_offset += strlen(orig);
186         if (start_offset < 0 || start_offset > strlen(orig)) {
187                 ejsSetErrorMsg(eid, "substr arg 2 out of bounds ([%s], %d)", orig, start_offset);
188                 return -1;
189         }
190
191         if (argc == 3) {
192                 length = mprToInt(argv[2]);
193                 if (length < 0) length += strlen(orig) - start_offset;
194                 if (length < 0 || length+start_offset > strlen(orig)) {
195                         ejsSetErrorMsg(eid, "substr arg 3 out of bounds ([%s], %d, %d)", orig, start_offset, length);
196                         return -1;
197                 }
198         }
199
200         target = talloc_strndup(mprMemCtx(), orig+start_offset, length);
201         
202         mpr_Return(eid, mprString(target));
203
204         talloc_free(target);
205
206         return 0;
207 }
208
209 /*
210   usage:
211      str = join("DC=", list);
212 */
213 static int ejs_join(MprVarHandle eid, int argc, struct MprVar **argv)
214 {
215         int i;
216         const char *separator;
217         char *ret = NULL;
218         const char **list;
219         TALLOC_CTX *tmp_ctx = talloc_new(mprMemCtx());
220         if (argc != 2 ||
221             argv[0]->type != MPR_TYPE_STRING ||
222             argv[1]->type != MPR_TYPE_OBJECT) {
223                 ejsSetErrorMsg(eid, "join invalid arguments");
224                 return -1;
225         }
226
227         separator = mprToString(argv[0]);
228         list      = mprToArray(tmp_ctx, argv[1]);
229
230         if (list == NULL || list[0] == NULL) {
231                 talloc_free(tmp_ctx);
232                 mpr_Return(eid, mprString(NULL));
233                 return 0;
234         }
235         
236         ret = talloc_strdup(tmp_ctx, list[0]);
237         if (ret == NULL) {
238                 goto failed;
239         }
240         for (i=1;list[i];i++) {
241                 ret = talloc_asprintf_append_buffer(ret, "%s%s", separator, list[i]);
242                 if (ret == NULL) {
243                         goto failed;
244                 }
245         }
246         mpr_Return(eid, mprString(ret));
247         talloc_free(tmp_ctx);
248         return 0;
249 failed:
250         ejsSetErrorMsg(eid, "out of memory");
251         return -1;
252 }
253
254
255 /*
256   blergh, C certainly makes this hard!
257   usage:
258      str = sprintf("i=%d s=%7s", 7, "foo");
259 */
260 typedef char *(*_asprintf_append_t)(char *, const char *, ...);
261 static int ejs_sprintf(MprVarHandle eid, int argc, struct MprVar **argv)
262 {
263         const char *format;
264         const char *p;
265         char *ret;
266         int a = 1;
267         _asprintf_append_t _asprintf_append;
268         TALLOC_CTX *tmp_ctx;
269         if (argc < 1 || argv[0]->type != MPR_TYPE_STRING) {
270                 ejsSetErrorMsg(eid, "sprintf invalid arguments");
271                 return -1;
272         }
273         format = mprToString(argv[0]);
274         tmp_ctx = talloc_new(mprMemCtx());
275         ret = talloc_strdup(tmp_ctx, "");
276
277         /* avoid all the format string warnings */
278         _asprintf_append = (_asprintf_append_t)talloc_asprintf_append_buffer;
279
280         /*
281           hackity hack ...
282         */
283         while ((p = strchr(format, '%'))) {
284                 char *fmt2;
285                 int len, len_count=0;
286                 char *tstr;
287                 ret = talloc_asprintf_append_buffer(ret, "%*.*s", 
288                                              (int)(p-format), (int)(p-format), 
289                                              format);
290                 if (ret == NULL) goto failed;
291                 format += (int)(p-format);
292                 len = strcspn(p+1, "dxuiofgGpXeEFcs%") + 1;
293                 fmt2 = talloc_strndup(tmp_ctx, p, len+1);
294                 if (fmt2 == NULL) goto failed;
295                 len_count = count_chars(fmt2, '*');
296                 /* find the type string */
297                 tstr = &fmt2[len];
298                 while (tstr > fmt2 && isalpha((unsigned char)tstr[-1])) {
299                         tstr--;
300                 }
301                 if (strcmp(tstr, "%") == 0) {
302                         ret = talloc_asprintf_append_buffer(ret, "%%");
303                         if (ret == NULL) {
304                                 goto failed;
305                         }
306                         format += len+1;
307                         continue;
308                 }
309                 if (len_count > 2 || 
310                     argc < a + len_count + 1) {
311                         ejsSetErrorMsg(eid, "sprintf: not enough arguments for format");
312                         goto failed;
313                 }
314 #define FMT_ARG(fn, type) do { \
315                         switch (len_count) { \
316                         case 0: \
317                                 ret = _asprintf_append(ret, fmt2, \
318                                                              (type)fn(argv[a])); \
319                                 break; \
320                         case 1: \
321                                 ret = _asprintf_append(ret, fmt2, \
322                                                              (int)mprVarToNumber(argv[a]), \
323                                                              (type)fn(argv[a+1])); \
324                                 break; \
325                         case 2: \
326                                 ret = _asprintf_append(ret, fmt2, \
327                                                              (int)mprVarToNumber(argv[a]), \
328                                                              (int)mprVarToNumber(argv[a+1]), \
329                                                              (type)fn(argv[a+2])); \
330                                 break; \
331                         } \
332                         a += len_count + 1; \
333                         if (ret == NULL) { \
334                                 goto failed; \
335                         } \
336 } while (0)
337
338                 if (strcmp(tstr, "s")==0)        FMT_ARG(mprToString,    const char *);
339                 else if (strcmp(tstr, "c")==0)   FMT_ARG(*mprToString,   char);
340                 else if (strcmp(tstr, "d")==0)   FMT_ARG(mprVarToNumber, int);
341                 else if (strcmp(tstr, "ld")==0)  FMT_ARG(mprVarToNumber, long);
342                 else if (strcmp(tstr, "lld")==0) FMT_ARG(mprVarToNumber, long long);
343                 else if (strcmp(tstr, "x")==0)   FMT_ARG(mprVarToNumber, int);
344                 else if (strcmp(tstr, "lx")==0)  FMT_ARG(mprVarToNumber, long);
345                 else if (strcmp(tstr, "llx")==0) FMT_ARG(mprVarToNumber, long long);
346                 else if (strcmp(tstr, "X")==0)   FMT_ARG(mprVarToNumber, int);
347                 else if (strcmp(tstr, "lX")==0)  FMT_ARG(mprVarToNumber, long);
348                 else if (strcmp(tstr, "llX")==0) FMT_ARG(mprVarToNumber, long long);
349                 else if (strcmp(tstr, "u")==0)   FMT_ARG(mprVarToNumber, int);
350                 else if (strcmp(tstr, "lu")==0)  FMT_ARG(mprVarToNumber, long);
351                 else if (strcmp(tstr, "llu")==0) FMT_ARG(mprVarToNumber, long long);
352                 else if (strcmp(tstr, "i")==0)   FMT_ARG(mprVarToNumber, int);
353                 else if (strcmp(tstr, "li")==0)  FMT_ARG(mprVarToNumber, long);
354                 else if (strcmp(tstr, "lli")==0) FMT_ARG(mprVarToNumber, long long);
355                 else if (strcmp(tstr, "o")==0)   FMT_ARG(mprVarToNumber, int);
356                 else if (strcmp(tstr, "lo")==0)  FMT_ARG(mprVarToNumber, long);
357                 else if (strcmp(tstr, "llo")==0) FMT_ARG(mprVarToNumber, long long);
358                 else if (strcmp(tstr, "f")==0)   FMT_ARG(mprVarToFloat,  double);
359                 else if (strcmp(tstr, "lf")==0)  FMT_ARG(mprVarToFloat,  double);
360                 else if (strcmp(tstr, "g")==0)   FMT_ARG(mprVarToFloat,  double);
361                 else if (strcmp(tstr, "lg")==0)  FMT_ARG(mprVarToFloat,  double);
362                 else if (strcmp(tstr, "e")==0)   FMT_ARG(mprVarToFloat,  double);
363                 else if (strcmp(tstr, "le")==0)  FMT_ARG(mprVarToFloat,  double);
364                 else if (strcmp(tstr, "E")==0)   FMT_ARG(mprVarToFloat,  double);
365                 else if (strcmp(tstr, "lE")==0)  FMT_ARG(mprVarToFloat,  double);
366                 else if (strcmp(tstr, "F")==0)   FMT_ARG(mprVarToFloat,  double);
367                 else if (strcmp(tstr, "lF")==0)  FMT_ARG(mprVarToFloat,  double);
368                 else {
369                         ejsSetErrorMsg(eid, "sprintf: unknown format string '%s'", fmt2);
370                         goto failed;
371                 }
372                 format += len+1;
373         }
374
375         ret = talloc_asprintf_append_buffer(ret, "%s", format);
376         mpr_Return(eid, mprString(ret));
377         talloc_free(tmp_ctx);
378         return 0;          
379         
380 failed:
381         talloc_free(tmp_ctx);
382         return -1;
383 }
384
385 /*
386   used to build your own print function
387      str = vsprintf(args);
388 */
389 static int ejs_vsprintf(MprVarHandle eid, int argc, struct MprVar **argv)
390 {
391         struct MprVar **args, *len, *v;
392         int i, ret, length;
393         if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
394                 ejsSetErrorMsg(eid, "vsprintf invalid arguments");
395                 return -1;
396         }
397         v = argv[0];
398         len = mprGetProperty(v, "length", NULL);
399         if (len == NULL) {
400                 ejsSetErrorMsg(eid, "vsprintf takes an array");
401                 return -1;
402         }
403         length = mprToInt(len);
404         args = talloc_array(mprMemCtx(), struct MprVar *, length);
405         if (args == NULL) {
406                 return -1;
407         }
408
409         for (i=0;i<length;i++) {
410                 char idx[16];
411                 mprItoa(i, idx, sizeof(idx));
412                 args[i] = mprGetProperty(v, idx, NULL);
413         }
414         
415         ret = ejs_sprintf(eid, length, args);
416         talloc_free(args);
417         return ret;
418 }
419
420
421 /*
422   encode a string, replacing all non-alpha with %02x form
423 */
424 static int ejs_encodeURIComponent(MprVarHandle eid, int argc, char **argv)
425 {
426         int i, j, count=0;
427         const char *s;
428         char *ret;
429         if (argc != 1) {
430                 ejsSetErrorMsg(eid, "encodeURIComponent invalid arguments");
431                 return -1;
432         }
433         
434         s = argv[0];
435
436         for (i=0;s[i];i++) {
437                 if (!isalnum(s[i])) count++;
438         }
439         
440         ret = talloc_array(mprMemCtx(), char, i + count*2 + 1);
441         if (ret == NULL) {
442                 return -1;
443         }
444         for (i=j=0;s[i];i++,j++) {
445                 if (!isalnum(s[i])) {
446                         snprintf(ret+j, 4, "%%%02X", (unsigned)s[i]);
447                         j += 2;
448                 } else {
449                         ret[j] = s[i];
450                 }
451         }
452         ret[j] = 0;
453         mpr_Return(eid, mprString(ret));
454         talloc_free(ret);
455         return 0;
456 }
457
458 /*
459   encode a string, replacing all non-alpha of %02x form
460 */
461 static int ejs_decodeURIComponent(MprVarHandle eid, int argc, char **argv)
462 {
463         int i, j, count=0;
464         const char *s;
465         char *ret;
466         if (argc != 1) {
467                 ejsSetErrorMsg(eid, "decodeURIComponent invalid arguments");
468                 return -1;
469         }
470         
471         s = argv[0];
472
473         ret = talloc_array(mprMemCtx(), char, strlen(s) + 1);
474         if (ret == NULL) {
475                 return -1;
476         }
477
478         for (i=j=0;s[i];i++,j++) {
479                 if (s[i] == '%') {
480                         unsigned c;
481                         if (sscanf(s+i+1, "%02X", &c) != 1) {
482                                 ejsSetErrorMsg(eid, "decodeURIComponent bad format");
483                                 return -1;
484                         }
485                         ret[j] = c;
486                         i += 2;
487                 } else {
488                         ret[j] = s[i];
489                 }
490                 if (!isalnum(s[i])) count++;
491         }
492         
493         ret[j] = 0;
494         mpr_Return(eid, mprString(ret));
495         talloc_free(ret);
496         return 0;
497 }
498
499 /*
500   initialise string ejs subsystem
501 */
502 static int ejs_string_init(MprVarHandle eid, int argc, struct MprVar **argv)
503 {
504         struct MprVar *obj = mprInitObject(eid, "string", argc, argv);
505
506         mprSetCFunction(obj, "substr", ejs_substr);
507         mprSetStringCFunction(obj, "strlen", ejs_strlen);
508         mprSetStringCFunction(obj, "strlower", ejs_strlower);
509         mprSetStringCFunction(obj, "strupper", ejs_strupper);
510         mprSetStringCFunction(obj, "strstr", ejs_strstr);
511         mprSetStringCFunction(obj, "strspn", ejs_strspn);
512         mprSetCFunction(obj, "split", ejs_split);
513         mprSetCFunction(obj, "join", ejs_join);
514         mprSetCFunction(obj, "sprintf", ejs_sprintf);
515         mprSetCFunction(obj, "vsprintf", ejs_vsprintf);
516         mprSetStringCFunction(obj, "encodeURIComponent", ejs_encodeURIComponent);
517         mprSetStringCFunction(obj, "decodeURIComponent", ejs_decodeURIComponent);
518
519         return 0;
520 }
521
522 /*
523   setup C functions that be called from ejs
524 */
525 NTSTATUS smb_setup_ejs_string(void)
526 {
527         ejsDefineCFunction(-1, "string_init", ejs_string_init, NULL, MPR_VAR_SCRIPT_HANDLE);
528         return NT_STATUS_OK;
529 }