Thu Apr 28 2011 16:56:47

Asterisk developer's documentation


func_cut.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (c) 2003-2006 Tilghman Lesher.  All rights reserved.
00005  *
00006  * Tilghman Lesher <app_cut__v003@the-tilghman.com>
00007  *
00008  * This code is released by the author with no restrictions on usage.
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  */
00017 
00018 /*! \file
00019  * 
00020  * \brief CUT function
00021  *
00022  * \author Tilghman Lesher <app_cut__v003@the-tilghman.com>
00023  *
00024  * \ingroup functions
00025  */
00026 
00027 #include "asterisk.h"
00028 
00029 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211580 $")
00030 
00031 #include "asterisk/file.h"
00032 #include "asterisk/channel.h"
00033 #include "asterisk/pbx.h"
00034 #include "asterisk/module.h"
00035 #include "asterisk/app.h"
00036 
00037 /*** DOCUMENTATION
00038    <function name="SORT" language="en_US">
00039       <synopsis>
00040          Sorts a list of key/vals into a list of keys, based upon the vals.   
00041       </synopsis>
00042       <syntax>
00043          <parameter name="keyval" required="true" argsep=":">
00044             <argument name="key1" required="true" />
00045             <argument name="val1" required="true" />
00046          </parameter>
00047          <parameter name="keyvaln" multiple="true" argsep=":">
00048             <argument name="key2" required="true" />
00049             <argument name="val2" required="true" />
00050          </parameter>
00051       </syntax>
00052       <description>
00053          <para>Takes a comma-separated list of keys and values, each separated by a colon, and returns a
00054          comma-separated list of the keys, sorted by their values.  Values will be evaluated as
00055          floating-point numbers.</para>
00056       </description>
00057    </function>
00058    <function name="CUT" language="en_US">
00059       <synopsis>
00060          Slices and dices strings, based upon a named delimiter.     
00061       </synopsis>
00062       <syntax>
00063          <parameter name="varname" required="true">
00064             <para>Variable you want cut</para>
00065          </parameter>
00066          <parameter name="char-delim" required="true">
00067             <para>Delimiter, defaults to <literal>-</literal></para>
00068          </parameter>
00069          <parameter name="range-spec" required="true">
00070             <para>Number of the field you want (1-based offset), may also be specified as a range (with <literal>-</literal>)
00071             or group of ranges and fields (with <literal>&amp;</literal>)</para>
00072          </parameter>
00073       </syntax>
00074       <description>
00075          <para>Cut out information from a string (<replaceable>varname</replaceable>), based upon a named delimiter.</para>
00076       </description> 
00077    </function>
00078  ***/
00079 
00080 /* Maximum length of any variable */
00081 #define MAXRESULT 1024
00082 
00083 struct sortable_keys {
00084    char *key;
00085    float value;
00086 };
00087 
00088 static int sort_subroutine(const void *arg1, const void *arg2)
00089 {
00090    const struct sortable_keys *one=arg1, *two=arg2;
00091    if (one->value < two->value)
00092       return -1;
00093    else if (one->value == two->value)
00094       return 0;
00095    else
00096       return 1;
00097 }
00098 
00099 #define ERROR_NOARG  (-1)
00100 #define ERROR_NOMEM  (-2)
00101 #define ERROR_USAGE  (-3)
00102 
00103 static int sort_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
00104 {
00105    char *strings, *ptrkey, *ptrvalue;
00106    int count=1, count2, element_count=0;
00107    struct sortable_keys *sortable_keys;
00108 
00109    *buffer = '\0';
00110 
00111    if (!data)
00112       return ERROR_NOARG;
00113 
00114    strings = ast_strdupa(data);
00115 
00116    for (ptrkey = strings; *ptrkey; ptrkey++) {
00117       if (*ptrkey == ',')
00118          count++;
00119    }
00120 
00121    sortable_keys = alloca(count * sizeof(struct sortable_keys));
00122 
00123    memset(sortable_keys, 0, count * sizeof(struct sortable_keys));
00124 
00125    /* Parse each into a struct */
00126    count2 = 0;
00127    while ((ptrkey = strsep(&strings, ","))) {
00128       ptrvalue = strchr(ptrkey, ':');
00129       if (!ptrvalue) {
00130          count--;
00131          continue;
00132       }
00133       *ptrvalue++ = '\0';
00134       sortable_keys[count2].key = ptrkey;
00135       sscanf(ptrvalue, "%30f", &sortable_keys[count2].value);
00136       count2++;
00137    }
00138 
00139    /* Sort the structs */
00140    qsort(sortable_keys, count, sizeof(struct sortable_keys), sort_subroutine);
00141 
00142    for (count2 = 0; count2 < count; count2++) {
00143       int blen = strlen(buffer);
00144       if (element_count++) {
00145          strncat(buffer + blen, ",", buflen - blen - 1);
00146          blen++;
00147       }
00148       strncat(buffer + blen, sortable_keys[count2].key, buflen - blen - 1);
00149    }
00150 
00151    return 0;
00152 }
00153 
00154 static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
00155 {
00156    char *parse;
00157    size_t delim_consumed;
00158    AST_DECLARE_APP_ARGS(args,
00159       AST_APP_ARG(varname);
00160       AST_APP_ARG(delimiter);
00161       AST_APP_ARG(field);
00162    );
00163 
00164    *buffer = '\0';
00165 
00166    parse = ast_strdupa(data);
00167 
00168    AST_STANDARD_APP_ARGS(args, parse);
00169 
00170    /* Check and parse arguments */
00171    if (args.argc < 3) {
00172       return ERROR_NOARG;
00173    } else {
00174       char d, ds[2] = "";
00175       char *tmp = alloca(strlen(args.varname) + 4);
00176       char varvalue[MAXRESULT], *tmp2=varvalue;
00177 
00178       if (tmp) {
00179          snprintf(tmp, strlen(args.varname) + 4, "${%s}", args.varname);
00180       } else {
00181          return ERROR_NOMEM;
00182       }
00183 
00184       if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed))
00185          ast_copy_string(ds, "-", sizeof(ds));
00186 
00187       /* String form of the delimiter, for use with strsep(3) */
00188       d = *ds;
00189 
00190       pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
00191 
00192       if (tmp2) {
00193          int curfieldnum = 1, firstfield = 1;
00194          while (tmp2 != NULL && args.field != NULL) {
00195             char *nextgroup = strsep(&(args.field), "&");
00196             int num1 = 0, num2 = MAXRESULT;
00197             char trashchar;
00198 
00199             if (sscanf(nextgroup, "%30d-%30d", &num1, &num2) == 2) {
00200                /* range with both start and end */
00201             } else if (sscanf(nextgroup, "-%30d", &num2) == 1) {
00202                /* range with end */
00203                num1 = 0;
00204             } else if ((sscanf(nextgroup, "%30d%1c", &num1, &trashchar) == 2) && (trashchar == '-')) {
00205                /* range with start */
00206                num2 = MAXRESULT;
00207             } else if (sscanf(nextgroup, "%30d", &num1) == 1) {
00208                /* single number */
00209                num2 = num1;
00210             } else {
00211                return ERROR_USAGE;
00212             }
00213 
00214             /* Get to start, if any */
00215             if (num1 > 0) {
00216                while (tmp2 != (char *)NULL + 1 && curfieldnum < num1) {
00217                   tmp2 = strchr(tmp2, d) + 1;
00218                   curfieldnum++;
00219                }
00220             }
00221 
00222             /* Most frequent problem is the expectation of reordering fields */
00223             if ((num1 > 0) && (curfieldnum > num1))
00224                ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
00225 
00226             /* Re-null tmp2 if we added 1 to NULL */
00227             if (tmp2 == (char *)NULL + 1)
00228                tmp2 = NULL;
00229 
00230             /* Output fields until we either run out of fields or num2 is reached */
00231             while (tmp2 != NULL && curfieldnum <= num2) {
00232                char *tmp3 = strsep(&tmp2, ds);
00233                int curlen = strlen(buffer);
00234 
00235                if (firstfield) {
00236                   snprintf(buffer, buflen, "%s", tmp3);
00237                   firstfield = 0;
00238                } else {
00239                   snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3);
00240                }
00241 
00242                curfieldnum++;
00243             }
00244          }
00245       }
00246    }
00247    return 0;
00248 }
00249 
00250 static int acf_sort_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00251 {
00252    int ret = -1;
00253 
00254    switch (sort_internal(chan, data, buf, len)) {
00255    case ERROR_NOARG:
00256       ast_log(LOG_ERROR, "SORT() requires an argument\n");
00257       break;
00258    case ERROR_NOMEM:
00259       ast_log(LOG_ERROR, "Out of memory\n");
00260       break;
00261    case 0:
00262       ret = 0;
00263       break;
00264    default:
00265       ast_log(LOG_ERROR, "Unknown internal error\n");
00266    }
00267 
00268    return ret;
00269 }
00270 
00271 static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00272 {
00273    int ret = -1;
00274 
00275    switch (cut_internal(chan, data, buf, len)) {
00276    case ERROR_NOARG:
00277       ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
00278       break;
00279    case ERROR_NOMEM:
00280       ast_log(LOG_ERROR, "Out of memory\n");
00281       break;
00282    case ERROR_USAGE:
00283       ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
00284       break;
00285    case 0:
00286       ret = 0;
00287       break;
00288    default:
00289       ast_log(LOG_ERROR, "Unknown internal error\n");
00290    }
00291 
00292    return ret;
00293 }
00294 
00295 struct ast_custom_function acf_sort = {
00296    .name = "SORT",
00297    .read = acf_sort_exec,
00298 };
00299 
00300 struct ast_custom_function acf_cut = {
00301    .name = "CUT",
00302    .read = acf_cut_exec,
00303 };
00304 
00305 static int unload_module(void)
00306 {
00307    int res = 0;
00308 
00309    res |= ast_custom_function_unregister(&acf_cut);
00310    res |= ast_custom_function_unregister(&acf_sort);
00311 
00312    return res;
00313 }
00314 
00315 static int load_module(void)
00316 {
00317    int res = 0;
00318 
00319    res |= ast_custom_function_register(&acf_cut);
00320    res |= ast_custom_function_register(&acf_sort);
00321 
00322    return res;
00323 }
00324 
00325 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Cut out information from a string");