Sat Mar 10 01:54:18 2012

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 /*** MODULEINFO
00028    <support_level>core</support_level>
00029  ***/
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00034 
00035 #include "asterisk/file.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/app.h"
00040 
00041 /*** DOCUMENTATION
00042    <function name="SORT" language="en_US">
00043       <synopsis>
00044          Sorts a list of key/vals into a list of keys, based upon the vals.   
00045       </synopsis>
00046       <syntax>
00047          <parameter name="keyval" required="true" argsep=":">
00048             <argument name="key1" required="true" />
00049             <argument name="val1" required="true" />
00050          </parameter>
00051          <parameter name="keyvaln" multiple="true" argsep=":">
00052             <argument name="key2" required="true" />
00053             <argument name="val2" required="true" />
00054          </parameter>
00055       </syntax>
00056       <description>
00057          <para>Takes a comma-separated list of keys and values, each separated by a colon, and returns a
00058          comma-separated list of the keys, sorted by their values.  Values will be evaluated as
00059          floating-point numbers.</para>
00060       </description>
00061    </function>
00062    <function name="CUT" language="en_US">
00063       <synopsis>
00064          Slices and dices strings, based upon a named delimiter.     
00065       </synopsis>
00066       <syntax>
00067          <parameter name="varname" required="true">
00068             <para>Variable you want cut</para>
00069          </parameter>
00070          <parameter name="char-delim" required="true">
00071             <para>Delimiter, defaults to <literal>-</literal></para>
00072          </parameter>
00073          <parameter name="range-spec" required="true">
00074             <para>Number of the field you want (1-based offset), may also be specified as a range (with <literal>-</literal>)
00075             or group of ranges and fields (with <literal>&amp;</literal>)</para>
00076          </parameter>
00077       </syntax>
00078       <description>
00079          <para>Cut out information from a string (<replaceable>varname</replaceable>), based upon a named delimiter.</para>
00080       </description> 
00081    </function>
00082  ***/
00083 
00084 struct sortable_keys {
00085    char *key;
00086    float value;
00087 };
00088 
00089 static int sort_subroutine(const void *arg1, const void *arg2)
00090 {
00091    const struct sortable_keys *one=arg1, *two=arg2;
00092    if (one->value < two->value)
00093       return -1;
00094    else if (one->value == two->value)
00095       return 0;
00096    else
00097       return 1;
00098 }
00099 
00100 #define ERROR_NOARG  (-1)
00101 #define ERROR_NOMEM  (-2)
00102 #define ERROR_USAGE  (-3)
00103 
00104 static int sort_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen)
00105 {
00106    char *strings, *ptrkey, *ptrvalue;
00107    int count=1, count2, element_count=0;
00108    struct sortable_keys *sortable_keys;
00109 
00110    *buffer = '\0';
00111 
00112    if (!data)
00113       return ERROR_NOARG;
00114 
00115    strings = ast_strdupa(data);
00116 
00117    for (ptrkey = strings; *ptrkey; ptrkey++) {
00118       if (*ptrkey == ',')
00119          count++;
00120    }
00121 
00122    sortable_keys = alloca(count * sizeof(struct sortable_keys));
00123 
00124    memset(sortable_keys, 0, count * sizeof(struct sortable_keys));
00125 
00126    /* Parse each into a struct */
00127    count2 = 0;
00128    while ((ptrkey = strsep(&strings, ","))) {
00129       ptrvalue = strchr(ptrkey, ':');
00130       if (!ptrvalue) {
00131          count--;
00132          continue;
00133       }
00134       *ptrvalue++ = '\0';
00135       sortable_keys[count2].key = ptrkey;
00136       sscanf(ptrvalue, "%30f", &sortable_keys[count2].value);
00137       count2++;
00138    }
00139 
00140    /* Sort the structs */
00141    qsort(sortable_keys, count, sizeof(struct sortable_keys), sort_subroutine);
00142 
00143    for (count2 = 0; count2 < count; count2++) {
00144       int blen = strlen(buffer);
00145       if (element_count++) {
00146          strncat(buffer + blen, ",", buflen - blen - 1);
00147          blen++;
00148       }
00149       strncat(buffer + blen, sortable_keys[count2].key, buflen - blen - 1);
00150    }
00151 
00152    return 0;
00153 }
00154 
00155 static int cut_internal(struct ast_channel *chan, char *data, struct ast_str **buf, ssize_t buflen)
00156 {
00157    char *parse, ds[2], *var_expr;
00158    size_t delim_consumed;
00159    struct ast_str *var_value;
00160    AST_DECLARE_APP_ARGS(args,
00161       AST_APP_ARG(varname);
00162       AST_APP_ARG(delimiter);
00163       AST_APP_ARG(field);
00164    );
00165 
00166    parse = ast_strdupa(data);
00167 
00168    AST_STANDARD_APP_ARGS(args, parse);
00169 
00170    /* Check arguments */
00171    if (args.argc < 3) {
00172       return ERROR_NOARG;
00173    } else if (!(var_expr = alloca(strlen(args.varname) + 4))) {
00174       return ERROR_NOMEM;
00175    }
00176 
00177    /* Get the value of the variable named in the 1st argument */
00178    snprintf(var_expr, strlen(args.varname) + 4, "${%s}", args.varname);
00179    var_value = ast_str_create(16);
00180    ast_str_substitute_variables(&var_value, 0, chan, var_expr);
00181 
00182    /* Copy delimiter from 2nd argument to ds[] possibly decoding backslash escapes */
00183    if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed)) {
00184       ast_copy_string(ds, "-", sizeof(ds));
00185    }
00186    ds[1] = '\0';
00187 
00188    if (ast_str_strlen(var_value)) {
00189       int curfieldnum = 1;
00190       char *curfieldptr = ast_str_buffer(var_value);
00191       int out_field_count = 0;
00192 
00193       while (curfieldptr != NULL && args.field != NULL) {
00194          char *next_range = strsep(&(args.field), "&");
00195          int start_field, stop_field;
00196          char trashchar;
00197 
00198          if (sscanf(next_range, "%30d-%30d", &start_field, &stop_field) == 2) {
00199             /* range with both start and end */
00200          } else if (sscanf(next_range, "-%30d", &stop_field) == 1) {
00201             /* range with end only */
00202             start_field = 1;
00203          } else if ((sscanf(next_range, "%30d%1c", &start_field, &trashchar) == 2) && (trashchar == '-')) {
00204             /* range with start only */
00205             stop_field = INT_MAX;
00206          } else if (sscanf(next_range, "%30d", &start_field) == 1) {
00207             /* single number */
00208             stop_field = start_field;
00209          } else {
00210             /* invalid field spec */
00211             ast_free(var_value);
00212             return ERROR_USAGE;
00213          }
00214 
00215          /* Get to start, if not there already */
00216          while (curfieldptr != NULL && curfieldnum < start_field) {
00217             strsep(&curfieldptr, ds);
00218             curfieldnum++;
00219          }
00220 
00221          /* Most frequent problem is the expectation of reordering fields */
00222          if (curfieldnum > start_field) {
00223             ast_log(LOG_WARNING, "We're already past the field you wanted?\n");
00224          }
00225 
00226          /* Output fields until we either run out of fields or stop_field is reached */
00227          while (curfieldptr != NULL && curfieldnum <= stop_field) {
00228             char *field_value = strsep(&curfieldptr, ds);
00229             ast_str_append(buf, buflen, "%s%s", out_field_count++ ? ds : "", field_value);
00230             curfieldnum++;
00231          }
00232       }
00233    }
00234    ast_free(var_value);
00235    return 0;
00236 }
00237 
00238 static int acf_sort_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00239 {
00240    int ret = -1;
00241 
00242    switch (sort_internal(chan, data, buf, len)) {
00243    case ERROR_NOARG:
00244       ast_log(LOG_ERROR, "SORT() requires an argument\n");
00245       break;
00246    case ERROR_NOMEM:
00247       ast_log(LOG_ERROR, "Out of memory\n");
00248       break;
00249    case 0:
00250       ret = 0;
00251       break;
00252    default:
00253       ast_log(LOG_ERROR, "Unknown internal error\n");
00254    }
00255 
00256    return ret;
00257 }
00258 
00259 static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00260 {
00261    int ret = -1;
00262    struct ast_str *str = ast_str_create(16);
00263 
00264    switch (cut_internal(chan, data, &str, len)) {
00265    case ERROR_NOARG:
00266       ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
00267       break;
00268    case ERROR_NOMEM:
00269       ast_log(LOG_ERROR, "Out of memory\n");
00270       break;
00271    case ERROR_USAGE:
00272       ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
00273       break;
00274    case 0:
00275       ret = 0;
00276       ast_copy_string(buf, ast_str_buffer(str), len);
00277       break;
00278    default:
00279       ast_log(LOG_ERROR, "Unknown internal error\n");
00280    }
00281    ast_free(str);
00282    return ret;
00283 }
00284 
00285 static int acf_cut_exec2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00286 {
00287    int ret = -1;
00288 
00289    switch (cut_internal(chan, data, buf, len)) {
00290    case ERROR_NOARG:
00291       ast_log(LOG_ERROR, "Syntax: CUT(<varname>,<char-delim>,<range-spec>) - missing argument!\n");
00292       break;
00293    case ERROR_NOMEM:
00294       ast_log(LOG_ERROR, "Out of memory\n");
00295       break;
00296    case ERROR_USAGE:
00297       ast_log(LOG_ERROR, "Usage: CUT(<varname>,<char-delim>,<range-spec>)\n");
00298       break;
00299    case 0:
00300       ret = 0;
00301       break;
00302    default:
00303       ast_log(LOG_ERROR, "Unknown internal error\n");
00304    }
00305 
00306    return ret;
00307 }
00308 
00309 static struct ast_custom_function acf_sort = {
00310    .name = "SORT",
00311    .read = acf_sort_exec,
00312 };
00313 
00314 static struct ast_custom_function acf_cut = {
00315    .name = "CUT",
00316    .read = acf_cut_exec,
00317    .read2 = acf_cut_exec2,
00318 };
00319 
00320 static int unload_module(void)
00321 {
00322    int res = 0;
00323 
00324    res |= ast_custom_function_unregister(&acf_cut);
00325    res |= ast_custom_function_unregister(&acf_sort);
00326 
00327    return res;
00328 }
00329 
00330 static int load_module(void)
00331 {
00332    int res = 0;
00333 
00334    res |= ast_custom_function_register(&acf_cut);
00335    res |= ast_custom_function_register(&acf_sort);
00336 
00337    return res;
00338 }
00339 
00340 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Cut out information from a string");

Generated on Sat Mar 10 01:54:18 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7