Merged revisions 163253 via svnmerge from
[asterisk/asterisk.git] / funcs / func_cut.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (c) 2003-2006 Tilghman Lesher.  All rights reserved.
5  *
6  * Tilghman Lesher <app_cut__v003@the-tilghman.com>
7  *
8  * This code is released by the author with no restrictions on usage.
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  */
17
18 /*! \file
19  * 
20  * \brief CUT function
21  *
22  * \author Tilghman Lesher <app_cut__v003@the-tilghman.com>
23  *
24  * \ingroup functions
25  */
26
27 #include "asterisk.h"
28
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30
31 #include "asterisk/file.h"
32 #include "asterisk/channel.h"
33 #include "asterisk/pbx.h"
34 #include "asterisk/module.h"
35 #include "asterisk/app.h"
36
37 /*** DOCUMENTATION
38         <function name="SORT" language="en_US">
39                 <synopsis>
40                         Sorts a list of key/vals into a list of keys, based upon the vals.      
41                 </synopsis>
42                 <syntax>
43                         <parameter name="keyval" required="true" argsep=":">
44                                 <argument name="key1" required="true" />
45                                 <argument name="val1" required="true" />
46                         </parameter>
47                         <parameter name="keyvaln" multiple="true" argsep=":">
48                                 <argument name="key2" required="true" />
49                                 <argument name="val2" required="true" />
50                         </parameter>
51                 </syntax>
52                 <description>
53                         <para>Takes a comma-separated list of keys and values, each separated by a colon, and returns a
54                         comma-separated list of the keys, sorted by their values.  Values will be evaluated as
55                         floating-point numbers.</para>
56                 </description>
57         </function>
58         <function name="CUT" language="en_US">
59                 <synopsis>
60                         Slices and dices strings, based upon a named delimiter.         
61                 </synopsis>
62                 <syntax>
63                         <parameter name="varname" required="true">
64                                 <para>Variable you want cut</para>
65                         </parameter>
66                         <parameter name="char-delim" required="true">
67                                 <para>Delimiter, defaults to <literal>-</literal></para>
68                         </parameter>
69                         <parameter name="range-spec" required="true">
70                                 <para>Number of the field you want (1-based offset), may also be specified as a range (with <literal>-</literal>)
71                                 or group of ranges and fields (with <literal>&amp;</literal>)</para>
72                         </parameter>
73                 </syntax>
74                 <description>
75                         <para>Cut out information from a string (<replaceable>varname</replaceable>), based upon a named delimiter.</para>
76                 </description>  
77         </function>
78  ***/
79
80 /* Maximum length of any variable */
81 #define MAXRESULT       1024
82
83 struct sortable_keys {
84         char *key;
85         float value;
86 };
87
88 static int sort_subroutine(const void *arg1, const void *arg2)
89 {
90         const struct sortable_keys *one=arg1, *two=arg2;
91         if (one->value < two->value)
92                 return -1;
93         else if (one->value == two->value)
94                 return 0;
95         else
96                 return 1;
97 }
98
99 #define ERROR_NOARG     (-1)
100 #define ERROR_NOMEM     (-2)
101 #define ERROR_USAGE     (-3)
102
103 static int sort_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
104 {
105         char *strings, *ptrkey, *ptrvalue;
106         int count=1, count2, element_count=0;
107         struct sortable_keys *sortable_keys;
108
109         *buffer = '\0';
110
111         if (!data)
112                 return ERROR_NOARG;
113
114         strings = ast_strdupa(data);
115
116         for (ptrkey = strings; *ptrkey; ptrkey++) {
117                 if (*ptrkey == ',')
118                         count++;
119         }
120
121         sortable_keys = alloca(count * sizeof(struct sortable_keys));
122
123         memset(sortable_keys, 0, count * sizeof(struct sortable_keys));
124
125         /* Parse each into a struct */
126         count2 = 0;
127         while ((ptrkey = strsep(&strings, ","))) {
128                 ptrvalue = strchr(ptrkey, ':');
129                 if (!ptrvalue) {
130                         count--;
131                         continue;
132                 }
133                 *ptrvalue++ = '\0';
134                 sortable_keys[count2].key = ptrkey;
135                 sscanf(ptrvalue, "%f", &sortable_keys[count2].value);
136                 count2++;
137         }
138
139         /* Sort the structs */
140         qsort(sortable_keys, count, sizeof(struct sortable_keys), sort_subroutine);
141
142         for (count2 = 0; count2 < count; count2++) {
143                 int blen = strlen(buffer);
144                 if (element_count++) {
145                         strncat(buffer + blen, ",", buflen - blen - 1);
146                         blen++;
147                 }
148                 strncat(buffer + blen, sortable_keys[count2].key, buflen - blen - 1);
149         }
150
151         return 0;
152 }
153
154 static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
155 {
156         char *parse;
157         size_t delim_consumed;
158         AST_DECLARE_APP_ARGS(args,
159                 AST_APP_ARG(varname);
160                 AST_APP_ARG(delimiter);
161                 AST_APP_ARG(field);
162         );
163
164         *buffer = '\0';
165
166         parse = ast_strdupa(data);
167
168         AST_STANDARD_APP_ARGS(args, parse);
169
170         /* Check and parse arguments */
171         if (args.argc < 3) {
172                 return ERROR_NOARG;
173         } else {
174                 char d, ds[2] = "";
175                 char *tmp = alloca(strlen(args.varname) + 4);
176                 char varvalue[MAXRESULT], *tmp2=varvalue;
177
178                 if (tmp) {
179                         snprintf(tmp, strlen(args.varname) + 4, "${%s}", args.varname);
180                 } else {
181                         return ERROR_NOMEM;
182                 }
183
184                 if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed))
185                         ast_copy_string(ds, "-", sizeof(ds));
186
187                 /* String form of the delimiter, for use with strsep(3) */
188                 d = *ds;
189
190                 pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
191
192                 if (tmp2) {
193                         int curfieldnum = 1;
194                         while (tmp2 != NULL && args.field != NULL) {
195                                 char *nextgroup = strsep(&(args.field), "&");
196                                 int num1 = 0, num2 = MAXRESULT;
197                                 char trashchar;
198
199                                 if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
200                                         /* range with both start and end */
201                                 } else if (sscanf(nextgroup, "-%d", &num2) == 1) {
202                                         /* range with end */
203                                         num1 = 0;
204                                 } else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
205                                         /* range with start */
206                                         num2 = MAXRESULT;
207                                 } else if (sscanf(nextgroup, "%d", &num1) == 1) {
208                                         /* single number */
209                                         num2 = num1;
210                                 } else {
211                                         return ERROR_USAGE;
212                                 }
213
214                                 /* Get to start, if any */
215                                 if (num1 > 0) {
216                                         while (tmp2 != (char *)NULL + 1 && curfieldnum < num1) {
217                                                 tmp2 = strchr(tmp2, d) + 1;
218                                                 curfieldnum++;
219                                         }
220                                 }
221
222                                 /* Most frequent problem is the expectation of reordering fields */
223                                 if ((num1 > 0) && (curfieldnum > num1))
224                                         ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
225
226                                 /* Re-null tmp2 if we added 1 to NULL */
227                                 if (tmp2 == (char *)NULL + 1)
228                                         tmp2 = NULL;
229
230                                 /* Output fields until we either run out of fields or num2 is reached */
231                                 while (tmp2 != NULL && curfieldnum <= num2) {
232                                         char *tmp3 = strsep(&tmp2, ds);
233                                         int curlen = strlen(buffer);
234
235                                         if (curlen)
236                                                 snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3);
237                                         else
238                                                 snprintf(buffer, buflen, "%s", tmp3);
239
240                                         curfieldnum++;
241                                 }
242                         }
243                 }
244         }
245         return 0;
246 }
247
248 static int acf_sort_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
249 {
250         int ret = -1;
251
252         switch (sort_internal(chan, data, buf, len)) {
253         case ERROR_NOARG:
254                 ast_log(LOG_ERROR, "SORT() requires an argument\n");
255                 break;
256         case ERROR_NOMEM:
257                 ast_log(LOG_ERROR, "Out of memory\n");
258                 break;
259         case 0:
260                 ret = 0;
261                 break;
262         default:
263                 ast_log(LOG_ERROR, "Unknown internal error\n");
264         }
265
266         return ret;
267 }
268
269 static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
270 {
271         int ret = -1;
272
273         switch (cut_internal(chan, data, buf, len)) {
274         case ERROR_NOARG:
275                 ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
276                 break;
277         case ERROR_NOMEM:
278                 ast_log(LOG_ERROR, "Out of memory\n");
279                 break;
280         case ERROR_USAGE:
281                 ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
282                 break;
283         case 0:
284                 ret = 0;
285                 break;
286         default:
287                 ast_log(LOG_ERROR, "Unknown internal error\n");
288         }
289
290         return ret;
291 }
292
293 struct ast_custom_function acf_sort = {
294         .name = "SORT",
295         .read = acf_sort_exec,
296 };
297
298 struct ast_custom_function acf_cut = {
299         .name = "CUT",
300         .read = acf_cut_exec,
301 };
302
303 static int unload_module(void)
304 {
305         int res = 0;
306
307         res |= ast_custom_function_unregister(&acf_cut);
308         res |= ast_custom_function_unregister(&acf_sort);
309
310         return res;
311 }
312
313 static int load_module(void)
314 {
315         int res = 0;
316
317         res |= ast_custom_function_register(&acf_cut);
318         res |= ast_custom_function_register(&acf_sort);
319
320         return res;
321 }
322
323 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Cut out information from a string");