Mon Aug 31 12:30:08 2015

Asterisk developer's documentation


func_env.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2010, Digium, Inc.
00005  *
00006  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*! \file
00018  *
00019  * \brief Environment related dialplan functions
00020  *
00021  * \ingroup functions
00022  */
00023 
00024 /*** MODULEINFO
00025    <support_level>core</support_level>
00026  ***/
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413592 $")
00031 
00032 #include <sys/stat.h>   /* stat(2) */
00033 
00034 #include "asterisk/module.h"
00035 #include "asterisk/channel.h"
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/app.h"
00039 #include "asterisk/file.h"
00040 
00041 /*** DOCUMENTATION
00042    <function name="ENV" language="en_US">
00043       <synopsis>
00044          Gets or sets the environment variable specified.
00045       </synopsis>
00046       <syntax>
00047          <parameter name="varname" required="true">
00048             <para>Environment variable name</para>
00049          </parameter>
00050       </syntax>
00051       <description>
00052          <para>Variables starting with <literal>AST_</literal> are reserved to the system and may not be set.</para>
00053       </description>
00054    </function>
00055    <function name="STAT" language="en_US">
00056       <synopsis>
00057          Does a check on the specified file.
00058       </synopsis>
00059       <syntax>
00060          <parameter name="flag" required="true">
00061             <para>Flag may be one of the following:</para>
00062             <para>d - Checks if the file is a directory.</para>
00063             <para>e - Checks if the file exists.</para>
00064             <para>f - Checks if the file is a regular file.</para>
00065             <para>m - Returns the file mode (in octal)</para>
00066             <para>s - Returns the size (in bytes) of the file</para>
00067             <para>A - Returns the epoch at which the file was last accessed.</para>
00068             <para>C - Returns the epoch at which the inode was last changed.</para>
00069             <para>M - Returns the epoch at which the file was last modified.</para>
00070          </parameter>
00071          <parameter name="filename" required="true" />
00072       </syntax>
00073       <description>
00074          <note>
00075             <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
00076             is set to <literal>no</literal>, this function can only be executed from the
00077             dialplan, and not directly from external protocols.</para>
00078          </note>
00079       </description>
00080    </function>
00081    <function name="FILE" language="en_US">
00082       <synopsis>
00083          Read or write text file.
00084       </synopsis>
00085       <syntax>
00086          <parameter name="filename" required="true" />
00087          <parameter name="offset">
00088             <para>Maybe specified as any number. If negative, <replaceable>offset</replaceable> specifies the number
00089             of bytes back from the end of the file.</para>
00090          </parameter>
00091          <parameter name="length">
00092             <para>If specified, will limit the length of the data read to that size. If negative,
00093             trims <replaceable>length</replaceable> bytes from the end of the file.</para>
00094          </parameter>
00095          <parameter name="options">
00096             <optionlist>
00097                <option name="l">
00098                   <para>Line mode:  offset and length are assumed to be
00099                   measured in lines, instead of byte offsets.</para>
00100                </option>
00101                <option name="a">
00102                   <para>In write mode only, the append option is used to
00103                   append to the end of the file, instead of overwriting
00104                   the existing file.</para>
00105                </option>
00106                <option name="d">
00107                   <para>In write mode and line mode only, this option does
00108                   not automatically append a newline string to the end of
00109                   a value.  This is useful for deleting lines, instead of
00110                   setting them to blank.</para>
00111                </option>
00112             </optionlist>
00113          </parameter>
00114          <parameter name="format">
00115             <para>The <replaceable>format</replaceable> parameter may be
00116             used to delimit the type of line terminators in line mode.</para>
00117             <optionlist>
00118                <option name="u">
00119                   <para>Unix newline format.</para>
00120                </option>
00121                <option name="d">
00122                   <para>DOS newline format.</para>
00123                </option>
00124                <option name="m">
00125                   <para>Macintosh newline format.</para>
00126                </option>
00127             </optionlist>
00128          </parameter>
00129       </syntax>
00130       <description>
00131          <para>Read and write text file in character and line mode.</para>
00132          <para>Examples:</para>
00133          <para/>
00134          <para>Read mode (byte):</para>
00135          <para>    ;reads the entire content of the file.</para>
00136          <para>    Set(foo=${FILE(/tmp/test.txt)})</para>
00137          <para>    ;reads from the 11th byte to the end of the file (i.e. skips the first 10).</para>
00138          <para>    Set(foo=${FILE(/tmp/test.txt,10)})</para>
00139          <para>    ;reads from the 11th to 20th byte in the file (i.e. skip the first 10, then read 10 bytes).</para>
00140          <para>    Set(foo=${FILE(/tmp/test.txt,10,10)})</para>
00141          <para/>
00142          <para>Read mode (line):</para>
00143          <para>    ; reads the 3rd line of the file.</para>
00144          <para>    Set(foo=${FILE(/tmp/test.txt,3,1,l)})</para>
00145          <para>    ; reads the 3rd and 4th lines of the file.</para>
00146          <para>    Set(foo=${FILE(/tmp/test.txt,3,2,l)})</para>
00147          <para>    ; reads from the third line to the end of the file.</para>
00148          <para>    Set(foo=${FILE(/tmp/test.txt,3,,l)})</para>
00149          <para>    ; reads the last three lines of the file.</para>
00150          <para>    Set(foo=${FILE(/tmp/test.txt,-3,,l)})</para>
00151          <para>    ; reads the 3rd line of a DOS-formatted file.</para>
00152          <para>    Set(foo=${FILE(/tmp/test.txt,3,1,l,d)})</para>
00153          <para/>
00154          <para>Write mode (byte):</para>
00155          <para>    ; truncate the file and write "bar"</para>
00156          <para>    Set(FILE(/tmp/test.txt)=bar)</para>
00157          <para>    ; Append "bar"</para>
00158          <para>    Set(FILE(/tmp/test.txt,,,a)=bar)</para>
00159          <para>    ; Replace the first byte with "bar" (replaces 1 character with 3)</para>
00160          <para>    Set(FILE(/tmp/test.txt,0,1)=bar)</para>
00161          <para>    ; Replace 10 bytes beginning at the 21st byte of the file with "bar"</para>
00162          <para>    Set(FILE(/tmp/test.txt,20,10)=bar)</para>
00163          <para>    ; Replace all bytes from the 21st with "bar"</para>
00164          <para>    Set(FILE(/tmp/test.txt,20)=bar)</para>
00165          <para>    ; Insert "bar" after the 4th character</para>
00166          <para>    Set(FILE(/tmp/test.txt,4,0)=bar)</para>
00167          <para/>
00168          <para>Write mode (line):</para>
00169          <para>    ; Replace the first line of the file with "bar"</para>
00170          <para>    Set(FILE(/tmp/foo.txt,0,1,l)=bar)</para>
00171          <para>    ; Replace the last line of the file with "bar"</para>
00172          <para>    Set(FILE(/tmp/foo.txt,-1,,l)=bar)</para>
00173          <para>    ; Append "bar" to the file with a newline</para>
00174          <para>    Set(FILE(/tmp/foo.txt,,,al)=bar)</para>
00175          <note>
00176             <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
00177             is set to <literal>no</literal>, this function can only be executed from the
00178             dialplan, and not directly from external protocols.</para>
00179          </note>
00180       </description>
00181       <see-also>
00182          <ref type="function">FILE_COUNT_LINE</ref>
00183          <ref type="function">FILE_FORMAT</ref>
00184       </see-also>
00185    </function>
00186    <function name="FILE_COUNT_LINE" language="en_US">
00187       <synopsis>
00188          Obtains the number of lines of a text file.
00189       </synopsis>
00190       <syntax>
00191          <parameter name="filename" required="true" />
00192          <parameter name="format">
00193             <para>Format may be one of the following:</para>
00194             <optionlist>
00195                <option name="u">
00196                   <para>Unix newline format.</para>
00197                </option>
00198                <option name="d">
00199                   <para>DOS newline format.</para>
00200                </option>
00201                <option name="m">
00202                   <para>Macintosh newline format.</para>
00203                </option>
00204             </optionlist>
00205             <note><para>If not specified, an attempt will be made to determine the newline format type.</para></note>
00206          </parameter>
00207       </syntax>
00208       <description>
00209          <para>Returns the number of lines, or <literal>-1</literal> on error.</para>
00210          <note>
00211             <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
00212             is set to <literal>no</literal>, this function can only be executed from the
00213             dialplan, and not directly from external protocols.</para>
00214          </note>
00215       </description>
00216       <see-also>
00217          <ref type="function">FILE</ref>
00218          <ref type="function">FILE_FORMAT</ref>
00219       </see-also>
00220    </function>
00221    <function name="FILE_FORMAT" language="en_US">
00222       <synopsis>
00223          Return the newline format of a text file.
00224       </synopsis>
00225       <syntax>
00226          <parameter name="filename" required="true" />
00227       </syntax>
00228       <description>
00229          <para>Return the line terminator type:</para>
00230          <para>'u' - Unix "\n" format</para>
00231          <para>'d' - DOS "\r\n" format</para>
00232          <para>'m' - Macintosh "\r" format</para>
00233          <para>'x' - Cannot be determined</para>
00234          <note>
00235             <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
00236             is set to <literal>no</literal>, this function can only be executed from the
00237             dialplan, and not directly from external protocols.</para>
00238          </note>
00239       </description>
00240       <see-also>
00241          <ref type="function">FILE</ref>
00242          <ref type="function">FILE_COUNT_LINE</ref>
00243       </see-also>
00244    </function>
00245  ***/
00246 
00247 static int env_read(struct ast_channel *chan, const char *cmd, char *data,
00248          char *buf, size_t len)
00249 {
00250    char *ret = NULL;
00251 
00252    *buf = '\0';
00253 
00254    if (data)
00255       ret = getenv(data);
00256 
00257    if (ret)
00258       ast_copy_string(buf, ret, len);
00259 
00260    return 0;
00261 }
00262 
00263 static int env_write(struct ast_channel *chan, const char *cmd, char *data,
00264           const char *value)
00265 {
00266    if (!ast_strlen_zero(data) && strncmp(data, "AST_", 4)) {
00267       if (!ast_strlen_zero(value)) {
00268          setenv(data, value, 1);
00269       } else {
00270          unsetenv(data);
00271       }
00272    }
00273 
00274    return 0;
00275 }
00276 
00277 static int stat_read(struct ast_channel *chan, const char *cmd, char *data,
00278           char *buf, size_t len)
00279 {
00280    char *action;
00281    struct stat s;
00282 
00283    ast_copy_string(buf, "0", len);
00284 
00285    action = strsep(&data, ",");
00286    if (stat(data, &s)) {
00287       return 0;
00288    } else {
00289       switch (*action) {
00290       case 'e':
00291          strcpy(buf, "1");
00292          break;
00293       case 's':
00294          snprintf(buf, len, "%u", (unsigned int) s.st_size);
00295          break;
00296       case 'f':
00297          snprintf(buf, len, "%d", S_ISREG(s.st_mode) ? 1 : 0);
00298          break;
00299       case 'd':
00300          snprintf(buf, len, "%d", S_ISDIR(s.st_mode) ? 1 : 0);
00301          break;
00302       case 'M':
00303          snprintf(buf, len, "%d", (int) s.st_mtime);
00304          break;
00305       case 'A':
00306          snprintf(buf, len, "%d", (int) s.st_mtime);
00307          break;
00308       case 'C':
00309          snprintf(buf, len, "%d", (int) s.st_ctime);
00310          break;
00311       case 'm':
00312          snprintf(buf, len, "%o", s.st_mode);
00313          break;
00314       }
00315    }
00316 
00317    return 0;
00318 }
00319 
00320 enum file_format {
00321    FF_UNKNOWN = -1,
00322    FF_UNIX,
00323    FF_DOS,
00324    FF_MAC,
00325 };
00326 
00327 static int64_t count_lines(const char *filename, enum file_format newline_format)
00328 {
00329    int count = 0;
00330    char fbuf[4096];
00331    FILE *ff;
00332 
00333    if (!(ff = fopen(filename, "r"))) {
00334       ast_log(LOG_ERROR, "Unable to open '%s': %s\n", filename, strerror(errno));
00335       return -1;
00336    }
00337 
00338    while (fgets(fbuf, sizeof(fbuf), ff)) {
00339       char *next = fbuf, *first_cr = NULL, *first_nl = NULL;
00340 
00341       /* Must do it this way, because if the fileformat is FF_MAC, then Unix
00342        * assumptions about line-format will not come into play. */
00343       while (next) {
00344          if (newline_format == FF_DOS || newline_format == FF_MAC || newline_format == FF_UNKNOWN) {
00345             first_cr = strchr(next, '\r');
00346          }
00347          if (newline_format == FF_UNIX || newline_format == FF_UNKNOWN) {
00348             first_nl = strchr(next, '\n');
00349          }
00350 
00351          /* No terminators found in buffer */
00352          if (!first_cr && !first_nl) {
00353             break;
00354          }
00355 
00356          if (newline_format == FF_UNKNOWN) {
00357             if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) {
00358                if (first_nl && first_nl == first_cr + 1) {
00359                   newline_format = FF_DOS;
00360                } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) {
00361                   /* Get it on the next pass */
00362                   fseek(ff, -1, SEEK_CUR);
00363                   break;
00364                } else {
00365                   newline_format = FF_MAC;
00366                   first_nl = NULL;
00367                }
00368             } else {
00369                newline_format = FF_UNIX;
00370                first_cr = NULL;
00371             }
00372             /* Jump down into next section */
00373          }
00374 
00375          if (newline_format == FF_DOS) {
00376             if (first_nl && first_cr && first_nl == first_cr + 1) {
00377                next = first_nl + 1;
00378                count++;
00379             } else if (first_cr == &fbuf[sizeof(fbuf) - 2]) {
00380                /* Get it on the next pass */
00381                fseek(ff, -1, SEEK_CUR);
00382                break;
00383             }
00384          } else if (newline_format == FF_MAC) {
00385             if (first_cr) {
00386                next = first_cr + 1;
00387                count++;
00388             }
00389          } else if (newline_format == FF_UNIX) {
00390             if (first_nl) {
00391                next = first_nl + 1;
00392                count++;
00393             }
00394          }
00395       }
00396    }
00397    fclose(ff);
00398 
00399    return count;
00400 }
00401 
00402 static int file_count_line(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00403 {
00404    enum file_format newline_format = FF_UNKNOWN;
00405    int64_t count;
00406    AST_DECLARE_APP_ARGS(args,
00407       AST_APP_ARG(filename);
00408       AST_APP_ARG(format);
00409    );
00410 
00411    AST_STANDARD_APP_ARGS(args, data);
00412    if (args.argc > 1) {
00413       if (tolower(args.format[0]) == 'd') {
00414          newline_format = FF_DOS;
00415       } else if (tolower(args.format[0]) == 'm') {
00416          newline_format = FF_MAC;
00417       } else if (tolower(args.format[0]) == 'u') {
00418          newline_format = FF_UNIX;
00419       }
00420    }
00421 
00422    count = count_lines(args.filename, newline_format);
00423    ast_str_set(buf, len, "%" PRId64, count);
00424    return 0;
00425 }
00426 
00427 #define LINE_COUNTER(cptr, term, counter) \
00428    if (*cptr == '\n' && term == FF_UNIX) { \
00429       counter++; \
00430    } else if (*cptr == '\n' && term == FF_DOS && dos_state == 0) { \
00431       dos_state = 1; \
00432    } else if (*cptr == '\r' && term == FF_DOS && dos_state == 1) { \
00433       dos_state = 0; \
00434       counter++; \
00435    } else if (*cptr == '\r' && term == FF_MAC) { \
00436       counter++; \
00437    } else if (term == FF_DOS) { \
00438       dos_state = 0; \
00439    }
00440 
00441 static enum file_format file2format(const char *filename)
00442 {
00443    FILE *ff;
00444    char fbuf[4096];
00445    char *first_cr, *first_nl;
00446    enum file_format newline_format = FF_UNKNOWN;
00447 
00448    if (!(ff = fopen(filename, "r"))) {
00449       ast_log(LOG_ERROR, "Cannot open '%s': %s\n", filename, strerror(errno));
00450       return -1;
00451    }
00452 
00453    while (fgets(fbuf, sizeof(fbuf), ff)) {
00454       first_cr = strchr(fbuf, '\r');
00455       first_nl = strchr(fbuf, '\n');
00456 
00457       if (!first_cr && !first_nl) {
00458          continue;
00459       }
00460 
00461       if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) {
00462 
00463          if (first_nl && first_nl == first_cr + 1) {
00464             newline_format = FF_DOS;
00465          } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) {
00466             /* Edge case: get it on the next pass */
00467             fseek(ff, -1, SEEK_CUR);
00468             continue;
00469          } else {
00470             newline_format = FF_MAC;
00471          }
00472       } else {
00473          newline_format = FF_UNIX;
00474       }
00475       break;
00476    }
00477    fclose(ff);
00478    return newline_format;
00479 }
00480 
00481 static int file_format(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00482 {
00483    enum file_format newline_format = file2format(data);
00484    ast_str_set(buf, len, "%c", newline_format == FF_UNIX ? 'u' : newline_format == FF_DOS ? 'd' : newline_format == FF_MAC ? 'm' : 'x');
00485    return 0;
00486 }
00487 
00488 static int file_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00489 {
00490    FILE *ff;
00491    int64_t offset = 0, length = LLONG_MAX;
00492    enum file_format format = FF_UNKNOWN;
00493    char fbuf[4096];
00494    int64_t flength, i; /* iterator needs to be signed, so it can go negative and terminate the loop */
00495    int64_t offset_offset = -1, length_offset = -1;
00496    char dos_state = 0;
00497    size_t readlen;
00498    AST_DECLARE_APP_ARGS(args,
00499       AST_APP_ARG(filename);
00500       AST_APP_ARG(offset);
00501       AST_APP_ARG(length);
00502       AST_APP_ARG(options);
00503       AST_APP_ARG(fileformat);
00504    );
00505 
00506    AST_STANDARD_APP_ARGS(args, data);
00507 
00508    if (args.argc > 1) {
00509       sscanf(args.offset, "%" SCNd64, &offset);
00510    }
00511    if (args.argc > 2) {
00512       sscanf(args.length, "%" SCNd64, &length);
00513    }
00514 
00515    if (args.argc < 4 || !strchr(args.options, 'l')) {
00516       /* Character-based mode */
00517       off_t off_i;
00518 
00519       if (!(ff = fopen(args.filename, "r"))) {
00520          ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", args.filename, strerror(errno));
00521          return 0;
00522       }
00523 
00524       if (fseeko(ff, 0, SEEK_END) < 0) {
00525          ast_log(LOG_ERROR, "Cannot seek to end of '%s': %s\n", args.filename, strerror(errno));
00526          fclose(ff);
00527          return -1;
00528       }
00529       flength = ftello(ff);
00530 
00531       if (offset < 0) {
00532          fseeko(ff, offset, SEEK_END);
00533          if ((offset = ftello(ff)) < 0) {
00534             ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
00535             fclose(ff);
00536             return -1;
00537          }
00538       }
00539       if (length < 0) {
00540          fseeko(ff, length, SEEK_END);
00541          if ((length = ftello(ff)) - offset < 0) {
00542             /* Eliminates all results */
00543             fclose(ff);
00544             return -1;
00545          }
00546       } else if (length == LLONG_MAX) {
00547          fseeko(ff, 0, SEEK_END);
00548          length = ftello(ff);
00549       }
00550 
00551       ast_str_reset(*buf);
00552 
00553       fseeko(ff, offset, SEEK_SET);
00554       for (off_i = ftello(ff); off_i < flength && off_i < offset + length; off_i += sizeof(fbuf)) {
00555          /* Calculate if we need to retrieve just a portion of the file in memory */
00556          size_t toappend = sizeof(fbuf);
00557 
00558          if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00559             ast_log(LOG_ERROR, "Short read?!!\n");
00560             break;
00561          }
00562 
00563          /* Don't go past the length requested */
00564          if (off_i + toappend > offset + length) {
00565             toappend = length - off_i;
00566          }
00567 
00568          ast_str_append_substr(buf, len, fbuf, toappend);
00569       }
00570       fclose(ff);
00571       return 0;
00572    }
00573 
00574    /* Line-based read */
00575    if (args.argc == 5) {
00576       if (tolower(args.fileformat[0]) == 'd') {
00577          format = FF_DOS;
00578       } else if (tolower(args.fileformat[0]) == 'm') {
00579          format = FF_MAC;
00580       } else if (tolower(args.fileformat[0]) == 'u') {
00581          format = FF_UNIX;
00582       }
00583    }
00584 
00585    if (format == FF_UNKNOWN) {
00586       if ((format = file2format(args.filename)) == FF_UNKNOWN) {
00587          ast_log(LOG_WARNING, "'%s' is not a line-based file\n", args.filename);
00588          return -1;
00589       }
00590    }
00591 
00592    if (offset < 0 && length <= offset) {
00593       /* Length eliminates all content */
00594       return -1;
00595    } else if (offset == 0) {
00596       offset_offset = 0;
00597    }
00598 
00599    if (!(ff = fopen(args.filename, "r"))) {
00600       ast_log(LOG_ERROR, "Cannot open '%s': %s\n", args.filename, strerror(errno));
00601       return -1;
00602    }
00603 
00604    if (fseek(ff, 0, SEEK_END)) {
00605       ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
00606       fclose(ff);
00607       return -1;
00608    }
00609 
00610    flength = ftello(ff);
00611 
00612    if (length == LLONG_MAX) {
00613       length_offset = flength;
00614    }
00615 
00616    /* For negative offset and/or negative length */
00617    if (offset < 0 || length < 0) {
00618       int64_t count = 0;
00619       /* Start with an even multiple of fbuf, so at the end of reading with a
00620        * 0 offset, we don't try to go past the beginning of the file. */
00621       for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
00622          size_t end;
00623          char *pos;
00624          if (fseeko(ff, i, SEEK_SET)) {
00625             ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
00626          }
00627          end = fread(fbuf, 1, sizeof(fbuf), ff);
00628          for (pos = end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
00629             LINE_COUNTER(pos, format, count);
00630 
00631             if (length < 0 && count * -1 == length) {
00632                length_offset = i + (pos - fbuf);
00633             } else if (offset < 0 && count * -1 == (offset - 1)) {
00634                /* Found our initial offset.  We're done with reverse motion! */
00635                if (format == FF_DOS) {
00636                   offset_offset = i + (pos - fbuf) + 2;
00637                } else {
00638                   offset_offset = i + (pos - fbuf) + 1;
00639                }
00640                break;
00641             }
00642          }
00643          if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
00644             break;
00645          }
00646       }
00647       /* We're at the beginning, and the negative offset indicates the exact number of lines in the file */
00648       if (offset < 0 && offset_offset < 0 && offset == count * -1) {
00649          offset_offset = 0;
00650       }
00651    }
00652 
00653    /* Positve line offset */
00654    if (offset > 0) {
00655       int64_t count = 0;
00656       fseek(ff, 0, SEEK_SET);
00657       for (i = 0; i < flength; i += sizeof(fbuf)) {
00658          char *pos;
00659          if (i + sizeof(fbuf) <= flength) {
00660             /* Don't let previous values influence current counts, due to short reads */
00661             memset(fbuf, 0, sizeof(fbuf));
00662          }
00663          if (fread(fbuf, 1, sizeof(fbuf), ff) && !feof(ff)) {
00664             ast_log(LOG_ERROR, "Short read?!!\n");
00665             fclose(ff);
00666             return -1;
00667          }
00668          for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
00669             LINE_COUNTER(pos, format, count);
00670 
00671             if (count == offset) {
00672                offset_offset = i + (pos - fbuf) + 1;
00673                break;
00674             }
00675          }
00676          if (offset_offset >= 0) {
00677             break;
00678          }
00679       }
00680    }
00681 
00682    if (offset_offset < 0) {
00683       ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
00684       fclose(ff);
00685       return -1;
00686    }
00687 
00688    ast_str_reset(*buf);
00689    if (fseeko(ff, offset_offset, SEEK_SET)) {
00690       ast_log(LOG_ERROR, "fseeko failed: %s\n", strerror(errno));
00691    }
00692 
00693    /* If we have both offset_offset and length_offset, then grabbing the
00694     * buffer is simply a matter of just retrieving the file and adding it
00695     * to buf.  Otherwise, we need to run byte-by-byte forward until the
00696     * length is complete. */
00697    if (length_offset >= 0) {
00698       ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
00699       for (i = offset_offset; i < length_offset; i += sizeof(fbuf)) {
00700          if (fread(fbuf, 1, i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf), ff) < (i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf))) {
00701             ast_log(LOG_ERROR, "Short read?!!\n");
00702          }
00703          ast_debug(3, "Appending first %" PRId64" bytes of fbuf=%s\n", (int64_t)(i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf)), fbuf);
00704          ast_str_append_substr(buf, len, fbuf, i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf));
00705       }
00706    } else if (length == 0) {
00707       /* Nothing to do */
00708    } else {
00709       /* Positive line offset */
00710       int64_t current_length = 0;
00711       char dos_state = 0;
00712       ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset);
00713       for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
00714          char *pos;
00715          if ((readlen = fread(fbuf, 1, sizeof(fbuf), ff)) < sizeof(fbuf) && !feof(ff)) {
00716             ast_log(LOG_ERROR, "Short read?!!\n");
00717          }
00718          for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
00719             LINE_COUNTER(pos, format, current_length);
00720 
00721             if (current_length == length) {
00722                length_offset = i + (pos - fbuf) + 1;
00723                break;
00724             }
00725          }
00726          ast_debug(3, "length_offset=%" PRId64 ", length_offset - i=%" PRId64 "\n", length_offset, length_offset - i);
00727          ast_str_append_substr(buf, len, fbuf, length_offset >= 0 ? length_offset - i : flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i;
00728 
00729          if (length_offset >= 0) {
00730             break;
00731          }
00732       }
00733    }
00734 
00735    fclose(ff);
00736    return 0;
00737 }
00738 
00739 const char *format2term(enum file_format f) __attribute__((const));
00740 const char *format2term(enum file_format f)
00741 {
00742    const char *term[] = { "", "\n", "\r\n", "\r" };
00743    return term[f + 1];
00744 }
00745 
00746 static int file_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00747 {
00748    AST_DECLARE_APP_ARGS(args,
00749       AST_APP_ARG(filename);
00750       AST_APP_ARG(offset);
00751       AST_APP_ARG(length);
00752       AST_APP_ARG(options);
00753       AST_APP_ARG(format);
00754    );
00755    int64_t offset = 0, length = LLONG_MAX;
00756    off_t flength, vlength;
00757    size_t foplen = 0;
00758    FILE *ff;
00759 
00760    AST_STANDARD_APP_ARGS(args, data);
00761 
00762    if (args.argc > 1) {
00763       sscanf(args.offset, "%" SCNd64, &offset);
00764    }
00765    if (args.argc > 2) {
00766       sscanf(args.length, "%" SCNd64, &length);
00767    }
00768 
00769    vlength = strlen(value);
00770 
00771    if (args.argc < 4 || !strchr(args.options, 'l')) {
00772       /* Character-based mode */
00773 
00774       if (args.argc > 3 && strchr(args.options, 'a')) {
00775          /* Append mode */
00776          if (!(ff = fopen(args.filename, "a"))) {
00777             ast_log(LOG_WARNING, "Cannot open file '%s' for appending: %s\n", args.filename, strerror(errno));
00778             return 0;
00779          }
00780          if (fwrite(value, 1, vlength, ff) < vlength) {
00781             ast_log(LOG_ERROR, "Short write?!!\n");
00782          }
00783          fclose(ff);
00784          return 0;
00785       } else if (offset == 0 && length == LLONG_MAX) {
00786          if (!(ff = fopen(args.filename, "w"))) {
00787             ast_log(LOG_WARNING, "Cannot open file '%s' for writing: %s\n", args.filename, strerror(errno));
00788             return 0;
00789          }
00790          if (fwrite(value, 1, vlength, ff) < vlength) {
00791             ast_log(LOG_ERROR, "Short write?!!\n");
00792          }
00793          fclose(ff);
00794          return 0;
00795       }
00796 
00797       if (!(ff = fopen(args.filename, "r+"))) {
00798          ast_log(LOG_WARNING, "Cannot open file '%s' for modification: %s\n", args.filename, strerror(errno));
00799          return 0;
00800       }
00801       fseeko(ff, 0, SEEK_END);
00802       flength = ftello(ff);
00803 
00804       if (offset < 0) {
00805          if (fseeko(ff, offset, SEEK_END)) {
00806             ast_log(LOG_ERROR, "Cannot seek to offset of '%s': %s\n", args.filename, strerror(errno));
00807             fclose(ff);
00808             return -1;
00809          }
00810          if ((offset = ftello(ff)) < 0) {
00811             ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno));
00812             fclose(ff);
00813             return -1;
00814          }
00815       }
00816 
00817       if (length < 0) {
00818          length = flength - offset + length;
00819          if (length < 0) {
00820             ast_log(LOG_ERROR, "Length '%s' exceeds the file length.  No data will be written.\n", args.length);
00821             fclose(ff);
00822             return -1;
00823          }
00824       }
00825 
00826       fseeko(ff, offset, SEEK_SET);
00827 
00828       ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
00829          S_OR(args.offset, "(null)"), offset, S_OR(args.length, "(null)"), length, vlength, flength);
00830 
00831       if (length == vlength) {
00832          /* Simplest case, a straight replace */
00833          if (fwrite(value, 1, vlength, ff) < vlength) {
00834             ast_log(LOG_ERROR, "Short write?!!\n");
00835          }
00836          fclose(ff);
00837       } else if (length == LLONG_MAX) {
00838          /* Simple truncation */
00839          if (fwrite(value, 1, vlength, ff) < vlength) {
00840             ast_log(LOG_ERROR, "Short write?!!\n");
00841          }
00842          fclose(ff);
00843          if (truncate(args.filename, offset + vlength)) {
00844             ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
00845          }
00846       } else if (length > vlength) {
00847          /* More complex -- need to close a gap */
00848          char fbuf[4096];
00849          off_t cur;
00850          if (fwrite(value, 1, vlength, ff) < vlength) {
00851             ast_log(LOG_ERROR, "Short write?!!\n");
00852          }
00853          fseeko(ff, length - vlength, SEEK_CUR);
00854          while ((cur = ftello(ff)) < flength) {
00855             if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00856                ast_log(LOG_ERROR, "Short read?!!\n");
00857             }
00858             fseeko(ff, cur + vlength - length, SEEK_SET);
00859             if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00860                ast_log(LOG_ERROR, "Short write?!!\n");
00861             }
00862             /* Seek to where we stopped reading */
00863             if (fseeko(ff, cur + sizeof(fbuf), SEEK_SET) < 0) {
00864                /* Only reason for seek to fail is EOF */
00865                break;
00866             }
00867          }
00868          fclose(ff);
00869          if (truncate(args.filename, flength - (length - vlength))) {
00870             ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno));
00871          }
00872       } else {
00873          /* Most complex -- need to open a gap */
00874          char fbuf[4096];
00875          off_t lastwritten = flength + vlength - length;
00876 
00877          /* Start reading exactly the buffer size back from the end. */
00878          fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
00879          while (offset < ftello(ff)) {
00880             if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00881                ast_log(LOG_ERROR, "Short read?!!\n");
00882                fclose(ff);
00883                return -1;
00884             }
00885             /* Since the read moved our file ptr forward, we reverse, but
00886              * seek an offset equal to the amount we want to extend the
00887              * file by */
00888             fseeko(ff, vlength - length - sizeof(fbuf), SEEK_CUR);
00889 
00890             /* Note the location of this buffer -- we must not overwrite this position. */
00891             lastwritten = ftello(ff);
00892 
00893             if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
00894                ast_log(LOG_ERROR, "Short write?!!\n");
00895                fclose(ff);
00896                return -1;
00897             }
00898 
00899             if (lastwritten < offset + sizeof(fbuf)) {
00900                break;
00901             }
00902             /* Our file pointer is now either pointing to the end of the
00903              * file (new position) or a multiple of the fbuf size back from
00904              * that point.  Move back to where we want to start reading
00905              * again.  We never actually try to read beyond the end of the
00906              * file, so we don't have do deal with short reads, as we would
00907              * when we're shortening the file. */
00908             fseeko(ff, 2 * sizeof(fbuf) + vlength - length, SEEK_CUR);
00909          }
00910 
00911          /* Last part of the file that we need to preserve */
00912          if (fseeko(ff, offset + length, SEEK_SET)) {
00913             ast_log(LOG_WARNING, "Unable to seek to %" PRId64 " + %" PRId64 " != %" PRId64 "?)\n", offset, length, ftello(ff));
00914          }
00915 
00916          /* Doesn't matter how much we read -- just need to restrict the write */
00917          ast_debug(1, "Reading at %" PRId64 "\n", ftello(ff));
00918          if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
00919             ast_log(LOG_ERROR, "Short read?!!\n");
00920          }
00921          fseek(ff, offset, SEEK_SET);
00922          /* Write out the value, then write just up until where we last moved some data */
00923          if (fwrite(value, 1, vlength, ff) < vlength) {
00924             ast_log(LOG_ERROR, "Short write?!!\n");
00925          } else {
00926             off_t curpos = ftello(ff);
00927             foplen = lastwritten - curpos;
00928             if (fwrite(fbuf, 1, foplen, ff) < foplen) {
00929                ast_log(LOG_ERROR, "Short write?!!\n");
00930             }
00931          }
00932          fclose(ff);
00933       }
00934    } else {
00935       enum file_format newline_format = FF_UNKNOWN;
00936 
00937       /* Line mode */
00938       if (args.argc == 5) {
00939          if (tolower(args.format[0]) == 'u') {
00940             newline_format = FF_UNIX;
00941          } else if (tolower(args.format[0]) == 'm') {
00942             newline_format = FF_MAC;
00943          } else if (tolower(args.format[0]) == 'd') {
00944             newline_format = FF_DOS;
00945          }
00946       }
00947       if (newline_format == FF_UNKNOWN && (newline_format = file2format(args.filename)) == FF_UNKNOWN) {
00948          ast_log(LOG_ERROR, "File '%s' not in line format\n", args.filename);
00949          return -1;
00950       }
00951 
00952       if (strchr(args.options, 'a')) {
00953          /* Append to file */
00954          if (!(ff = fopen(args.filename, "a"))) {
00955             ast_log(LOG_ERROR, "Unable to open '%s' for appending: %s\n", args.filename, strerror(errno));
00956             return -1;
00957          }
00958          if (fwrite(value, 1, vlength, ff) < vlength) {
00959             ast_log(LOG_ERROR, "Short write?!!\n");
00960          } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
00961             ast_log(LOG_ERROR, "Short write?!!\n");
00962          }
00963          fclose(ff);
00964       } else if (offset == 0 && length == LLONG_MAX) {
00965          /* Overwrite file */
00966          off_t truncsize;
00967          if (!(ff = fopen(args.filename, "w"))) {
00968             ast_log(LOG_ERROR, "Unable to open '%s' for writing: %s\n", args.filename, strerror(errno));
00969             return -1;
00970          }
00971          if (fwrite(value, 1, vlength, ff) < vlength) {
00972             ast_log(LOG_ERROR, "Short write?!!\n");
00973          } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
00974             ast_log(LOG_ERROR, "Short write?!!\n");
00975          }
00976          if ((truncsize = ftello(ff)) < 0) {
00977             ast_log(AST_LOG_ERROR, "Unable to determine truncate position of '%s': %s\n", args.filename, strerror(errno));
00978          }
00979          fclose(ff);
00980          if (truncsize >= 0 && truncate(args.filename, truncsize)) {
00981             ast_log(LOG_ERROR, "Unable to truncate file '%s': %s\n", args.filename, strerror(errno));
00982             return -1;
00983          }
00984       } else {
00985          int64_t offset_offset = (offset == 0 ? 0 : -1), length_offset = -1, flength, i, current_length = 0;
00986          char dos_state = 0, fbuf[4096];
00987 
00988          if (offset < 0 && length < offset) {
00989             /* Nonsense! */
00990             ast_log(LOG_ERROR, "Length cannot specify a position prior to the offset\n");
00991             return -1;
00992          }
00993 
00994          if (!(ff = fopen(args.filename, "r+"))) {
00995             ast_log(LOG_ERROR, "Cannot open '%s' for modification: %s\n", args.filename, strerror(errno));
00996             return -1;
00997          }
00998 
00999          if (fseek(ff, 0, SEEK_END)) {
01000             ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno));
01001             fclose(ff);
01002             return -1;
01003          }
01004          if ((flength = ftello(ff)) < 0) {
01005             ast_log(AST_LOG_ERROR, "Cannot determine end position of file '%s': %s\n", args.filename, strerror(errno));
01006             fclose(ff);
01007             return -1;
01008          }
01009 
01010          /* For negative offset and/or negative length */
01011          if (offset < 0 || length < 0) {
01012             int64_t count = 0;
01013             for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) {
01014                char *pos;
01015                if (fseeko(ff, i, SEEK_SET)) {
01016                   ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
01017                }
01018                if (i + sizeof(fbuf) >= flength) {
01019                   memset(fbuf, 0, sizeof(fbuf));
01020                }
01021                if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01022                   ast_log(LOG_ERROR, "Short read: %s\n", strerror(errno));
01023                   fclose(ff);
01024                   return -1;
01025                }
01026                for (pos = fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
01027                   LINE_COUNTER(pos, newline_format, count);
01028 
01029                   if (length < 0 && count * -1 == length) {
01030                      length_offset = i + (pos - fbuf);
01031                   } else if (offset < 0 && count * -1 == (offset - 1)) {
01032                      /* Found our initial offset.  We're done with reverse motion! */
01033                      if (newline_format == FF_DOS) {
01034                         offset_offset = i + (pos - fbuf) + 2;
01035                      } else {
01036                         offset_offset = i + (pos - fbuf) + 1;
01037                      }
01038                      break;
01039                   }
01040                }
01041                if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) {
01042                   break;
01043                }
01044             }
01045             /* We're at the beginning, and the negative offset indicates the exact number of lines in the file */
01046             if (offset < 0 && offset_offset < 0 && offset == count * -1) {
01047                offset_offset = 0;
01048             }
01049          }
01050 
01051          /* Positve line offset */
01052          if (offset > 0) {
01053             int64_t count = 0;
01054             fseek(ff, 0, SEEK_SET);
01055             for (i = 0; i < flength; i += sizeof(fbuf)) {
01056                char *pos;
01057                if (i + sizeof(fbuf) >= flength) {
01058                   memset(fbuf, 0, sizeof(fbuf));
01059                }
01060                if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01061                   ast_log(LOG_ERROR, "Short read?!!\n");
01062                   fclose(ff);
01063                   return -1;
01064                }
01065                for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
01066                   LINE_COUNTER(pos, newline_format, count);
01067 
01068                   if (count == offset) {
01069                      offset_offset = i + (pos - fbuf) + 1;
01070                      break;
01071                   }
01072                }
01073                if (offset_offset >= 0) {
01074                   break;
01075                }
01076             }
01077          }
01078 
01079          if (offset_offset < 0) {
01080             ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset);
01081             fclose(ff);
01082             return -1;
01083          }
01084 
01085          if (length == 0) {
01086             length_offset = offset_offset;
01087          } else if (length == LLONG_MAX) {
01088             length_offset = flength;
01089          }
01090 
01091          /* Positive line length */
01092          if (length_offset < 0) {
01093             fseeko(ff, offset_offset, SEEK_SET);
01094             for (i = offset_offset; i < flength; i += sizeof(fbuf)) {
01095                char *pos;
01096                if (i + sizeof(fbuf) >= flength) {
01097                   memset(fbuf, 0, sizeof(fbuf));
01098                }
01099                if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01100                   ast_log(LOG_ERROR, "Short read?!!\n");
01101                   fclose(ff);
01102                   return -1;
01103                }
01104                for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) {
01105                   LINE_COUNTER(pos, newline_format, current_length);
01106 
01107                   if (current_length == length) {
01108                      length_offset = i + (pos - fbuf) + 1;
01109                      break;
01110                   }
01111                }
01112                if (length_offset >= 0) {
01113                   break;
01114                }
01115             }
01116             if (length_offset < 0) {
01117                /* Exceeds length of file */
01118                ast_debug(3, "Exceeds length of file? length=%" PRId64 ", count=%" PRId64 ", flength=%" PRId64 "\n", length, current_length, flength);
01119                length_offset = flength;
01120             }
01121          }
01122 
01123          /* Have offset_offset and length_offset now */
01124          if (length_offset - offset_offset == vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
01125             /* Simple case - replacement of text inline */
01126             fseeko(ff, offset_offset, SEEK_SET);
01127             if (fwrite(value, 1, vlength, ff) < vlength) {
01128                ast_log(LOG_ERROR, "Short write?!!\n");
01129             } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
01130                ast_log(LOG_ERROR, "Short write?!!\n");
01131             }
01132             fclose(ff);
01133          } else if (length_offset - offset_offset > vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) {
01134             /* More complex case - need to shorten file */
01135             off_t cur;
01136             int64_t length_length = length_offset - offset_offset;
01137             size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
01138 
01139             ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 " (%" PRId64 "), vlength=%" PRId64 ", flength=%" PRId64 "\n",
01140                args.offset, offset_offset, args.length, length_offset, length_length, vlength, flength);
01141 
01142             fseeko(ff, offset_offset, SEEK_SET);
01143             if (fwrite(value, 1, vlength, ff) < vlength) {
01144                ast_log(LOG_ERROR, "Short write?!!\n");
01145                fclose(ff);
01146                return -1;
01147             } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, vlen - vlength, ff) < vlen - vlength) {
01148                ast_log(LOG_ERROR, "Short write?!!\n");
01149                fclose(ff);
01150                return -1;
01151             }
01152             while ((cur = ftello(ff)) < flength) {
01153                if (cur < 0) {
01154                   ast_log(AST_LOG_ERROR, "Unable to determine last write position for '%s': %s\n", args.filename, strerror(errno));
01155                   fclose(ff);
01156                   return -1;
01157                }
01158                fseeko(ff, length_length - vlen, SEEK_CUR);
01159                if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01160                   ast_log(LOG_ERROR, "Short read?!!\n");
01161                   fclose(ff);
01162                   return -1;
01163                }
01164                /* Seek to where we last stopped writing */
01165                fseeko(ff, cur, SEEK_SET);
01166                if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01167                   ast_log(LOG_ERROR, "Short write?!!\n");
01168                   fclose(ff);
01169                   return -1;
01170                }
01171             }
01172             fclose(ff);
01173             if (truncate(args.filename, flength - (length_length - vlen))) {
01174                ast_log(LOG_ERROR, "Truncation of file failed: %s\n", strerror(errno));
01175             }
01176          } else {
01177             /* Most complex case - need to lengthen file */
01178             size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)));
01179             int64_t origlen = length_offset - offset_offset;
01180             off_t lastwritten = flength + vlen - origlen;
01181 
01182             ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n",
01183                args.offset, offset_offset, args.length, length_offset, vlength, flength);
01184 
01185             fseeko(ff, flength - sizeof(fbuf), SEEK_SET);
01186             while (offset_offset + sizeof(fbuf) < ftello(ff)) {
01187                if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01188                   ast_log(LOG_ERROR, "Short read?!!\n");
01189                   fclose(ff);
01190                   return -1;
01191                }
01192                fseeko(ff, sizeof(fbuf) - vlen - origlen, SEEK_CUR);
01193                if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) {
01194                   ast_log(LOG_ERROR, "Short write?!!\n");
01195                   fclose(ff);
01196                   return -1;
01197                }
01198                if ((lastwritten = ftello(ff) - sizeof(fbuf)) < offset_offset + sizeof(fbuf)) {
01199                   break;
01200                }
01201                fseeko(ff, 2 * sizeof(fbuf) + vlen - origlen, SEEK_CUR);
01202             }
01203             fseek(ff, length_offset, SEEK_SET);
01204             if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) {
01205                ast_log(LOG_ERROR, "Short read?!!\n");
01206                fclose(ff);
01207                return -1;
01208             }
01209             fseek(ff, offset_offset, SEEK_SET);
01210             if (fwrite(value, 1, vlength, ff) < vlength) {
01211                ast_log(LOG_ERROR, "Short write?!!\n");
01212                fclose(ff);
01213                return -1;
01214             } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) {
01215                ast_log(LOG_ERROR, "Short write?!!\n");
01216                fclose(ff);
01217                return -1;
01218             } else {
01219                off_t curpos = ftello(ff);
01220                foplen = lastwritten - curpos;
01221                if (fwrite(fbuf, 1, foplen, ff) < foplen) {
01222                   ast_log(LOG_ERROR, "Short write?!!\n");
01223                }
01224             }
01225             fclose(ff);
01226          }
01227       }
01228    }
01229 
01230    return 0;
01231 }
01232 
01233 static struct ast_custom_function env_function = {
01234    .name = "ENV",
01235    .read = env_read,
01236    .write = env_write
01237 };
01238 
01239 static struct ast_custom_function stat_function = {
01240    .name = "STAT",
01241    .read = stat_read,
01242    .read_max = 12,
01243 };
01244 
01245 static struct ast_custom_function file_function = {
01246    .name = "FILE",
01247    .read2 = file_read,
01248    .write = file_write,
01249 };
01250 
01251 static struct ast_custom_function file_count_line_function = {
01252    .name = "FILE_COUNT_LINE",
01253    .read2 = file_count_line,
01254    .read_max = 12,
01255 };
01256 
01257 static struct ast_custom_function file_format_function = {
01258    .name = "FILE_FORMAT",
01259    .read2 = file_format,
01260    .read_max = 2,
01261 };
01262 
01263 static int unload_module(void)
01264 {
01265    int res = 0;
01266 
01267    res |= ast_custom_function_unregister(&env_function);
01268    res |= ast_custom_function_unregister(&stat_function);
01269    res |= ast_custom_function_unregister(&file_function);
01270    res |= ast_custom_function_unregister(&file_count_line_function);
01271    res |= ast_custom_function_unregister(&file_format_function);
01272 
01273    return res;
01274 }
01275 
01276 static int load_module(void)
01277 {
01278    int res = 0;
01279 
01280    res |= ast_custom_function_register(&env_function);
01281    res |= ast_custom_function_register_escalating(&stat_function, AST_CFE_READ);
01282    res |= ast_custom_function_register_escalating(&file_function, AST_CFE_BOTH);
01283    res |= ast_custom_function_register_escalating(&file_count_line_function, AST_CFE_READ);
01284    res |= ast_custom_function_register_escalating(&file_format_function, AST_CFE_READ);
01285 
01286    return res;
01287 }
01288 
01289 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Environment/filesystem dialplan functions");

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1