#include "asterisk.h"
#include <sys/stat.h>
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"
#include "asterisk/file.h"

Go to the source code of this file.
Defines | |
| #define | LINE_COUNTER(cptr, term, counter) |
Enumerations | |
| enum | file_format { FF_UNKNOWN = -1, FF_UNIX, FF_DOS, FF_MAC } |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int64_t | count_lines (const char *filename, enum file_format newline_format) |
| static int | env_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
| static int | env_write (struct ast_channel *chan, const char *cmd, char *data, const char *value) |
| static enum file_format | file2format (const char *filename) |
| static int | file_count_line (struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) |
| static int | file_format (struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) |
| static int | file_read (struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) |
| static int | file_write (struct ast_channel *chan, const char *cmd, char *data, const char *value) |
| const char * | format2term (enum file_format f) |
| static int | load_module (void) |
| static int | stat_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Environment/filesystem dialplan functions" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static struct ast_custom_function | env_function |
| static struct ast_custom_function | file_count_line_function |
| static struct ast_custom_function | file_format_function |
| static struct ast_custom_function | file_function |
| static struct ast_custom_function | stat_function |
Definition in file func_env.c.
| #define LINE_COUNTER | ( | cptr, | |||
| term, | |||||
| counter | ) |
| enum file_format |
Definition at line 300 of file func_env.c.
00300 { 00301 FF_UNKNOWN = -1, 00302 FF_UNIX, 00303 FF_DOS, 00304 FF_MAC, 00305 };
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1270 of file func_env.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1270 of file func_env.c.
| static int64_t count_lines | ( | const char * | filename, | |
| enum file_format | newline_format | |||
| ) | [static] |
Definition at line 307 of file func_env.c.
References ast_log(), errno, FF_DOS, FF_MAC, FF_UNIX, FF_UNKNOWN, and LOG_ERROR.
Referenced by file_count_line().
00308 { 00309 int count = 0; 00310 char fbuf[4096]; 00311 FILE *ff; 00312 00313 if (!(ff = fopen(filename, "r"))) { 00314 ast_log(LOG_ERROR, "Unable to open '%s': %s\n", filename, strerror(errno)); 00315 return -1; 00316 } 00317 00318 while (fgets(fbuf, sizeof(fbuf), ff)) { 00319 char *next = fbuf, *first_cr = NULL, *first_nl = NULL; 00320 00321 /* Must do it this way, because if the fileformat is FF_MAC, then Unix 00322 * assumptions about line-format will not come into play. */ 00323 while (next) { 00324 if (newline_format == FF_DOS || newline_format == FF_MAC || newline_format == FF_UNKNOWN) { 00325 first_cr = strchr(next, '\r'); 00326 } 00327 if (newline_format == FF_UNIX || newline_format == FF_UNKNOWN) { 00328 first_nl = strchr(next, '\n'); 00329 } 00330 00331 /* No terminators found in buffer */ 00332 if (!first_cr && !first_nl) { 00333 break; 00334 } 00335 00336 if (newline_format == FF_UNKNOWN) { 00337 if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) { 00338 if (first_nl && first_nl == first_cr + 1) { 00339 newline_format = FF_DOS; 00340 } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) { 00341 /* Get it on the next pass */ 00342 fseek(ff, -1, SEEK_CUR); 00343 break; 00344 } else { 00345 newline_format = FF_MAC; 00346 first_nl = NULL; 00347 } 00348 } else { 00349 newline_format = FF_UNIX; 00350 first_cr = NULL; 00351 } 00352 /* Jump down into next section */ 00353 } 00354 00355 if (newline_format == FF_DOS) { 00356 if (first_nl && first_cr && first_nl == first_cr + 1) { 00357 next = first_nl + 1; 00358 count++; 00359 } else if (first_cr == &fbuf[sizeof(fbuf) - 2]) { 00360 /* Get it on the next pass */ 00361 fseek(ff, -1, SEEK_CUR); 00362 break; 00363 } 00364 } else if (newline_format == FF_MAC) { 00365 if (first_cr) { 00366 next = first_cr + 1; 00367 count++; 00368 } 00369 } else if (newline_format == FF_UNIX) { 00370 if (first_nl) { 00371 next = first_nl + 1; 00372 count++; 00373 } 00374 } 00375 } 00376 } 00377 fclose(ff); 00378 00379 return count; 00380 }
| static int env_read | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| char * | buf, | |||
| size_t | len | |||
| ) | [static] |
Definition at line 227 of file func_env.c.
References ast_copy_string().
00229 { 00230 char *ret = NULL; 00231 00232 *buf = '\0'; 00233 00234 if (data) 00235 ret = getenv(data); 00236 00237 if (ret) 00238 ast_copy_string(buf, ret, len); 00239 00240 return 0; 00241 }
| static int env_write | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| const char * | value | |||
| ) | [static] |
Definition at line 243 of file func_env.c.
References ast_strlen_zero(), setenv(), and unsetenv().
00245 { 00246 if (!ast_strlen_zero(data) && strncmp(data, "AST_", 4)) { 00247 if (!ast_strlen_zero(value)) { 00248 setenv(data, value, 1); 00249 } else { 00250 unsetenv(data); 00251 } 00252 } 00253 00254 return 0; 00255 }
| static enum file_format file2format | ( | const char * | filename | ) | [static] |
Definition at line 421 of file func_env.c.
References ast_log(), errno, FF_DOS, FF_MAC, FF_UNIX, FF_UNKNOWN, and LOG_ERROR.
Referenced by file_format(), file_read(), and file_write().
00422 { 00423 FILE *ff; 00424 char fbuf[4096]; 00425 char *first_cr, *first_nl; 00426 enum file_format newline_format = FF_UNKNOWN; 00427 00428 if (!(ff = fopen(filename, "r"))) { 00429 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", filename, strerror(errno)); 00430 return -1; 00431 } 00432 00433 while (fgets(fbuf, sizeof(fbuf), ff)) { 00434 first_cr = strchr(fbuf, '\r'); 00435 first_nl = strchr(fbuf, '\n'); 00436 00437 if (!first_cr && !first_nl) { 00438 continue; 00439 } 00440 00441 if ((first_cr && !first_nl) || (first_cr && first_cr < first_nl)) { 00442 00443 if (first_nl && first_nl == first_cr + 1) { 00444 newline_format = FF_DOS; 00445 } else if (first_cr && first_cr == &fbuf[sizeof(fbuf) - 2]) { 00446 /* Edge case: get it on the next pass */ 00447 fseek(ff, -1, SEEK_CUR); 00448 continue; 00449 } else { 00450 newline_format = FF_MAC; 00451 } 00452 } else { 00453 newline_format = FF_UNIX; 00454 } 00455 break; 00456 } 00457 fclose(ff); 00458 return newline_format; 00459 }
| static int file_count_line | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| struct ast_str ** | buf, | |||
| ssize_t | len | |||
| ) | [static] |
Definition at line 382 of file func_env.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_STANDARD_APP_ARGS, ast_str_set(), count_lines(), FF_DOS, FF_MAC, FF_UNIX, FF_UNKNOWN, and format.
00383 { 00384 enum file_format newline_format = FF_UNKNOWN; 00385 int64_t count; 00386 AST_DECLARE_APP_ARGS(args, 00387 AST_APP_ARG(filename); 00388 AST_APP_ARG(format); 00389 ); 00390 00391 AST_STANDARD_APP_ARGS(args, data); 00392 if (args.argc > 1) { 00393 if (tolower(args.format[0]) == 'd') { 00394 newline_format = FF_DOS; 00395 } else if (tolower(args.format[0]) == 'm') { 00396 newline_format = FF_MAC; 00397 } else if (tolower(args.format[0]) == 'u') { 00398 newline_format = FF_UNIX; 00399 } 00400 } 00401 00402 count = count_lines(args.filename, newline_format); 00403 ast_str_set(buf, len, "%" PRId64, count); 00404 return 0; 00405 }
| static int file_format | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| struct ast_str ** | buf, | |||
| ssize_t | len | |||
| ) | [static] |
Definition at line 461 of file func_env.c.
References ast_str_set(), FF_DOS, FF_MAC, FF_UNIX, and file2format().
00462 { 00463 enum file_format newline_format = file2format(data); 00464 ast_str_set(buf, len, "%c", newline_format == FF_UNIX ? 'u' : newline_format == FF_DOS ? 'd' : newline_format == FF_MAC ? 'm' : 'x'); 00465 return 0; 00466 }
| static int file_read | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| struct ast_str ** | buf, | |||
| ssize_t | len | |||
| ) | [static] |
Definition at line 468 of file func_env.c.
References args, AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), AST_LOG_ERROR, AST_STANDARD_APP_ARGS, ast_str_append_substr(), ast_str_reset(), errno, FF_DOS, FF_MAC, FF_UNIX, FF_UNKNOWN, file2format(), format, LINE_COUNTER, LLONG_MAX, LOG_ERROR, and LOG_WARNING.
00469 { 00470 FILE *ff; 00471 int64_t offset = 0, length = LLONG_MAX; 00472 enum file_format format = FF_UNKNOWN; 00473 char fbuf[4096]; 00474 int64_t flength, i; /* iterator needs to be signed, so it can go negative and terminate the loop */ 00475 int64_t offset_offset = -1, length_offset = -1; 00476 char dos_state = 0; 00477 AST_DECLARE_APP_ARGS(args, 00478 AST_APP_ARG(filename); 00479 AST_APP_ARG(offset); 00480 AST_APP_ARG(length); 00481 AST_APP_ARG(options); 00482 AST_APP_ARG(fileformat); 00483 ); 00484 00485 AST_STANDARD_APP_ARGS(args, data); 00486 00487 if (args.argc > 1) { 00488 sscanf(args.offset, "%" SCNd64, &offset); 00489 } 00490 if (args.argc > 2) { 00491 sscanf(args.length, "%" SCNd64, &length); 00492 } 00493 00494 if (args.argc < 4 || !strchr(args.options, 'l')) { 00495 /* Character-based mode */ 00496 off_t off_i; 00497 00498 if (!(ff = fopen(args.filename, "r"))) { 00499 ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", args.filename, strerror(errno)); 00500 return 0; 00501 } 00502 00503 if (fseeko(ff, 0, SEEK_END) < 0) { 00504 ast_log(LOG_ERROR, "Cannot seek to end of '%s': %s\n", args.filename, strerror(errno)); 00505 fclose(ff); 00506 return -1; 00507 } 00508 flength = ftello(ff); 00509 00510 if (offset < 0) { 00511 fseeko(ff, offset, SEEK_END); 00512 if ((offset = ftello(ff)) < 0) { 00513 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno)); 00514 fclose(ff); 00515 return -1; 00516 } 00517 } 00518 if (length < 0) { 00519 fseeko(ff, length, SEEK_END); 00520 if ((length = ftello(ff)) - offset < 0) { 00521 /* Eliminates all results */ 00522 fclose(ff); 00523 return -1; 00524 } 00525 } else if (length == LLONG_MAX) { 00526 fseeko(ff, 0, SEEK_END); 00527 length = ftello(ff); 00528 } 00529 00530 ast_str_reset(*buf); 00531 00532 fseeko(ff, offset, SEEK_SET); 00533 for (off_i = ftello(ff); off_i < flength && off_i < offset + length; off_i += sizeof(fbuf)) { 00534 /* Calculate if we need to retrieve just a portion of the file in memory */ 00535 size_t toappend = sizeof(fbuf); 00536 00537 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 00538 ast_log(LOG_ERROR, "Short read?!!\n"); 00539 break; 00540 } 00541 00542 /* Don't go past the length requested */ 00543 if (off_i + toappend > offset + length) { 00544 toappend = length - off_i; 00545 } 00546 00547 ast_str_append_substr(buf, len, fbuf, toappend); 00548 } 00549 fclose(ff); 00550 return 0; 00551 } 00552 00553 /* Line-based read */ 00554 if (args.argc == 5) { 00555 if (tolower(args.fileformat[0]) == 'd') { 00556 format = FF_DOS; 00557 } else if (tolower(args.fileformat[0]) == 'm') { 00558 format = FF_MAC; 00559 } else if (tolower(args.fileformat[0]) == 'u') { 00560 format = FF_UNIX; 00561 } 00562 } 00563 00564 if (format == FF_UNKNOWN) { 00565 if ((format = file2format(args.filename)) == FF_UNKNOWN) { 00566 ast_log(LOG_WARNING, "'%s' is not a line-based file\n", args.filename); 00567 return -1; 00568 } 00569 } 00570 00571 if (offset < 0 && length <= offset) { 00572 /* Length eliminates all content */ 00573 return -1; 00574 } else if (offset == 0) { 00575 offset_offset = 0; 00576 } 00577 00578 if (!(ff = fopen(args.filename, "r"))) { 00579 ast_log(LOG_ERROR, "Cannot open '%s': %s\n", args.filename, strerror(errno)); 00580 return -1; 00581 } 00582 00583 if (fseek(ff, 0, SEEK_END)) { 00584 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno)); 00585 fclose(ff); 00586 return -1; 00587 } 00588 00589 flength = ftello(ff); 00590 00591 if (length == LLONG_MAX) { 00592 length_offset = flength; 00593 } 00594 00595 /* For negative offset and/or negative length */ 00596 if (offset < 0 || length < 0) { 00597 int64_t count = 0; 00598 /* Start with an even multiple of fbuf, so at the end of reading with a 00599 * 0 offset, we don't try to go past the beginning of the file. */ 00600 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) { 00601 size_t end; 00602 char *pos; 00603 if (fseeko(ff, i, SEEK_SET)) { 00604 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno)); 00605 } 00606 end = fread(fbuf, 1, sizeof(fbuf), ff); 00607 for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos > fbuf - 1; pos--) { 00608 LINE_COUNTER(pos, format, count); 00609 00610 if (length < 0 && count * -1 == length) { 00611 length_offset = i + (pos - fbuf); 00612 } else if (offset < 0 && count * -1 == (offset - 1)) { 00613 /* Found our initial offset. We're done with reverse motion! */ 00614 if (format == FF_DOS) { 00615 offset_offset = i + (pos - fbuf) + 2; 00616 } else { 00617 offset_offset = i + (pos - fbuf) + 1; 00618 } 00619 break; 00620 } 00621 } 00622 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) { 00623 break; 00624 } 00625 } 00626 /* We're at the beginning, and the negative offset indicates the exact number of lines in the file */ 00627 if (offset < 0 && offset_offset < 0 && offset == count * -1) { 00628 offset_offset = 0; 00629 } 00630 } 00631 00632 /* Positve line offset */ 00633 if (offset > 0) { 00634 int64_t count = 0; 00635 fseek(ff, 0, SEEK_SET); 00636 for (i = 0; i < flength; i += sizeof(fbuf)) { 00637 char *pos; 00638 if (i + sizeof(fbuf) <= flength) { 00639 /* Don't let previous values influence current counts, due to short reads */ 00640 memset(fbuf, 0, sizeof(fbuf)); 00641 } 00642 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 00643 ast_log(LOG_ERROR, "Short read?!!\n"); 00644 fclose(ff); 00645 return -1; 00646 } 00647 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) { 00648 LINE_COUNTER(pos, format, count); 00649 00650 if (count == offset) { 00651 offset_offset = i + (pos - fbuf) + 1; 00652 break; 00653 } 00654 } 00655 if (offset_offset >= 0) { 00656 break; 00657 } 00658 } 00659 } 00660 00661 if (offset_offset < 0) { 00662 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset); 00663 fclose(ff); 00664 return -1; 00665 } 00666 00667 ast_str_reset(*buf); 00668 if (fseeko(ff, offset_offset, SEEK_SET)) { 00669 ast_log(LOG_ERROR, "fseeko failed: %s\n", strerror(errno)); 00670 } 00671 00672 /* If we have both offset_offset and length_offset, then grabbing the 00673 * buffer is simply a matter of just retrieving the file and adding it 00674 * to buf. Otherwise, we need to run byte-by-byte forward until the 00675 * length is complete. */ 00676 if (length_offset >= 0) { 00677 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset); 00678 for (i = offset_offset; i < length_offset; i += sizeof(fbuf)) { 00679 if (fread(fbuf, 1, i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf), ff) < (i + sizeof(fbuf) > flength ? flength - i : sizeof(fbuf))) { 00680 ast_log(LOG_ERROR, "Short read?!!\n"); 00681 } 00682 ast_debug(3, "Appending first %" PRId64" bytes of fbuf=%s\n", i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf), fbuf); 00683 ast_str_append_substr(buf, len, fbuf, i + sizeof(fbuf) > length_offset ? length_offset - i : sizeof(fbuf)); 00684 } 00685 } else if (length == 0) { 00686 /* Nothing to do */ 00687 } else { 00688 /* Positive line offset */ 00689 int64_t current_length = 0; 00690 char dos_state = 0; 00691 ast_debug(3, "offset=%" PRId64 ", length=%" PRId64 ", offset_offset=%" PRId64 ", length_offset=%" PRId64 "\n", offset, length, offset_offset, length_offset); 00692 for (i = offset_offset; i < flength; i += sizeof(fbuf)) { 00693 char *pos; 00694 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 00695 ast_log(LOG_ERROR, "Short read?!!\n"); 00696 fclose(ff); 00697 return -1; 00698 } 00699 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) { 00700 LINE_COUNTER(pos, format, current_length); 00701 00702 if (current_length == length) { 00703 length_offset = i + (pos - fbuf) + 1; 00704 break; 00705 } 00706 } 00707 ast_debug(3, "length_offset=%" PRId64 ", length_offset - i=%" PRId64 "\n", length_offset, length_offset - i); 00708 ast_str_append_substr(buf, len, fbuf, length_offset >= 0 ? length_offset - i : flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i; 00709 00710 if (length_offset >= 0) { 00711 break; 00712 } 00713 } 00714 } 00715 00716 fclose(ff); 00717 return 0; 00718 }
| static int file_write | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| const char * | value | |||
| ) | [static] |
Definition at line 727 of file func_env.c.
References args, AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), AST_LOG_ERROR, AST_STANDARD_APP_ARGS, errno, FF_DOS, FF_MAC, FF_UNIX, FF_UNKNOWN, file2format(), format, format2term(), LINE_COUNTER, LLONG_MAX, LOG_ERROR, LOG_WARNING, and S_OR.
00728 { 00729 AST_DECLARE_APP_ARGS(args, 00730 AST_APP_ARG(filename); 00731 AST_APP_ARG(offset); 00732 AST_APP_ARG(length); 00733 AST_APP_ARG(options); 00734 AST_APP_ARG(format); 00735 ); 00736 int64_t offset = 0, length = LLONG_MAX; 00737 off_t flength, vlength; 00738 size_t foplen = 0; 00739 FILE *ff; 00740 00741 AST_STANDARD_APP_ARGS(args, data); 00742 00743 if (args.argc > 1) { 00744 sscanf(args.offset, "%" SCNd64, &offset); 00745 } 00746 if (args.argc > 2) { 00747 sscanf(args.length, "%" SCNd64, &length); 00748 } 00749 00750 vlength = strlen(value); 00751 00752 if (args.argc < 4 || !strchr(args.options, 'l')) { 00753 /* Character-based mode */ 00754 00755 if (args.argc > 3 && strchr(args.options, 'a')) { 00756 /* Append mode */ 00757 if (!(ff = fopen(args.filename, "a"))) { 00758 ast_log(LOG_WARNING, "Cannot open file '%s' for appending: %s\n", args.filename, strerror(errno)); 00759 return 0; 00760 } 00761 if (fwrite(value, 1, vlength, ff) < vlength) { 00762 ast_log(LOG_ERROR, "Short write?!!\n"); 00763 } 00764 fclose(ff); 00765 return 0; 00766 } else if (offset == 0 && length == LLONG_MAX) { 00767 if (!(ff = fopen(args.filename, "w"))) { 00768 ast_log(LOG_WARNING, "Cannot open file '%s' for writing: %s\n", args.filename, strerror(errno)); 00769 return 0; 00770 } 00771 if (fwrite(value, 1, vlength, ff) < vlength) { 00772 ast_log(LOG_ERROR, "Short write?!!\n"); 00773 } 00774 fclose(ff); 00775 return 0; 00776 } 00777 00778 if (!(ff = fopen(args.filename, "r+"))) { 00779 ast_log(LOG_WARNING, "Cannot open file '%s' for modification: %s\n", args.filename, strerror(errno)); 00780 return 0; 00781 } 00782 fseeko(ff, 0, SEEK_END); 00783 flength = ftello(ff); 00784 00785 if (offset < 0) { 00786 if (fseeko(ff, offset, SEEK_END)) { 00787 ast_log(LOG_ERROR, "Cannot seek to offset of '%s': %s\n", args.filename, strerror(errno)); 00788 fclose(ff); 00789 return -1; 00790 } 00791 if ((offset = ftello(ff)) < 0) { 00792 ast_log(AST_LOG_ERROR, "Cannot determine offset position of '%s': %s\n", args.filename, strerror(errno)); 00793 fclose(ff); 00794 return -1; 00795 } 00796 } 00797 00798 if (length < 0) { 00799 length = flength - offset + length; 00800 if (length < 0) { 00801 ast_log(LOG_ERROR, "Length '%s' exceeds the file length. No data will be written.\n", args.length); 00802 fclose(ff); 00803 return -1; 00804 } 00805 } 00806 00807 fseeko(ff, offset, SEEK_SET); 00808 00809 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n", 00810 S_OR(args.offset, "(null)"), offset, S_OR(args.length, "(null)"), length, vlength, flength); 00811 00812 if (length == vlength) { 00813 /* Simplest case, a straight replace */ 00814 if (fwrite(value, 1, vlength, ff) < vlength) { 00815 ast_log(LOG_ERROR, "Short write?!!\n"); 00816 } 00817 fclose(ff); 00818 } else if (length == LLONG_MAX) { 00819 /* Simple truncation */ 00820 if (fwrite(value, 1, vlength, ff) < vlength) { 00821 ast_log(LOG_ERROR, "Short write?!!\n"); 00822 } 00823 fclose(ff); 00824 if (truncate(args.filename, offset + vlength)) { 00825 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno)); 00826 } 00827 } else if (length > vlength) { 00828 /* More complex -- need to close a gap */ 00829 char fbuf[4096]; 00830 off_t cur; 00831 if (fwrite(value, 1, vlength, ff) < vlength) { 00832 ast_log(LOG_ERROR, "Short write?!!\n"); 00833 } 00834 fseeko(ff, length - vlength, SEEK_CUR); 00835 while ((cur = ftello(ff)) < flength) { 00836 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 00837 ast_log(LOG_ERROR, "Short read?!!\n"); 00838 } 00839 fseeko(ff, cur + vlength - length, SEEK_SET); 00840 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) { 00841 ast_log(LOG_ERROR, "Short write?!!\n"); 00842 } 00843 /* Seek to where we stopped reading */ 00844 if (fseeko(ff, cur + sizeof(fbuf), SEEK_SET) < 0) { 00845 /* Only reason for seek to fail is EOF */ 00846 break; 00847 } 00848 } 00849 fclose(ff); 00850 if (truncate(args.filename, flength - (length - vlength))) { 00851 ast_log(LOG_ERROR, "Unable to truncate the file: %s\n", strerror(errno)); 00852 } 00853 } else { 00854 /* Most complex -- need to open a gap */ 00855 char fbuf[4096]; 00856 off_t lastwritten = flength + vlength - length; 00857 00858 /* Start reading exactly the buffer size back from the end. */ 00859 fseeko(ff, flength - sizeof(fbuf), SEEK_SET); 00860 while (offset < ftello(ff)) { 00861 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) { 00862 ast_log(LOG_ERROR, "Short read?!!\n"); 00863 fclose(ff); 00864 return -1; 00865 } 00866 /* Since the read moved our file ptr forward, we reverse, but 00867 * seek an offset equal to the amount we want to extend the 00868 * file by */ 00869 fseeko(ff, vlength - length - sizeof(fbuf), SEEK_CUR); 00870 00871 /* Note the location of this buffer -- we must not overwrite this position. */ 00872 lastwritten = ftello(ff); 00873 00874 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) { 00875 ast_log(LOG_ERROR, "Short write?!!\n"); 00876 fclose(ff); 00877 return -1; 00878 } 00879 00880 if (lastwritten < offset + sizeof(fbuf)) { 00881 break; 00882 } 00883 /* Our file pointer is now either pointing to the end of the 00884 * file (new position) or a multiple of the fbuf size back from 00885 * that point. Move back to where we want to start reading 00886 * again. We never actually try to read beyond the end of the 00887 * file, so we don't have do deal with short reads, as we would 00888 * when we're shortening the file. */ 00889 fseeko(ff, 2 * sizeof(fbuf) + vlength - length, SEEK_CUR); 00890 } 00891 00892 /* Last part of the file that we need to preserve */ 00893 if (fseeko(ff, offset + length, SEEK_SET)) { 00894 ast_log(LOG_WARNING, "Unable to seek to %" PRId64 " + %" PRId64 " != %" PRId64 "?)\n", offset, length, ftello(ff)); 00895 } 00896 00897 /* Doesn't matter how much we read -- just need to restrict the write */ 00898 ast_debug(1, "Reading at %" PRId64 "\n", ftello(ff)); 00899 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 00900 ast_log(LOG_ERROR, "Short read?!!\n"); 00901 } 00902 fseek(ff, offset, SEEK_SET); 00903 /* Write out the value, then write just up until where we last moved some data */ 00904 if (fwrite(value, 1, vlength, ff) < vlength) { 00905 ast_log(LOG_ERROR, "Short write?!!\n"); 00906 } else { 00907 off_t curpos = ftello(ff); 00908 foplen = lastwritten - curpos; 00909 if (fwrite(fbuf, 1, foplen, ff) < foplen) { 00910 ast_log(LOG_ERROR, "Short write?!!\n"); 00911 } 00912 } 00913 fclose(ff); 00914 } 00915 } else { 00916 enum file_format newline_format = FF_UNKNOWN; 00917 00918 /* Line mode */ 00919 if (args.argc == 5) { 00920 if (tolower(args.format[0]) == 'u') { 00921 newline_format = FF_UNIX; 00922 } else if (tolower(args.format[0]) == 'm') { 00923 newline_format = FF_MAC; 00924 } else if (tolower(args.format[0]) == 'd') { 00925 newline_format = FF_DOS; 00926 } 00927 } 00928 if (newline_format == FF_UNKNOWN && (newline_format = file2format(args.filename)) == FF_UNKNOWN) { 00929 ast_log(LOG_ERROR, "File '%s' not in line format\n", args.filename); 00930 return -1; 00931 } 00932 00933 if (strchr(args.options, 'a')) { 00934 /* Append to file */ 00935 if (!(ff = fopen(args.filename, "a"))) { 00936 ast_log(LOG_ERROR, "Unable to open '%s' for appending: %s\n", args.filename, strerror(errno)); 00937 return -1; 00938 } 00939 if (fwrite(value, 1, vlength, ff) < vlength) { 00940 ast_log(LOG_ERROR, "Short write?!!\n"); 00941 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) { 00942 ast_log(LOG_ERROR, "Short write?!!\n"); 00943 } 00944 fclose(ff); 00945 } else if (offset == 0 && length == LLONG_MAX) { 00946 /* Overwrite file */ 00947 off_t truncsize; 00948 if (!(ff = fopen(args.filename, "w"))) { 00949 ast_log(LOG_ERROR, "Unable to open '%s' for writing: %s\n", args.filename, strerror(errno)); 00950 return -1; 00951 } 00952 if (fwrite(value, 1, vlength, ff) < vlength) { 00953 ast_log(LOG_ERROR, "Short write?!!\n"); 00954 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) { 00955 ast_log(LOG_ERROR, "Short write?!!\n"); 00956 } 00957 if ((truncsize = ftello(ff)) < 0) { 00958 ast_log(AST_LOG_ERROR, "Unable to determine truncate position of '%s': %s\n", args.filename, strerror(errno)); 00959 } 00960 fclose(ff); 00961 if (truncsize >= 0 && truncate(args.filename, truncsize)) { 00962 ast_log(LOG_ERROR, "Unable to truncate file '%s': %s\n", args.filename, strerror(errno)); 00963 return -1; 00964 } 00965 } else { 00966 int64_t offset_offset = (offset == 0 ? 0 : -1), length_offset = -1, flength, i, current_length = 0; 00967 char dos_state = 0, fbuf[4096]; 00968 00969 if (offset < 0 && length < offset) { 00970 /* Nonsense! */ 00971 ast_log(LOG_ERROR, "Length cannot specify a position prior to the offset\n"); 00972 return -1; 00973 } 00974 00975 if (!(ff = fopen(args.filename, "r+"))) { 00976 ast_log(LOG_ERROR, "Cannot open '%s' for modification: %s\n", args.filename, strerror(errno)); 00977 return -1; 00978 } 00979 00980 if (fseek(ff, 0, SEEK_END)) { 00981 ast_log(LOG_ERROR, "Cannot seek to end of file '%s': %s\n", args.filename, strerror(errno)); 00982 fclose(ff); 00983 return -1; 00984 } 00985 if ((flength = ftello(ff)) < 0) { 00986 ast_log(AST_LOG_ERROR, "Cannot determine end position of file '%s': %s\n", args.filename, strerror(errno)); 00987 fclose(ff); 00988 return -1; 00989 } 00990 00991 /* For negative offset and/or negative length */ 00992 if (offset < 0 || length < 0) { 00993 int64_t count = 0; 00994 for (i = (flength / sizeof(fbuf)) * sizeof(fbuf); i >= 0; i -= sizeof(fbuf)) { 00995 char *pos; 00996 if (fseeko(ff, i, SEEK_SET)) { 00997 ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno)); 00998 } 00999 if (i + sizeof(fbuf) >= flength) { 01000 memset(fbuf, 0, sizeof(fbuf)); 01001 } 01002 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 01003 ast_log(LOG_ERROR, "Short read: %s\n", strerror(errno)); 01004 fclose(ff); 01005 return -1; 01006 } 01007 for (pos = fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) { 01008 LINE_COUNTER(pos, newline_format, count); 01009 01010 if (length < 0 && count * -1 == length) { 01011 length_offset = i + (pos - fbuf); 01012 } else if (offset < 0 && count * -1 == (offset - 1)) { 01013 /* Found our initial offset. We're done with reverse motion! */ 01014 if (newline_format == FF_DOS) { 01015 offset_offset = i + (pos - fbuf) + 2; 01016 } else { 01017 offset_offset = i + (pos - fbuf) + 1; 01018 } 01019 break; 01020 } 01021 } 01022 if ((offset < 0 && offset_offset >= 0) || (offset >= 0 && length_offset >= 0)) { 01023 break; 01024 } 01025 } 01026 /* We're at the beginning, and the negative offset indicates the exact number of lines in the file */ 01027 if (offset < 0 && offset_offset < 0 && offset == count * -1) { 01028 offset_offset = 0; 01029 } 01030 } 01031 01032 /* Positve line offset */ 01033 if (offset > 0) { 01034 int64_t count = 0; 01035 fseek(ff, 0, SEEK_SET); 01036 for (i = 0; i < flength; i += sizeof(fbuf)) { 01037 char *pos; 01038 if (i + sizeof(fbuf) >= flength) { 01039 memset(fbuf, 0, sizeof(fbuf)); 01040 } 01041 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 01042 ast_log(LOG_ERROR, "Short read?!!\n"); 01043 fclose(ff); 01044 return -1; 01045 } 01046 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) { 01047 LINE_COUNTER(pos, newline_format, count); 01048 01049 if (count == offset) { 01050 offset_offset = i + (pos - fbuf) + 1; 01051 break; 01052 } 01053 } 01054 if (offset_offset >= 0) { 01055 break; 01056 } 01057 } 01058 } 01059 01060 if (offset_offset < 0) { 01061 ast_log(LOG_ERROR, "Offset '%s' refers to before the beginning of the file!\n", args.offset); 01062 fclose(ff); 01063 return -1; 01064 } 01065 01066 if (length == 0) { 01067 length_offset = offset_offset; 01068 } else if (length == LLONG_MAX) { 01069 length_offset = flength; 01070 } 01071 01072 /* Positive line length */ 01073 if (length_offset < 0) { 01074 fseeko(ff, offset_offset, SEEK_SET); 01075 for (i = offset_offset; i < flength; i += sizeof(fbuf)) { 01076 char *pos; 01077 if (i + sizeof(fbuf) >= flength) { 01078 memset(fbuf, 0, sizeof(fbuf)); 01079 } 01080 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 01081 ast_log(LOG_ERROR, "Short read?!!\n"); 01082 fclose(ff); 01083 return -1; 01084 } 01085 for (pos = fbuf; pos < fbuf + sizeof(fbuf); pos++) { 01086 LINE_COUNTER(pos, newline_format, current_length); 01087 01088 if (current_length == length) { 01089 length_offset = i + (pos - fbuf) + 1; 01090 break; 01091 } 01092 } 01093 if (length_offset >= 0) { 01094 break; 01095 } 01096 } 01097 if (length_offset < 0) { 01098 /* Exceeds length of file */ 01099 ast_debug(3, "Exceeds length of file? length=%" PRId64 ", count=%" PRId64 ", flength=%" PRId64 "\n", length, current_length, flength); 01100 length_offset = flength; 01101 } 01102 } 01103 01104 /* Have offset_offset and length_offset now */ 01105 if (length_offset - offset_offset == vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) { 01106 /* Simple case - replacement of text inline */ 01107 fseeko(ff, offset_offset, SEEK_SET); 01108 if (fwrite(value, 1, vlength, ff) < vlength) { 01109 ast_log(LOG_ERROR, "Short write?!!\n"); 01110 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) { 01111 ast_log(LOG_ERROR, "Short write?!!\n"); 01112 } 01113 fclose(ff); 01114 } else if (length_offset - offset_offset > vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format)))) { 01115 /* More complex case - need to shorten file */ 01116 off_t cur; 01117 int64_t length_length = length_offset - offset_offset; 01118 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format))); 01119 01120 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 " (%" PRId64 "), vlength=%" PRId64 ", flength=%" PRId64 "\n", 01121 args.offset, offset_offset, args.length, length_offset, length_length, vlength, flength); 01122 01123 fseeko(ff, offset_offset, SEEK_SET); 01124 if (fwrite(value, 1, vlength, ff) < vlength) { 01125 ast_log(LOG_ERROR, "Short write?!!\n"); 01126 fclose(ff); 01127 return -1; 01128 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, vlen - vlength, ff) < vlen - vlength) { 01129 ast_log(LOG_ERROR, "Short write?!!\n"); 01130 fclose(ff); 01131 return -1; 01132 } 01133 while ((cur = ftello(ff)) < flength) { 01134 if (cur < 0) { 01135 ast_log(AST_LOG_ERROR, "Unable to determine last write position for '%s': %s\n", args.filename, strerror(errno)); 01136 fclose(ff); 01137 return -1; 01138 } 01139 fseeko(ff, length_length - vlen, SEEK_CUR); 01140 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 01141 ast_log(LOG_ERROR, "Short read?!!\n"); 01142 fclose(ff); 01143 return -1; 01144 } 01145 /* Seek to where we last stopped writing */ 01146 fseeko(ff, cur, SEEK_SET); 01147 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) { 01148 ast_log(LOG_ERROR, "Short write?!!\n"); 01149 fclose(ff); 01150 return -1; 01151 } 01152 } 01153 fclose(ff); 01154 if (truncate(args.filename, flength - (length_length - vlen))) { 01155 ast_log(LOG_ERROR, "Truncation of file failed: %s\n", strerror(errno)); 01156 } 01157 } else { 01158 /* Most complex case - need to lengthen file */ 01159 size_t vlen = vlength + (strchr(args.options, 'd') ? 0 : strlen(format2term(newline_format))); 01160 int64_t origlen = length_offset - offset_offset; 01161 off_t lastwritten = flength + vlen - origlen; 01162 01163 ast_debug(3, "offset=%s/%" PRId64 ", length=%s/%" PRId64 ", vlength=%" PRId64 ", flength=%" PRId64 "\n", 01164 args.offset, offset_offset, args.length, length_offset, vlength, flength); 01165 01166 fseeko(ff, flength - sizeof(fbuf), SEEK_SET); 01167 while (offset_offset + sizeof(fbuf) < ftello(ff)) { 01168 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) { 01169 ast_log(LOG_ERROR, "Short read?!!\n"); 01170 fclose(ff); 01171 return -1; 01172 } 01173 fseeko(ff, sizeof(fbuf) - vlen - origlen, SEEK_CUR); 01174 if (fwrite(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf)) { 01175 ast_log(LOG_ERROR, "Short write?!!\n"); 01176 fclose(ff); 01177 return -1; 01178 } 01179 if ((lastwritten = ftello(ff) - sizeof(fbuf)) < offset_offset + sizeof(fbuf)) { 01180 break; 01181 } 01182 fseeko(ff, 2 * sizeof(fbuf) + vlen - origlen, SEEK_CUR); 01183 } 01184 fseek(ff, length_offset, SEEK_SET); 01185 if (fread(fbuf, 1, sizeof(fbuf), ff) < sizeof(fbuf) && !feof(ff)) { 01186 ast_log(LOG_ERROR, "Short read?!!\n"); 01187 fclose(ff); 01188 return -1; 01189 } 01190 fseek(ff, offset_offset, SEEK_SET); 01191 if (fwrite(value, 1, vlength, ff) < vlength) { 01192 ast_log(LOG_ERROR, "Short write?!!\n"); 01193 fclose(ff); 01194 return -1; 01195 } else if (!strchr(args.options, 'd') && fwrite(format2term(newline_format), 1, strlen(format2term(newline_format)), ff) < strlen(format2term(newline_format))) { 01196 ast_log(LOG_ERROR, "Short write?!!\n"); 01197 fclose(ff); 01198 return -1; 01199 } else { 01200 off_t curpos = ftello(ff); 01201 foplen = lastwritten - curpos; 01202 if (fwrite(fbuf, 1, foplen, ff) < foplen) { 01203 ast_log(LOG_ERROR, "Short write?!!\n"); 01204 } 01205 } 01206 fclose(ff); 01207 } 01208 } 01209 } 01210 01211 return 0; 01212 }
| const char * format2term | ( | enum file_format | f | ) |
Definition at line 721 of file func_env.c.
Referenced by file_write().
00722 { 00723 const char *term[] = { "", "\n", "\r\n", "\r" }; 00724 return term[f + 1]; 00725 }
| static int load_module | ( | void | ) | [static] |
Definition at line 1257 of file func_env.c.
References ast_custom_function_register.
01258 { 01259 int res = 0; 01260 01261 res |= ast_custom_function_register(&env_function); 01262 res |= ast_custom_function_register(&stat_function); 01263 res |= ast_custom_function_register(&file_function); 01264 res |= ast_custom_function_register(&file_count_line_function); 01265 res |= ast_custom_function_register(&file_format_function); 01266 01267 return res; 01268 }
| static int stat_read | ( | struct ast_channel * | chan, | |
| const char * | cmd, | |||
| char * | data, | |||
| char * | buf, | |||
| size_t | len | |||
| ) | [static] |
Definition at line 257 of file func_env.c.
References ast_copy_string(), and strsep().
00259 { 00260 char *action; 00261 struct stat s; 00262 00263 ast_copy_string(buf, "0", len); 00264 00265 action = strsep(&data, ","); 00266 if (stat(data, &s)) { 00267 return 0; 00268 } else { 00269 switch (*action) { 00270 case 'e': 00271 strcpy(buf, "1"); 00272 break; 00273 case 's': 00274 snprintf(buf, len, "%d", (unsigned int) s.st_size); 00275 break; 00276 case 'f': 00277 snprintf(buf, len, "%d", S_ISREG(s.st_mode) ? 1 : 0); 00278 break; 00279 case 'd': 00280 snprintf(buf, len, "%d", S_ISDIR(s.st_mode) ? 1 : 0); 00281 break; 00282 case 'M': 00283 snprintf(buf, len, "%d", (int) s.st_mtime); 00284 break; 00285 case 'A': 00286 snprintf(buf, len, "%d", (int) s.st_mtime); 00287 break; 00288 case 'C': 00289 snprintf(buf, len, "%d", (int) s.st_ctime); 00290 break; 00291 case 'm': 00292 snprintf(buf, len, "%o", (int) s.st_mode); 00293 break; 00294 } 00295 } 00296 00297 return 0; 00298 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 1244 of file func_env.c.
References ast_custom_function_unregister().
01245 { 01246 int res = 0; 01247 01248 res |= ast_custom_function_unregister(&env_function); 01249 res |= ast_custom_function_unregister(&stat_function); 01250 res |= ast_custom_function_unregister(&file_function); 01251 res |= ast_custom_function_unregister(&file_count_line_function); 01252 res |= ast_custom_function_unregister(&file_format_function); 01253 01254 return res; 01255 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Environment/filesystem dialplan functions" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static] |
Definition at line 1270 of file func_env.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1270 of file func_env.c.
struct ast_custom_function env_function [static] |
Initial value:
Definition at line 1214 of file func_env.c.
struct ast_custom_function file_count_line_function [static] |
Initial value:
{
.name = "FILE_COUNT_LINE",
.read2 = file_count_line,
.read_max = 12,
}
Definition at line 1232 of file func_env.c.
struct ast_custom_function file_format_function [static] |
Initial value:
{
.name = "FILE_FORMAT",
.read2 = file_format,
.read_max = 2,
}
Definition at line 1238 of file func_env.c.
struct ast_custom_function file_function [static] |
Initial value:
{
.name = "FILE",
.read2 = file_read,
.write = file_write,
}
Definition at line 1226 of file func_env.c.
struct ast_custom_function stat_function [static] |
Initial value:
{
.name = "STAT",
.read = stat_read,
.read_max = 12,
}
Definition at line 1220 of file func_env.c.
1.5.6