net-snmp 5.7
text_utils.c
00001 #include <net-snmp/net-snmp-config.h>
00002 #include <net-snmp/net-snmp-features.h>
00003 #include <net-snmp/net-snmp-includes.h>
00004 
00005 #include <stdio.h>
00006 #include <ctype.h>
00007 #if HAVE_STDLIB_H
00008 #   include <stdlib.h>
00009 #endif
00010 #if HAVE_UNISTD_H
00011 #   include <unistd.h>
00012 #endif
00013 #if HAVE_STRING_H
00014 #   include <string.h>
00015 #else
00016 #  include <strings.h>
00017 #endif
00018 
00019 #include <sys/types.h>
00020 
00021 #if HAVE_LIMITS_H
00022 #   include <limits.h>
00023 #endif
00024 #if HAVE_SYS_PARAM_H
00025 #   include <sys/param.h>
00026 #endif
00027 #ifdef HAVE_SYS_STAT_H
00028 #   include <sys/stat.h>
00029 #endif
00030 #ifdef HAVE_FCNTL_H
00031 #   include <fcntl.h>
00032 #endif
00033 
00034 #include <errno.h>
00035 
00036 #if HAVE_DMALLOC_H
00037 #  include <dmalloc.h>
00038 #endif
00039 
00040 #include <net-snmp/types.h>
00041 #include <net-snmp/library/snmp_debug.h>
00042 #include <net-snmp/library/container.h>
00043 #include <net-snmp/library/file_utils.h>
00044 #include <net-snmp/library/text_utils.h>
00045 
00046 netsnmp_feature_child_of(text_utils, libnetsnmp)
00047 
00048 netsnmp_feature_provide(text_utils)
00049 #ifdef NETSNMP_FEATURE_REQUIRE_TEXT_UTILS
00050 netsnmp_feature_require(file_utils)
00051 #endif /* NETSNMP_FEATURE_REQUIRE_TEXT_UTILS */
00052 
00053 #ifndef NETSNMP_FEATURE_REMOVE_TEXT_UTILS
00054 /*------------------------------------------------------------------
00055  *
00056  * Prototypes
00057  *
00058  */
00059 /*
00060  * parse methods
00061  */
00062 void
00063 _pm_save_index_string_string(FILE *f, netsnmp_container *cin,
00064                              int flags);
00065 void
00066 _pm_save_everything(FILE *f, netsnmp_container *cin, int flags);
00067 void
00068 _pm_user_function(FILE *f, netsnmp_container *cin,
00069                   netsnmp_line_process_info *lpi, int flags);
00070 
00071 
00072 /*
00073  * line processors
00074  */
00075 int _process_line_tvi(netsnmp_line_info *line_info, void *mem,
00076                       struct netsnmp_line_process_info_s* lpi);
00077 
00078 
00079 
00080 /*------------------------------------------------------------------
00081  *
00082  * Text file processing functions
00083  *
00084  */
00085 
00089 netsnmp_container *
00090 netsnmp_file_text_parse(netsnmp_file *f, netsnmp_container *cin,
00091                         int parse_mode, u_int flags, void *context)
00092 {
00093     netsnmp_container *c = cin;
00094     FILE              *fin;
00095     int                rc;
00096 
00097     if (NULL == f)
00098         return NULL;
00099 
00100     if ((NULL == c) && (!(flags & PM_FLAG_NO_CONTAINER))) {
00101         c = netsnmp_container_find("text_parse:binary_array");
00102         if (NULL == c)
00103             return NULL;
00104     }
00105 
00106     rc = netsnmp_file_open(f);
00107     if (rc < 0) { 
00108         if ((NULL !=c) && (c != cin))
00109             CONTAINER_FREE(c);
00110         return NULL;
00111     }
00112     
00113     /*
00114      * get a stream from the file descriptor. This DOES NOT rewind the
00115      * file (if fd was previously opened).
00116      */
00117     fin = fdopen(f->fd, "r");
00118     if (NULL == fin) {
00119         if (NS_FI_AUTOCLOSE(f->ns_flags))
00120             close(f->fd);
00121         if ((NULL !=c) && (c != cin))
00122             CONTAINER_FREE(c);
00123         return NULL;
00124     }
00125 
00126     switch (parse_mode) {
00127 
00128         case PM_SAVE_EVERYTHING:
00129             _pm_save_everything(fin, c, flags);
00130             break;
00131 
00132         case PM_INDEX_STRING_STRING:
00133             _pm_save_index_string_string(fin, c, flags);
00134             break;
00135 
00136         case PM_USER_FUNCTION:
00137             if (NULL != context)
00138                 _pm_user_function(fin, c, (netsnmp_line_process_info*)context,
00139                                   flags);
00140             break;
00141 
00142         default:
00143             snmp_log(LOG_ERR, "unknown parse mode %d\n", parse_mode);
00144             break;
00145     }
00146 
00147 
00148     /*
00149      * close the stream, which will have the side effect of also closing
00150      * the file descriptor, so we need to reset it.
00151      */
00152     fclose(fin);
00153     f->fd = -1;
00154 
00155     return c;
00156 }
00157 
00158 netsnmp_feature_child_of(text_token_container_from_file, netsnmp_unused)
00159 #ifndef NETSNMP_FEATURE_REMOVE_TEXT_TOKEN_CONTAINER_FROM_FILE
00160 netsnmp_container *
00161 netsnmp_text_token_container_from_file(const char *file, u_int flags,
00162                                        netsnmp_container *cin, void *context)
00163 {
00164     netsnmp_line_process_info  lpi;
00165     netsnmp_container         *c = cin, *c_rc;
00166     netsnmp_file              *fp;
00167 
00168     if (NULL == file)
00169         return NULL;
00170 
00171     /*
00172      * allocate file resources
00173      */
00174     fp = netsnmp_file_fill(NULL, file, O_RDONLY, 0, 0);
00175     if (NULL == fp) 
00176         return NULL;
00177 
00178     memset(&lpi, 0x0, sizeof(lpi));
00179     lpi.mem_size = sizeof(netsnmp_token_value_index);
00180     lpi.process = _process_line_tvi;
00181     lpi.user_context = context;
00182 
00183     if (NULL == c) {
00184         c = netsnmp_container_find("string:binary_array");
00185         if (NULL == c) {
00186             snmp_log(LOG_ERR,"malloc failed\n");
00187             netsnmp_file_release(fp);
00188             return NULL;
00189         }
00190     }
00191 
00192     c_rc = netsnmp_file_text_parse(fp, c, PM_USER_FUNCTION, 0, &lpi);
00193 
00194     /*
00195      * if we got a bad return and the user didn't pass us a container,
00196      * we need to release the container we allocated.
00197      */
00198     if ((NULL == c_rc) && (NULL == cin)) {
00199         CONTAINER_FREE(c);
00200         c = NULL;
00201     }
00202     else
00203         c = c_rc;
00204 
00205     /*
00206      * release file resources
00207      */
00208     netsnmp_file_release(fp);
00209     
00210     return c;
00211 }
00212 #endif /* NETSNMP_FEATURE_REMOVE_TEXT_TOKEN_CONTAINER_FROM_FILE */
00213 
00214 
00215 /*------------------------------------------------------------------
00216  *
00217  * Text file process modes helper functions
00218  *
00219  */
00220 
00225 void
00226 _pm_save_everything(FILE *f, netsnmp_container *cin, int flags)
00227 {
00228     char               line[STRINGMAX], *ptr;
00229     size_t             len;
00230 
00231     netsnmp_assert(NULL != f);
00232     netsnmp_assert(NULL != cin);
00233 
00234     while (fgets(line, sizeof(line), f) != NULL) {
00235 
00236         ptr = line;
00237         len = strlen(line) - 1;
00238         if (line[len] == '\n')
00239             line[len] = 0;
00240 
00241         /*
00242          * save blank line or comment?
00243          */
00244         if (flags & PM_FLAG_SKIP_WHITESPACE) {
00245             if (NULL == (ptr = skip_white(ptr)))
00246                 continue;
00247         }
00248 
00249         ptr = strdup(line);
00250         if (NULL == ptr) {
00251             snmp_log(LOG_ERR,"malloc failed\n");
00252             break;
00253         }
00254 
00255         CONTAINER_INSERT(cin,ptr);
00256     }
00257 }
00258 
00263 void
00264 _pm_save_index_string_string(FILE *f, netsnmp_container *cin,
00265                              int flags)
00266 {
00267     char                        line[STRINGMAX], *ptr;
00268     netsnmp_token_value_index  *tvi;
00269     size_t                      count = 0, len;
00270 
00271     netsnmp_assert(NULL != f);
00272     netsnmp_assert(NULL != cin);
00273 
00274     while (fgets(line, sizeof(line), f) != NULL) {
00275 
00276         ++count;
00277         ptr = line;
00278         len = strlen(line) - 1;
00279         if (line[len] == '\n')
00280             line[len] = 0;
00281 
00282         /*
00283          * save blank line or comment?
00284          */
00285         if (flags & PM_FLAG_SKIP_WHITESPACE) {
00286             if (NULL == (ptr = skip_white(ptr)))
00287                 continue;
00288         }
00289 
00290         tvi = SNMP_MALLOC_TYPEDEF(netsnmp_token_value_index);
00291         if (NULL == tvi) {
00292             snmp_log(LOG_ERR,"malloc failed\n");
00293             break;
00294         }
00295             
00296         /*
00297          * copy whole line, then set second pointer to
00298          * after token. One malloc, 2 strings!
00299          */
00300         tvi->index = count;
00301         tvi->token = strdup(line);
00302         if (NULL == tvi->token) {
00303             snmp_log(LOG_ERR,"malloc failed\n");
00304             free(tvi);
00305             break;
00306         }
00307         tvi->value.cp = skip_not_white(tvi->token);
00308         if (NULL != tvi->value.cp) {
00309             *(tvi->value.cp) = 0;
00310             ++(tvi->value.cp);
00311         }
00312         CONTAINER_INSERT(cin, tvi);
00313     }
00314 }
00315 
00320 void
00321 _pm_user_function(FILE *f, netsnmp_container *cin,
00322                   netsnmp_line_process_info *lpi, int flags)
00323 {
00324     char                        buf[STRINGMAX];
00325     netsnmp_line_info           li;
00326     void                       *mem = NULL;
00327     int                         rc;
00328 
00329     netsnmp_assert(NULL != f);
00330     netsnmp_assert(NULL != cin);
00331 
00332     /*
00333      * static buf, or does the user want the memory?
00334      */
00335     if (flags & PMLP_FLAG_ALLOC_LINE) {
00336         if (0 != lpi->line_max)
00337             li.line_max =  lpi->line_max;
00338         else
00339             li.line_max = STRINGMAX;
00340         li.line = (char *)calloc(li.line_max, 1);
00341         if (NULL == li.line) {
00342             snmp_log(LOG_ERR,"malloc failed\n");
00343             return;
00344         }
00345     }
00346     else {
00347         li.line = buf;
00348         li.line_max = sizeof(buf);
00349     }
00350         
00351     li.index = 0;
00352     while (fgets(li.line, li.line_max, f) != NULL) {
00353 
00354         ++li.index;
00355         li.start = li.line;
00356         li.line_len = strlen(li.line) - 1;
00357         if ((!(lpi->flags & PMLP_FLAG_LEAVE_NEWLINE)) &&
00358             (li.line[li.line_len] == '\n'))
00359             li.line[li.line_len] = 0;
00360         
00361         /*
00362          * save blank line or comment?
00363          */
00364         if (!(lpi->flags & PMLP_FLAG_PROCESS_WHITESPACE)) {
00365             if (NULL == (li.start = skip_white(li.start)))
00366                 continue;
00367         }
00368 
00369         /*
00370          *  do we need to allocate memory for the use?
00371          * if the last call didn't use the memory we allocated,
00372          * re-use it. Otherwise, allocate new chunk.
00373          */
00374         if ((0 != lpi->mem_size) && (NULL == mem)) {
00375             mem = calloc(lpi->mem_size, 1);
00376             if (NULL == mem) {
00377                 snmp_log(LOG_ERR,"malloc failed\n");
00378                 break;
00379             }
00380         }
00381 
00382         /*
00383          * do they want a copy ot the line?
00384          */
00385         if (lpi->flags & PMLP_FLAG_STRDUP_LINE) {
00386             li.start = strdup(li.start);
00387             if (NULL == li.start) {
00388                 snmp_log(LOG_ERR,"malloc failed\n");
00389                 break;
00390             }
00391         }
00392         else if (lpi->flags & PMLP_FLAG_ALLOC_LINE) {
00393             li.start = li.line;
00394         }
00395 
00396         /*
00397          * call the user function. If the function wants to save
00398          * the memory chunk, insert it in the container, the clear
00399          * pointer so we reallocate next time.
00400          */
00401         li.start_len = strlen(li.start);
00402         rc = (*lpi->process)(&li, mem, lpi);
00403         if (PMLP_RC_MEMORY_USED == rc) {
00404 
00405             if (!(lpi->flags & PMLP_FLAG_NO_CONTAINER))
00406                 CONTAINER_INSERT(cin, mem);
00407             
00408             mem = NULL;
00409             
00410             if (lpi->flags & PMLP_FLAG_ALLOC_LINE) {
00411                 li.line = (char *)calloc(li.line_max, 1);
00412                 if (NULL == li.line) {
00413                     snmp_log(LOG_ERR,"malloc failed\n");
00414                     break;
00415                 }
00416             }
00417         }
00418         else if (PMLP_RC_MEMORY_UNUSED == rc ) {
00419             /*
00420              * they didn't use the memory. if li.start was a strdup, we have
00421              * to release it. leave mem, we can re-use it (its a fixed size).
00422              */
00423             if (lpi->flags & PMLP_FLAG_STRDUP_LINE)
00424                 free(li.start); /* no point in SNMP_FREE */
00425         }
00426         else {
00427             if (PMLP_RC_STOP_PROCESSING != rc )
00428                 snmp_log(LOG_ERR, "unknown rc %d from text processor\n", rc);
00429             break;
00430         }
00431     }
00432     SNMP_FREE(mem);
00433 }
00434 
00435 /*------------------------------------------------------------------
00436  *
00437  * Test line process helper functions
00438  *
00439  */
00444 int
00445 _process_line_tvi(netsnmp_line_info *line_info, void *mem,
00446                   struct netsnmp_line_process_info_s* lpi)
00447 {
00448     netsnmp_token_value_index *tvi = (netsnmp_token_value_index *)mem;
00449     char                      *ptr;
00450 
00451     /*
00452      * get token
00453      */
00454     ptr = skip_not_white(line_info->start);
00455     if (NULL == ptr) {
00456         DEBUGMSGTL(("text:util:tvi", "no value after token '%s'\n",
00457                     line_info->start));
00458         return PMLP_RC_MEMORY_UNUSED;
00459     }
00460 
00461     /*
00462      * null terminate, search for value;
00463      */
00464     *(ptr++) = 0;
00465     ptr = skip_white(ptr);
00466     if (NULL == ptr) {
00467         DEBUGMSGTL(("text:util:tvi", "no value after token '%s'\n",
00468                     line_info->start));
00469         return PMLP_RC_MEMORY_UNUSED;
00470     }
00471 
00472     /*
00473      * get value
00474      */
00475     switch((int)(intptr_t)lpi->user_context) {
00476 
00477         case PMLP_TYPE_UNSIGNED:
00478             tvi->value.ul = strtoul(ptr, NULL, 0);
00479             if ((errno == ERANGE) && (ULONG_MAX == tvi->value.ul))
00480                 snmp_log(LOG_WARNING,"value overflow\n");
00481             break;
00482 
00483 
00484         case PMLP_TYPE_INTEGER:
00485             tvi->value.sl = strtol(ptr, NULL, 0);
00486             if ((errno == ERANGE) &&
00487                 ((LONG_MAX == tvi->value.sl) ||
00488                  (LONG_MIN == tvi->value.sl)))
00489                 snmp_log(LOG_WARNING,"value over/under-flow\n");
00490             break;
00491 
00492         case PMLP_TYPE_STRING:
00493             tvi->value.cp = strdup(ptr);
00494             break;
00495 
00496         case PMLP_TYPE_BOOLEAN:
00497             if (isdigit((unsigned char)(*ptr)))
00498                 tvi->value.ul = strtoul(ptr, NULL, 0);
00499             else if (strcasecmp(ptr,"true") == 0)
00500                 tvi->value.ul = 1;
00501             else if (strcasecmp(ptr,"false") == 0)
00502                 tvi->value.ul = 0;
00503             else {
00504                 snmp_log(LOG_WARNING,"bad value for boolean\n");
00505                 return PMLP_RC_MEMORY_UNUSED;
00506             }
00507             break;
00508 
00509         default:
00510             snmp_log(LOG_ERR,"unsupported value type %d\n",
00511                      (int)(intptr_t)lpi->user_context);
00512             break;
00513     }
00514     
00515     /*
00516      * save token and value
00517      */
00518     tvi->token = strdup(line_info->start);
00519     tvi->index = line_info->index;
00520 
00521     return PMLP_RC_MEMORY_USED;
00522 }
00523 
00524 #else  /* ! NETSNMP_FEATURE_REMOVE_TEXT_UTILS */
00525 netsnmp_feature_unused(text_utils);
00526 #endif /* ! NETSNMP_FEATURE_REMOVE_TEXT_UTILS */