Annotation of embedaddon/libpdel/structs/structs.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * Copyright (c) 2001-2002 Packet Design, LLC.
        !             4:  * All rights reserved.
        !             5:  * 
        !             6:  * Subject to the following obligations and disclaimer of warranty,
        !             7:  * use and redistribution of this software, in source or object code
        !             8:  * forms, with or without modifications are expressly permitted by
        !             9:  * Packet Design; provided, however, that:
        !            10:  * 
        !            11:  *    (i)  Any and all reproductions of the source or object code
        !            12:  *         must include the copyright notice above and the following
        !            13:  *         disclaimer of warranties; and
        !            14:  *    (ii) No rights are granted, in any manner or form, to use
        !            15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
        !            16:  *         on advertising, endorsements, or otherwise except as such
        !            17:  *         appears in the above copyright notice or in the software.
        !            18:  * 
        !            19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
        !            20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
        !            21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
        !            22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
        !            23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
        !            24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
        !            25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
        !            26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
        !            27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
        !            28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
        !            29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
        !            30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
        !            31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
        !            32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
        !            33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        !            34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
        !            35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
        !            36:  * THE POSSIBILITY OF SUCH DAMAGE.
        !            37:  *
        !            38:  * Author: Archie Cobbs <archie@freebsd.org>
        !            39:  */
        !            40: 
        !            41: #include <sys/types.h>
        !            42: 
        !            43: #include <stdlib.h>
        !            44: #include <stdio.h>
        !            45: #include <stdarg.h>
        !            46: #include <assert.h>
        !            47: #include <string.h>
        !            48: #include <ctype.h>
        !            49: #include <errno.h>
        !            50: 
        !            51: #include "structs/structs.h"
        !            52: #include "structs/type/int.h"
        !            53: #include "structs/type/array.h"
        !            54: #include "structs/type/string.h"
        !            55: #include "structs/type/struct.h"
        !            56: #include "structs/type/union.h"
        !            57: #include "util/typed_mem.h"
        !            58: 
        !            59: /* Special handling for array length as a read-only field */
        !            60: static const struct structs_type structs_type_array_length = {
        !            61:        sizeof(u_int),
        !            62:        "uint",
        !            63:        STRUCTS_TYPE_PRIMITIVE,
        !            64:        structs_region_init,
        !            65:        structs_region_copy,
        !            66:        structs_region_equal,
        !            67:        structs_int_ascify,
        !            68:        structs_notsupp_binify,
        !            69:        structs_region_encode_netorder,
        !            70:        structs_notsupp_decode,
        !            71:        structs_nothing_free,
        !            72:        { { (void *)2 }, { (void *)0 } }    /* args for structs_int_ascify */
        !            73: };
        !            74: 
        !            75: /* Special handling for union field name as a read-only field */
        !            76: static const struct structs_type structs_type_union_field_name = {
        !            77:        sizeof(char *),
        !            78:        "string",
        !            79:        STRUCTS_TYPE_PRIMITIVE,
        !            80:        structs_notsupp_init,
        !            81:        structs_notsupp_copy,
        !            82:        structs_string_equal,
        !            83:        structs_string_ascify,
        !            84:        structs_notsupp_binify,
        !            85:        structs_notsupp_encode,
        !            86:        structs_notsupp_decode,
        !            87:        structs_nothing_free,
        !            88:        { { (void *)"union field_name" },    /* args for structs_type_string */
        !            89:        { (void *)0 } }
        !            90: };
        !            91: 
        !            92: /*
        !            93:  * Initialize an item.
        !            94:  */
        !            95: int
        !            96: structs_init(const struct structs_type *type, const char *name, void *data)
        !            97: {
        !            98:        /* Find item */
        !            99:        if ((type = structs_find(type, name, &data, 0)) == NULL)
        !           100:                return (-1);
        !           101: 
        !           102:        /* Initialize it */
        !           103:        return ((*type->init)(type, data));
        !           104: }
        !           105: 
        !           106: /*
        !           107:  * Reset an item.
        !           108:  */
        !           109: int
        !           110: structs_reset(const struct structs_type *type, const char *name, void *data)
        !           111: {
        !           112:        void *temp;
        !           113: 
        !           114:        /* Find item */
        !           115:        if ((type = structs_find(type, name, &data, 0)) == NULL)
        !           116:                return (-1);
        !           117: 
        !           118:        /* Make a temporary new copy */
        !           119:        if ((temp = MALLOC(TYPED_MEM_TEMP, type->size)) == NULL)
        !           120:                return (-1);
        !           121:        memset(temp, 0, type->size);
        !           122:        if ((*type->init)(type, temp) == -1) {
        !           123:                FREE(TYPED_MEM_TEMP, temp);
        !           124:                return (-1);
        !           125:        }
        !           126: 
        !           127:        /* Replace existing item, freeing it first */
        !           128:        (*type->uninit)(type, data);
        !           129:        memcpy(data, temp, type->size);
        !           130:        FREE(TYPED_MEM_TEMP, temp);
        !           131:        return (0);
        !           132: }
        !           133: 
        !           134: /*
        !           135:  * Free an item, returning it to its initialized state.
        !           136:  *
        !           137:  * If "name" is NULL or empty string, the entire structure is free'd.
        !           138:  */
        !           139: int
        !           140: structs_free(const struct structs_type *type, const char *name, void *data)
        !           141: {
        !           142:        const int errno_save = errno;
        !           143: 
        !           144:        /* Find item */
        !           145:        if ((type = structs_find(type, name, &data, 0)) == NULL)
        !           146:                return (-1);
        !           147: 
        !           148:        /* Free it */
        !           149:        (*type->uninit)(type, data);
        !           150:        errno = errno_save;
        !           151:        return (0);
        !           152: }
        !           153: 
        !           154: /*
        !           155:  * Get a copy of an item.
        !           156:  *
        !           157:  * If "name" is NULL or empty string, the entire structure is copied.
        !           158:  *
        !           159:  * It is assumed that "to" points to a region big enough to hold
        !           160:  * the copy of the item. "to" will not be free'd first, so it should
        !           161:  * not already be initialized.
        !           162:  *
        !           163:  * Note: "name" is only applied to "from". The structs types of
        !           164:  * "from.<name>" and "to" must be the same.
        !           165:  */
        !           166: int
        !           167: structs_get(const struct structs_type *type,
        !           168:        const char *name, const void *from, void *to)
        !           169: {
        !           170:        /* Find item */
        !           171:        if ((type = structs_find(type, name, (void **)&from, 0)) == NULL)
        !           172:                return (-1);
        !           173: 
        !           174:        /* Copy item */
        !           175:        return ((*type->copy)(type, from, to));
        !           176: }
        !           177: 
        !           178: /*
        !           179:  * Set an item in a structure.
        !           180:  *
        !           181:  * If "name" is NULL or empty string, the entire structure is copied.
        !           182:  *
        !           183:  * It is assumed that "to" is already initialized.
        !           184:  *
        !           185:  * Note: "name" is only applied to "to". The structs types of
        !           186:  * "from" and "to.<name>" must be the same.
        !           187:  */
        !           188: int
        !           189: structs_set(const struct structs_type *type,
        !           190:        const void *from, const char *name, void *to)
        !           191: {
        !           192:        void *copy;
        !           193: 
        !           194:        /* Find item */
        !           195:        if ((type = structs_find(type, name, &to, 0)) == NULL)
        !           196:                return (-1);
        !           197: 
        !           198:        /* Make a new copy of 'from' */
        !           199:        if ((copy = MALLOC(TYPED_MEM_TEMP, type->size)) == NULL)
        !           200:                return (-1);
        !           201:        if ((*type->copy)(type, from, copy) == -1) {
        !           202:                FREE(TYPED_MEM_TEMP, copy);
        !           203:                return (-1);
        !           204:        }
        !           205: 
        !           206:        /* Free overwritten item in 'to' */
        !           207:        (*type->uninit)(type, to);
        !           208: 
        !           209:        /* Move new item in its place */
        !           210:        memcpy(to, copy, type->size);
        !           211: 
        !           212:        /* Done */
        !           213:        FREE(TYPED_MEM_TEMP, copy);
        !           214:        return (0);
        !           215: }
        !           216: 
        !           217: /*
        !           218:  * Get the ASCII form of an item.
        !           219:  */
        !           220: char *
        !           221: structs_get_string(const struct structs_type *type,
        !           222:        const char *name, const void *data, const char *mtype)
        !           223: {
        !           224:        /* Find item */
        !           225:        if ((type = structs_find(type, name, (void **)&data, 0)) == NULL)
        !           226:                return (NULL);
        !           227: 
        !           228:        /* Ascify it */
        !           229:        return ((*type->ascify)(type, mtype, data));
        !           230: }
        !           231: 
        !           232: /*
        !           233:  * Set an item's value from a string.
        !           234:  *
        !           235:  * The referred to item must be of a type that supports ASCII encoding,
        !           236:  * and is assumed to be already initialized.
        !           237:  */
        !           238: int
        !           239: structs_set_string(const struct structs_type *type, const char *name,
        !           240:        const char *ascii, void *data, char *ebuf, size_t emax)
        !           241: {
        !           242:        void *temp;
        !           243:        char dummy[1];
        !           244: 
        !           245:        /* Sanity check */
        !           246:        if (ascii == NULL)
        !           247:                ascii = "";
        !           248:        if (ebuf == NULL) {
        !           249:                ebuf = dummy;
        !           250:                emax = sizeof(dummy);
        !           251:        }
        !           252: 
        !           253:        /* Initialize error buffer */
        !           254:        if (emax > 0)
        !           255:                *ebuf = '\0';
        !           256: 
        !           257:        /* Find item */
        !           258:        if ((type = structs_find(type, name, &data, 1)) == NULL) {
        !           259:                strlcpy(ebuf, strerror(errno), emax);
        !           260:                return (-1);
        !           261:        }
        !           262: 
        !           263:        /* Binify item into temporary storage */
        !           264:        if ((temp = MALLOC(TYPED_MEM_TEMP, type->size)) == NULL)
        !           265:                return (-1);
        !           266:        memset(temp, 0, type->size);
        !           267:        if ((*type->binify)(type, ascii, temp, ebuf, emax) == -1) {
        !           268:                FREE(TYPED_MEM_TEMP, temp);
        !           269:                if (emax > 0 && *ebuf == '\0')
        !           270:                        strlcpy(ebuf, strerror(errno), emax);
        !           271:                return (-1);
        !           272:        }
        !           273: 
        !           274:        /* Replace existing item, freeing it first */
        !           275:        (*type->uninit)(type, data);
        !           276:        memcpy(data, temp, type->size);
        !           277:        FREE(TYPED_MEM_TEMP, temp);
        !           278:        return (0);
        !           279: }
        !           280: 
        !           281: /*
        !           282:  * Get the binary encoded form of an item, allocated with type 'mtype'.
        !           283:  */
        !           284: int
        !           285: structs_get_binary(const struct structs_type *type, const char *name,
        !           286:        const void *data, const char *mtype, struct structs_data *code)
        !           287: {
        !           288:        /* Find item */
        !           289:        if ((type = structs_find(type, name, (void **)&data, 0)) == NULL) {
        !           290:                memset(code, 0, sizeof(*code));
        !           291:                return (-1);
        !           292:        }
        !           293: 
        !           294:        /* Encode it */
        !           295:        if ((*type->encode)(type, mtype, code, data) == -1) {
        !           296:                memset(code, 0, sizeof(*code));
        !           297:                return (-1);
        !           298:        }
        !           299: 
        !           300:        /* Done */
        !           301:        return (0);
        !           302: }
        !           303: 
        !           304: /*
        !           305:  * Set an item's value from its binary encoded value.
        !           306:  */
        !           307: int
        !           308: structs_set_binary(const struct structs_type *type, const char *name,
        !           309:        const struct structs_data *code, void *data, char *ebuf, size_t emax)
        !           310: {
        !           311:        void *temp;
        !           312:        int clen;
        !           313: 
        !           314:        /* Initialize error buffer */
        !           315:        if (emax > 0)
        !           316:                *ebuf = '\0';
        !           317: 
        !           318:        /* Find item */
        !           319:        if ((type = structs_find(type, name, (void **)&data, 0)) == NULL) {
        !           320:                strlcpy(ebuf, strerror(errno), emax);
        !           321:                return (-1);
        !           322:        }
        !           323: 
        !           324:        /* Decode item into temporary storage */
        !           325:        if ((temp = MALLOC(TYPED_MEM_TEMP, type->size)) == NULL)
        !           326:                return (-1);
        !           327:        memset(temp, 0, type->size);
        !           328:        if ((clen = (*type->decode)(type, code->data,
        !           329:            code->length, temp, ebuf, emax)) == -1) {
        !           330:                FREE(TYPED_MEM_TEMP, temp);
        !           331:                if (emax > 0 && *ebuf == '\0')
        !           332:                        strlcpy(ebuf, strerror(errno), emax);
        !           333:                return (-1);
        !           334:        }
        !           335:        assert(clen <= code->length);
        !           336: 
        !           337:        /* Replace existing item, freeing it first */
        !           338:        (*type->uninit)(type, data);
        !           339:        memcpy(data, temp, type->size);
        !           340:        FREE(TYPED_MEM_TEMP, temp);
        !           341: 
        !           342:        /* Done */
        !           343:        return (clen);
        !           344: }
        !           345: 
        !           346: /*
        !           347:  * Test for equality.
        !           348:  */
        !           349: int
        !           350: structs_equal(const struct structs_type *type,
        !           351:        const char *name, const void *data1, const void *data2)
        !           352: {
        !           353:        /* Find items */
        !           354:        if (structs_find(type, name, (void **)&data1, 0) == NULL)
        !           355:                return (-1);
        !           356:        if ((type = structs_find(type, name, (void **)&data2, 0)) == NULL)
        !           357:                return (-1);
        !           358: 
        !           359:        /* Compare them */
        !           360:        return ((*type->equal)(type, data1, data2));
        !           361: }
        !           362: 
        !           363: /*
        !           364:  * Find an item in a structure.
        !           365:  */
        !           366: const struct structs_type *
        !           367: structs_find(const struct structs_type *type, const char *name,
        !           368:        void **datap, int set_union)
        !           369: {
        !           370:        void *data = *datap;
        !           371:        const char *next;
        !           372: 
        !           373:        /* Empty string means stop recursing */
        !           374:        if (name == NULL || *name == '\0')
        !           375:                return (type);
        !           376: 
        !           377:        /* Primitive types don't have sub-elements */
        !           378:        if (type->tclass == STRUCTS_TYPE_PRIMITIVE) {
        !           379:                errno = ENOENT;
        !           380:                return (NULL);
        !           381:        }
        !           382: 
        !           383:        /* Dereference through pointer(s) */
        !           384:        while (type->tclass == STRUCTS_TYPE_POINTER) {
        !           385:                type = type->args[0].v;
        !           386:                data = *((void **)data);
        !           387:        }
        !           388: 
        !           389:        /* Get next name component */
        !           390:        if ((next = strchr(name, STRUCTS_SEPARATOR)) != NULL)
        !           391:                next++;
        !           392: 
        !           393:        /* Find element of aggregate structure update type and data */
        !           394:        switch (type->tclass) {
        !           395:        case STRUCTS_TYPE_ARRAY:
        !           396:            {
        !           397:                const struct structs_type *const etype = type->args[0].v;
        !           398:                const struct structs_array *const ary = data;
        !           399:                u_long index;
        !           400:                char *eptr;
        !           401: 
        !           402:                /* Special handling for "length" */
        !           403:                if (strcmp(name, "length") == 0) {
        !           404:                        type = &structs_type_array_length;
        !           405:                        data = (void *)&ary->length;
        !           406:                        break;
        !           407:                }
        !           408: 
        !           409:                /* Decode an index */
        !           410:                index = strtoul(name, &eptr, 10);
        !           411:                if (!isdigit(*name)
        !           412:                    || eptr == name
        !           413:                    || (*eptr != '\0' && *eptr != STRUCTS_SEPARATOR)) {
        !           414:                        errno = ENOENT;
        !           415:                        return (NULL);
        !           416:                }
        !           417:                if (index >= ary->length) {
        !           418:                        errno = EDOM;
        !           419:                        return (NULL);
        !           420:                }
        !           421:                type = etype;
        !           422:                data = (char *)ary->elems + (index * etype->size);
        !           423:                break;
        !           424:            }
        !           425:        case STRUCTS_TYPE_FIXEDARRAY:
        !           426:            {
        !           427:                const struct structs_type *const etype = type->args[0].v;
        !           428:                const u_int length = type->args[2].i;
        !           429:                u_long index;
        !           430:                char *eptr;
        !           431: 
        !           432:                /* Special handling for "length" */
        !           433:                if (strcmp(name, "length") == 0) {
        !           434:                        type = &structs_type_array_length;
        !           435:                        data = (void *)&type->args[2].i;
        !           436:                        break;
        !           437:                }
        !           438: 
        !           439:                /* Decode an index */
        !           440:                index = strtoul(name, &eptr, 10);
        !           441:                if (!isdigit(*name)
        !           442:                    || eptr == name
        !           443:                    || (*eptr != '\0' && *eptr != STRUCTS_SEPARATOR)) {
        !           444:                        errno = ENOENT;
        !           445:                        return (NULL);
        !           446:                }
        !           447:                if (index >= length) {
        !           448:                        errno = EDOM;
        !           449:                        return (NULL);
        !           450:                }
        !           451:                type = etype;
        !           452:                data = (char *)data + (index * etype->size);
        !           453:                break;
        !           454:            }
        !           455:        case STRUCTS_TYPE_STRUCTURE:
        !           456:            {
        !           457:                const struct structs_field *field;
        !           458: 
        !           459:                /* Find the field */
        !           460:                for (field = type->args[0].v; field->name != NULL; field++) {
        !           461:                        const size_t fnlen = strlen(field->name);
        !           462: 
        !           463:                        /* Handle field names with separator in them */
        !           464:                        if (strncmp(name, field->name, fnlen) == 0
        !           465:                            && (name[fnlen] == '\0'
        !           466:                              || name[fnlen] == STRUCTS_SEPARATOR)) {
        !           467:                                next = (name[fnlen] != '\0') ?
        !           468:                                    name + fnlen + 1 : NULL;
        !           469:                                break;
        !           470:                        }
        !           471:                }
        !           472:                if (field->name == NULL) {
        !           473:                        errno = ENOENT;
        !           474:                        return (NULL);
        !           475:                }
        !           476:                type = field->type;
        !           477:                data = (char *)data + field->offset;
        !           478:                break;
        !           479:            }
        !           480:        case STRUCTS_TYPE_UNION:
        !           481:            {
        !           482:                const struct structs_ufield *const fields = type->args[0].v;
        !           483:                const char *const mtype = type->args[1].s;
        !           484:                struct structs_union *const un = data;
        !           485:                const size_t oflen = strlen(un->field_name);
        !           486:                const struct structs_ufield *ofield;
        !           487:                const struct structs_ufield *field;
        !           488:                void *new_un;
        !           489:                void *data2;
        !           490: 
        !           491:                /* Special handling for "field_name" */
        !           492:                if (strcmp(name, "field_name") == 0) {
        !           493:                        type = &structs_type_union_field_name;
        !           494:                        data = (void *)&un->field_name;
        !           495:                        break;
        !           496:                }
        !           497: 
        !           498:                /* Find the old field */
        !           499:                for (ofield = fields; ofield->name != NULL; ofield++) {
        !           500:                        if (strcmp(ofield->name, un->field_name) == 0)
        !           501:                                break;
        !           502:                }
        !           503:                if (ofield->name == NULL) {
        !           504:                        assert(0);
        !           505:                        errno = EINVAL;
        !           506:                        return (NULL);
        !           507:                }
        !           508: 
        !           509:                /*
        !           510:                 * Check if the union is already set to the desired field,
        !           511:                 * handling field names with the separator char in them.
        !           512:                 */
        !           513:                if (strncmp(name, un->field_name, oflen) == 0
        !           514:                    && (name[oflen] == '\0'
        !           515:                      || name[oflen] == STRUCTS_SEPARATOR)) {
        !           516:                        next = (name[oflen] != '\0') ? name + oflen + 1 : NULL;
        !           517:                        field = ofield;
        !           518:                        goto union_done;
        !           519:                }
        !           520: 
        !           521:                /* Is modifying the union to get the right name acceptable? */
        !           522:                if (!set_union) {
        !           523:                        errno = ENOENT;
        !           524:                        return (NULL);
        !           525:                }
        !           526: 
        !           527:                /* Find the new field */
        !           528:                for (field = fields; field->name != NULL; field++) {
        !           529:                        const size_t fnlen = strlen(field->name);
        !           530: 
        !           531:                        /* Handle field names with separator in them */
        !           532:                        if (strncmp(name, field->name, fnlen) == 0
        !           533:                            && (name[fnlen] == '\0'
        !           534:                              || name[fnlen] == STRUCTS_SEPARATOR)) {
        !           535:                                next = (name[fnlen] != '\0') ?
        !           536:                                    name + fnlen + 1 : NULL;
        !           537:                                break;
        !           538:                        }
        !           539:                }
        !           540:                if (field->name == NULL) {
        !           541:                        errno = ENOENT;
        !           542:                        return (NULL);
        !           543:                }
        !           544: 
        !           545:                /* Create a new union with the new field type */
        !           546:                if ((new_un = MALLOC(mtype, field->type->size)) == NULL)
        !           547:                        return (NULL);
        !           548:                if ((*field->type->init)(field->type, new_un) == -1) {
        !           549:                        FREE(mtype, new_un);
        !           550:                        return (NULL);
        !           551:                }
        !           552: 
        !           553:                /* See if name would be found with new union instead of old */
        !           554:                data2 = new_un;
        !           555:                if (next != NULL
        !           556:                    && structs_find(field->type, next, &data2, 1) == NULL) {
        !           557:                        (*field->type->uninit)(field->type, new_un);
        !           558:                        FREE(mtype, new_un);
        !           559:                        return (NULL);
        !           560:                }
        !           561: 
        !           562:                /* Replace existing union with new one having desired type */
        !           563:                (*ofield->type->uninit)(ofield->type, un->un);
        !           564:                FREE(mtype, un->un);
        !           565:                un->un = new_un;
        !           566:                *((const char **)&un->field_name) = field->name;
        !           567: 
        !           568:        union_done:
        !           569:                /* Continue recursing */
        !           570:                type = field->type;
        !           571:                data = un->un;
        !           572:                break;
        !           573:            }
        !           574:        default:
        !           575:                assert(0);
        !           576:                return (NULL);
        !           577:        }
        !           578: 
        !           579:        /* Recurse on sub-element */
        !           580:        if ((type = structs_find(type, next, &data, set_union)) == NULL)
        !           581:                return (NULL);
        !           582: 
        !           583:        /* Done */
        !           584:        *datap = data;
        !           585:        return (type);
        !           586: }
        !           587: 
        !           588: /*
        !           589:  * Traverse a structure.
        !           590:  */
        !           591: 
        !           592: struct structs_trav {
        !           593:        char            **list;
        !           594:        u_int           len;
        !           595:        u_int           alloc;
        !           596:        const char      *mtype;
        !           597: };
        !           598: 
        !           599: static int     structs_trav(struct structs_trav *t, const char *name,
        !           600:                        const struct structs_type *type, const void *data);
        !           601: 
        !           602: int
        !           603: structs_traverse(const struct structs_type *type,
        !           604:        const void *data, char ***listp, const char *mtype)
        !           605: {
        !           606:        struct structs_trav t;
        !           607: 
        !           608:        /* Initialize traversal structure */
        !           609:        memset(&t, 0, sizeof(t));
        !           610:        t.mtype = mtype;
        !           611: 
        !           612:        /* Recurse */
        !           613:        if (structs_trav(&t, "", type, data) == -1) {
        !           614:                while (t.len > 0)
        !           615:                        FREE(t.mtype, t.list[--t.len]);
        !           616:                FREE(t.mtype, t.list);
        !           617:                return (-1);
        !           618:        }
        !           619: 
        !           620:        /* Return the result */
        !           621:        *listp = t.list;
        !           622:        return (t.len);
        !           623: }
        !           624: 
        !           625: static int
        !           626: structs_trav(struct structs_trav *t, const char *name,
        !           627:        const struct structs_type *type, const void *data)
        !           628: {
        !           629:        const char *const dot = (*name == '\0' ? "" : ".");
        !           630:        char *ename;
        !           631:        int i;
        !           632: 
        !           633:        /* Dereference through pointer(s) */
        !           634:        while (type->tclass == STRUCTS_TYPE_POINTER) {
        !           635:                type = type->args[0].v;
        !           636:                data = *((void **)data);
        !           637:        }
        !           638: 
        !           639:        switch (type->tclass) {
        !           640:        case STRUCTS_TYPE_PRIMITIVE:
        !           641:            {
        !           642:                /* Grow list as necessary */
        !           643:                if (t->len == t->alloc) {
        !           644:                        u_int new_alloc;
        !           645:                        char **new_list;
        !           646: 
        !           647:                        new_alloc = (t->alloc + 32) * 2;
        !           648:                        if ((new_list = REALLOC(t->mtype,
        !           649:                            t->list, new_alloc * sizeof(*t->list))) == NULL)
        !           650:                                return (-1);
        !           651:                        t->list = new_list;
        !           652:                        t->alloc = new_alloc;
        !           653:                }
        !           654: 
        !           655:                /* Add new name to list */
        !           656:                if ((t->list[t->len] = STRDUP(t->mtype, name)) == NULL)
        !           657:                        return (-1);
        !           658:                t->len++;
        !           659: 
        !           660:                /* Done */
        !           661:                return (0);
        !           662:            }
        !           663: 
        !           664:        case STRUCTS_TYPE_ARRAY:
        !           665:            {
        !           666:                const struct structs_type *const etype = type->args[0].v;
        !           667:                const struct structs_array *const ary = data;
        !           668: 
        !           669:                /* Iterate over array elements */
        !           670:                for (i = 0; i < ary->length; i++) {
        !           671:                        const void *const edata
        !           672:                            = (char *)ary->elems + (i * etype->size);
        !           673: 
        !           674:                        if (ASPRINTF(t->mtype,
        !           675:                            &ename, "%s%s%d", name, dot, i) == -1)
        !           676:                                return (-1);
        !           677:                        if (structs_trav(t, ename, etype, edata) == -1) {
        !           678:                                FREE(t->mtype, ename);
        !           679:                                return (-1);
        !           680:                        }
        !           681:                        FREE(t->mtype, ename);
        !           682:                }
        !           683: 
        !           684:                /* Done */
        !           685:                return (0);
        !           686:            }
        !           687: 
        !           688:        case STRUCTS_TYPE_FIXEDARRAY:
        !           689:            {
        !           690:                const struct structs_type *const etype = type->args[0].v;
        !           691:                const u_int length = type->args[2].i;
        !           692: 
        !           693:                /* Iterate over array elements */
        !           694:                for (i = 0; i < length; i++) {
        !           695:                        const void *const edata
        !           696:                            = (char *)data + (i * etype->size);
        !           697: 
        !           698:                        if (ASPRINTF(t->mtype,
        !           699:                            &ename, "%s%s%d", name, dot, i) == -1)
        !           700:                                return (-1);
        !           701:                        if (structs_trav(t, ename, etype, edata) == -1) {
        !           702:                                FREE(t->mtype, ename);
        !           703:                                return (-1);
        !           704:                        }
        !           705:                        FREE(t->mtype, ename);
        !           706:                }
        !           707: 
        !           708:                /* Done */
        !           709:                return (0);
        !           710:            }
        !           711: 
        !           712:        case STRUCTS_TYPE_STRUCTURE:
        !           713:            {
        !           714:                const struct structs_field *field;
        !           715: 
        !           716:                /* Iterate over structure fields */
        !           717:                for (field = type->args[0].v; field->name != NULL; field++) {
        !           718:                        const void *const edata
        !           719:                            = (char *)data + field->offset;
        !           720: 
        !           721:                        if (ASPRINTF(t->mtype,
        !           722:                            &ename, "%s%s%s", name, dot, field->name) == -1)
        !           723:                                return (-1);
        !           724:                        if (structs_trav(t, ename, field->type, edata) == -1) {
        !           725:                                FREE(t->mtype, ename);
        !           726:                                return (-1);
        !           727:                        }
        !           728:                        FREE(t->mtype, ename);
        !           729:                }
        !           730: 
        !           731:                /* Done */
        !           732:                return (0);
        !           733:            }
        !           734: 
        !           735:        case STRUCTS_TYPE_UNION:
        !           736:            {
        !           737:                const struct structs_ufield *const fields = type->args[0].v;
        !           738:                const struct structs_union *const un = data;
        !           739:                const struct structs_ufield *field;
        !           740: 
        !           741:                /* Find field */
        !           742:                for (field = fields; field->name != NULL
        !           743:                    && strcmp(un->field_name, field->name) != 0; field++);
        !           744:                if (field->name == NULL) {
        !           745:                        assert(0);
        !           746:                        errno = EINVAL;
        !           747:                        return (-1);
        !           748:                }
        !           749: 
        !           750:                /* Do selected union field */
        !           751:                if (ASPRINTF(t->mtype,
        !           752:                    &ename, "%s%s%s", name, dot, field->name) == -1)
        !           753:                        return (-1);
        !           754:                if (structs_trav(t, ename, field->type, un->un) == -1) {
        !           755:                        FREE(t->mtype, ename);
        !           756:                        return (-1);
        !           757:                }
        !           758:                FREE(t->mtype, ename);
        !           759: 
        !           760:                /* Done */
        !           761:                return (0);
        !           762:            }
        !           763: 
        !           764:        default:
        !           765:                assert(0);
        !           766:                errno = ECONNABORTED;
        !           767:                return (-1);
        !           768:        }
        !           769: }
        !           770: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>