X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/d11a0bf77a5230387d222ec727865a898767ff3e..ef13e9a46baaa347014ac236f36a2536f055b108:/key-data.c diff --git a/key-data.c b/key-data.c index 01def8c..a0644a1 100644 --- a/key-data.c +++ b/key-data.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: key-data.c,v 1.1 1999/12/22 15:47:48 mdw Exp $ + * $Id$ * * Encoding and decoding of key data * @@ -27,32 +27,34 @@ * MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: key-data.c,v $ - * Revision 1.1 1999/12/22 15:47:48 mdw - * Major key-management revision. - * - */ - /*----- Header files ------------------------------------------------------*/ #include #include #include -#include #include #include #include #include #include -#include "key.h" +#include "key-data.h" #include "mp.h" #include "mptext.h" -/*----- Disposal ----------------------------------------------------------*/ +/*----- Reference counting stuff ------------------------------------------*/ + +/* --- @key_incref@ --- * + * + * Arguments: @key_data *k@ = pointer to key data + * + * Returns: --- + * + * Use: Increments the refcount on a key data block. + */ + +void key_incref(key_data *k) { KEY_INCREF(k); } /* --- @key_destroy@ --- * * @@ -60,7 +62,8 @@ * * Returns: --- * - * Use: Destroys a lump of key data. + * Use: Destroys a block of key data, regardless of reference count. + * Don't use this unless you know what you're doing. */ void key_destroy(key_data *k) @@ -68,93 +71,203 @@ void key_destroy(key_data *k) switch (k->e & KF_ENCMASK) { case KENC_BINARY: case KENC_ENCRYPT: - if (k->e & KF_BURN) - memset(k->u.k.k, 0, k->u.k.sz); - sub_free(k->u.k.k, k->u.k.sz); + if (k->u.k.k) { + if (k->e & KF_BURN) + memset(k->u.k.k, 0, k->u.k.sz); + sub_free(k->u.k.k, k->u.k.sz); + } break; case KENC_MP: mp_drop(k->u.m); break; + case KENC_STRING: + xfree(k->u.p); + break; + case KENC_EC: + EC_DESTROY(&k->u.e); + break; case KENC_STRUCT: { - sym_iter i; - key_struct *ks; + key_data *kd; + key_subkeyiter i; - for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) - key_destroy(&ks->k); + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, 0, &kd); ) + KEY_DROP(kd); sym_destroy(&k->u.s); } break; + default: + abort(); + } + DESTROY(k); +} + +/* --- @key_drop@ --- * + * + * Arguments: @key_data *k@ = pointer to key data to destroy + * + * Returns: --- + * + * Use: Drops a reference to key data, destroying it if necessary. + */ + +void key_drop(key_data *k) { KEY_DROP(k); } + +/* --- @key_split@ --- * + * + * Arguments: @key_data **kk@ = address of pointer to key data block + * + * Returns: --- + * + * Use: Replaces @*kk@ with a pointer to the same key data, but with + * just one reference. + */ + +void key_split(key_data **kk) +{ + key_data *k = *kk; + + if (k->ref == 1) + return; + switch (k->e & KF_ENCMASK) { + case KENC_BINARY: + *kk = key_newbinary(k->e, k->u.k.k, k->u.k.sz); + break; + case KENC_ENCRYPT: + *kk = key_newencrypted(k->e, k->u.k.k, k->u.k.sz); + break; + case KENC_MP: + *kk = key_newmp(k->e, k->u.m); + break; + case KENC_STRING: + *kk = key_newstring(k->e, k->u.p); + break; + case KENC_EC: + *kk = key_newec(k->e, &k->u.e); + break; + case KENC_STRUCT: { + key_subkeyiter i; + const char *tag; + key_data *kd; + + *kk = key_newstruct(); + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, &tag, &kd); ) + key_structset(*kk, tag, kd); + } break; + default: + abort(); } } /*----- Setting new values ------------------------------------------------*/ -/* --- @key_binary@ --- * +/* --- @key_newraw@ --- * * - * Arguments: @key_data *k@ = pointer to key data block + * Arguments: @unsigned e@ = encoding type to set + * + * Returns: New key block, not filled in. + */ + +key_data *key_newraw(unsigned e) +{ + key_data *k = CREATE(key_data); + k->e = e; + k->ref = 1; + return (k); +} + +/* --- @key_newbinary@ --- * + * + * Arguments: @unsigned e@ = other encoding flags * @const void *p@ = pointer to key data * @size_t sz@ = size of the key data * - * Returns: --- - * - * Use: Sets a binary key in a key data block. + * Returns: New key data object. */ -void key_binary(key_data *k, const void *p, size_t sz) +key_data *key_newbinary(unsigned e, const void *p, size_t sz) { - k->e = (k->e & ~KF_ENCMASK) | KENC_BINARY; + key_data *k = key_newraw(KENC_BINARY | e); k->u.k.k = sub_alloc(sz); memcpy(k->u.k.k, p, sz); k->u.k.sz = sz; + return (k); } -/* --- @key_encrypted@ --- * +/* --- @key_newencrypted@ --- * * - * Arguments: @key_data *k@ = pointer to key data block + * Arguments: @unsigned e@ = other encoding flags * @const void *p@ = pointer to key data * @size_t sz@ = size of the key data * - * Returns: --- - * - * Use: Sets an encrypted key in a key data block. + * Returns: New key data object. */ -void key_encrypted(key_data *k, const void *p, size_t sz) +key_data *key_newencrypted(unsigned e, const void *p, size_t sz) { - k->e = (k->e & ~KF_ENCMASK) | KENC_ENCRYPT; + key_data *k = key_newraw(KENC_ENCRYPT | e); k->u.k.k = sub_alloc(sz); memcpy(k->u.k.k, p, sz); k->u.k.sz = sz; + return (k); } -/* --- @key_mp@ --- * +/* --- @key_mewmp@ --- * * - * Arguments: @key_data *k@ = pointer to key data block + * Arguments: @unsigned e@ = other encoding flags * @mp *m@ = pointer to the value to set * - * Returns: --- - * - * Use: Sets a multiprecision integer key in a key block. + * Returns: New key data object. */ -void key_mp(key_data *k, mp *m) +key_data *key_newmp(unsigned e, mp *m) { - k->e = (k->e & ~KF_ENCMASK) | KENC_MP; + key_data *k = key_newraw(KENC_MP | e); k->u.m = MP_COPY(m); + return (k); } -/* --- @key_structure@ --- * +/* --- @key_newstring@ --- * * - * Arguments: @key_data *k@ = pointer to key data block + * Arguments: @unsigned e@ = other encoding flags + * @const char *p@ = pointer to the value to set * - * Returns: --- + * Returns: New key data object. + */ + +key_data *key_newstring(unsigned e, const char *p) +{ + key_data *k = key_newraw(KENC_STRING | e); + k->u.p = xstrdup(p); + return (k); +} + +/* --- @key_newec@ --- * + * + * Arguments: @unsigned e@ = other encoding flags + * @const ec *pt@ = pointer to the value to set + * + * Returns: New key data object. + */ + +key_data *key_newec(unsigned e, const ec *pt) +{ + key_data *k = key_newraw(KENC_EC | e); + EC_CREATE(&k->u.e); + EC_COPY(&k->u.e, pt); + return (k); +} + +/* --- @key_newstruct@ --- * + * + * Arguments: --- * - * Use: Initializes a structured key type. + * Returns: New key data object. */ -void key_structure(key_data *k) +key_data *key_newstruct(void) { - k->e = KENC_STRUCT; + key_data *k = key_newraw(KENC_STRUCT); sym_create(&k->u.s); + return (k); } /* --- @key_structfind@ --- * @@ -170,61 +283,91 @@ void key_structure(key_data *k) key_data *key_structfind(key_data *k, const char *tag) { key_struct *ks; - assert(((void)"Key is not structured", k->e == KENC_STRUCT)); + assert(((void)"Key is not structured", + (k->e & KF_ENCMASK) == KENC_STRUCT)); ks = sym_find(&k->u.s, tag, -1, 0, 0); - return (&ks->k); + if (!ks) + return (0); + return (ks->k); } -/* --- @key_structcreate@ --- * +/* --- @key_mksubkeyiter@ --- * * - * Arguments: @key_data *k@ = pointer to key data block - * @const char *tag@ = pointer to tag string + * Arguments: @key_subkeyiter *i@ = pointer to iterator block + * @key_data *k@ = pointer to key data block + * + * Returns: --- + * + * Use: Initializes a subkey iterator. + */ + +void key_mksubkeyiter(key_subkeyiter *i, key_data *k) +{ + assert(((void)"Key is not structured", + (k->e & KF_ENCMASK) == KENC_STRUCT)); + sym_mkiter(&i->i, &k->u.s); +} + +/* --- @key_nextsubkey@ --- * * - * Returns: Pointer to newly created key data. + * Arguments: @key_structiter *i@ = pointer to iterator block + * @const char **tag@ = where to put the tag pointer, or null + * @key_data **kd@ = where to put the key data pointer, or null * - * Use: Creates a new uninitialized subkey. + * Returns: Nonzero if there was another item, zero if we hit the + * end-stop. + * + * Use: Collects the next subkey of a structured key. */ -key_data *key_structcreate(key_data *k, const char *tag) +int key_nextsubkey(key_subkeyiter *i, const char **tag, key_data **kd) { key_struct *ks; - unsigned f; - assert(((void)"Key is not structured", k->e == KENC_STRUCT)); - ks = sym_find(&k->u.s, tag, -1, sizeof(*ks), &f); - if (f) - key_destroy(&ks->k); - ks->k.e = 0; - return (&ks->k); + if ((ks = sym_next(&i->i)) == 0) + return (0); + if (tag) *tag = SYM_NAME(ks); + if (kd) *kd = ks->k; } -/* --- @key_match@ --- * +/* --- @key_structset@, @key_structsteal@ --- * * * Arguments: @key_data *k@ = pointer to key data block - * @const key_filter *kf@ = pointer to filter block + * @const char *tag@ = pointer to tag string + * @key_data *kd@ = new key data to store * - * Returns: Nonzero if the key matches the filter. + * Returns: --- * - * Use: Checks whether a key matches a filter. + * Use: Creates a new subkey. Stealing doesn't affect @kd@'s + * refcount. If @kd@ is null, the subkey is deleted. */ -int key_match(key_data *k, const key_filter *kf) +static void structset(key_data *k, int stealp, + const char *tag, key_data *kd) { - sym_iter i; key_struct *ks; + unsigned f; - if (!kf) - return (1); - if ((k->e & KF_ENCMASK) != KENC_STRUCT) - return ((k->e & kf->m) == kf->f); - - for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) { - if (key_match(&ks->k, kf)) - return (1); - } - return (0); + assert(((void)"Key is not structured", k->e == KENC_STRUCT)); + if (!kd) { + ks = sym_find(&k->u.s, tag, -1, 0, 0); + if (ks) sym_remove(&k->u.s, ks); + } else { + ks = sym_find(&k->u.s, tag, -1, sizeof(*ks), &f); + if (f) + key_drop(ks->k); + if (!stealp) KEY_INCREF(kd); + ks->k = kd; + } } +void key_structset(key_data *k, const char *tag, key_data *kd) + { structset(k, 0, tag, kd); } +void key_structsteal(key_data *k, const char *tag, key_data *kd) + { structset(k, 1, tag, kd); } + +/*----- Miscellaneous operations ------------------------------------------*/ + /* --- @key_do@ --- * * * Arguments: @key_data *k@ = pointer to key data block @@ -247,537 +390,23 @@ int key_do(key_data *k, const key_filter *kf, dstr *d, if ((k->e & KF_ENCMASK) != KENC_STRUCT) return (func(k, d, p)); else { - sym_iter i; - key_struct *ks; - size_t n; + key_subkeyiter i; + const char *tag; + size_t n = 0; int rc; if (d) n = d->len; - for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) { + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, &tag, &k); ) { if (d) { d->len = n; - dstr_putf(d, ".%s", SYM_NAME(ks)); + dstr_putf(d, ".%s", tag); } - if ((rc = key_do(&ks->k, kf, d, func, p)) != 0) + if ((rc = key_do(k, kf, d, func, p)) != 0) return (rc); } return (0); } } -/*----- Copying -----------------------------------------------------------*/ - -/* --- @key_copy@ --- * - * - * Arguments: @key_data *kd@ = pointer to destination data block - * @key_data *k@ = pointer to source data block - * @const key_filter *kf@ = pointer to filter block - * - * Returns: Nonzero if an item was actually copied. - * - * Use: Copies a chunk of key data from one place to another. - */ - -int key_copy(key_data *kd, key_data *k, const key_filter *kf) -{ - kd->e = k->e; - - if (!KEY_MATCH(kd, kf)) - return (0); - switch (k->e & KF_ENCMASK) { - - /* --- Plain binary data --- */ - - case KENC_BINARY: - case KENC_ENCRYPT: - kd->u.k.k = sub_alloc(k->u.k.sz); - memcpy(kd->u.k.k, k->u.k.k, k->u.k.sz); - kd->u.k.sz = k->u.k.sz; - break; - - /* --- Multiprecision integers --- */ - - case KENC_MP: - kd->u.m = MP_COPY(k->u.m); - break; - - /* --- Structured key data --- */ - - case KENC_STRUCT: { - sym_iter i; - key_struct *ks; - int rc = 0; - - sym_create(&kd->u.s); - for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) { - unsigned f; - key_struct *kks = sym_find(&kd->u.s, SYM_NAME(ks), -1, - sizeof(*kks), &f); - assert(((void)"Duplicate subkey tags", !f)); - if (key_copy(&kks->k, &ks->k, kf)) - rc = 1; - else - sym_remove(&kd->u.s, kks); - } - if (!rc) { - sym_destroy(&kd->u.s); - return (0); - } - } break; - } - return (1); -} - -/*----- Textual encoding --------------------------------------------------*/ - -/* --- @key_read@ --- * - * - * Arguments: @const char *p@ = pointer to textual key representation - * @key_data *k@ = pointer to output block for key data - * @char **pp@ = where to store the end pointer - * - * Returns: Zero if all went well, nonzero if there was a problem. - * - * Use: Parses a textual key description. - */ - -int key_read(const char *p, key_data *k, char **pp) -{ - unsigned e; - - /* --- Read the encoding type --- * - * - * The key format is `[FLAGS:]DATA'. If there is no encoding type - * named, assume that it's `binary' for backwards compatibility. - */ - - if (strchr(p, ':') == 0) - e = 0; - else { - char *q; - if (key_readflags(p, &q, &e, 0)) - return (-1); - p = q + 1; - } - - /* --- Now scan the data based on the encoding type --- */ - - k->e = e; - switch (e & KF_ENCMASK) { - - /* --- Binary encoding --- * - * - * Simply read out the Base64-encoded data. Since `,' and `]' are our - * delimeter characters, and they can't appear in Base64-encoded data, I - * can just do a simple search to find the end of the encoded data. - */ - - case KENC_BINARY: - case KENC_ENCRYPT: { - dstr d = DSTR_INIT; - base64_ctx b; - size_t sz = strcspn(p, ",]"); - - base64_init(&b); - base64_decode(&b, p, sz, &d); - base64_decode(&b, 0, 0, &d); - k->u.k.k = sub_alloc(d.len); - k->u.k.sz = d.len; - memcpy(k->u.k.k, d.buf, d.len); - dstr_destroy(&d); - p += sz; - } break; - - /* --- Multiprecision integer encoding --- * - * - * Multiprecision integers have a convenient reading function. - */ - - case KENC_MP: { - char *q; - mp *m = mp_readstring(MP_NEW, p, &q, 0); - if (!m) - return (-1); - if (k->e & KF_BURN) - mp_burn(m); - k->u.m = m; - p = q; - } break; - - /* --- Structured information encoding --- * - * - * The format for structured key data is `[NAME=KEY,...]', where the - * brackets are part of the syntax. Structured keys have no flags apart - * from the encoding. - * - * The binary encoding only allows names up to 255 bytes long. Check for - * this here. - */ - - case KENC_STRUCT: { - dstr d = DSTR_INIT; - char *q; - - /* --- Read the opening bracket --- */ - - k->e &= KF_ENCMASK; - if (*p != '[') - return (-1); - p++; - sym_create(&k->u.s); - - /* --- Read named key subparts --- */ - - for (;;) { - size_t sz; - key_struct *ks; - - /* --- Stop if there's a close-bracket --- * - * - * This allows `[]' to be an empty structured key, which is good. It - * also makes `[foo=enc:bar,]' legal, and that's less good but I can - * live with it. - */ - - if (*p == ']') - break; - - /* --- Read the name out and check the length --- */ - - if ((q = strchr(p, '=')) == 0) - goto fail; - sz = q - p; - if (sz >= 256) - goto fail; - DRESET(&d); - DPUTM(&d, p, sz); - DPUTZ(&d); - - /* --- Add an appropriate block to the key table --- * - * - * Simply destroy old data if there's already a match. - */ - - { - unsigned f; - ks = sym_find(&k->u.s, d.buf, d.len + 1, sizeof(*ks), &f); - if (f) - key_destroy(&ks->k); - } - - /* --- Read the key data for the subkey --- */ - - if (key_read(q + 1, &ks->k, &q)) { - sym_remove(&k->u.s, ks); - goto fail; - } - p = q; - - /* --- Read the comma or close-bracket --- */ - - if (*p == ']') - break; - else if (*p == ',') - p++; - else - goto fail; - } - - /* --- Step past the close bracket --- */ - - p++; - dstr_destroy(&d); - break; - - /* --- Tidy up after a failure --- */ - - fail: - dstr_destroy(&d); - key_destroy(k); - return (-1); - } break; - - /* --- Anything else is unknown --- */ - - default: - return (-1); - } - - /* --- Return the end pointer --- */ - - if (pp) - *pp = (char *)p; - return (0); -} - -/* --- @key_write@ --- * - * - * Arguments: @key_data *k@ = pointer to key data - * @dstr *d@ = destination string to write on - * @const key_filter *kf@ = pointer to key selection block - * - * Returns: Nonzero if an item was actually written. - * - * Use: Writes a key in a textual encoding. - */ - -int key_write(key_data *k, dstr *d, const key_filter *kf) -{ - int rc = 0; - if (!KEY_MATCH(k, kf)) - return (0); - switch (k->e & KF_ENCMASK) { - case KENC_BINARY: - case KENC_ENCRYPT: { - base64_ctx b; - - if ((k->e & KF_ENCMASK) == KENC_BINARY) - key_writeflags(k->e, d); - else - DPUTS(d, "encrypt,secret"); - DPUTC(d, ':'); - base64_init(&b); - b.indent = ""; - b.maxline = 0; - base64_encode(&b, k->u.k.k, k->u.k.sz, d); - base64_encode(&b, 0, 0, d); - rc = 1; - } break; - case KENC_MP: - key_writeflags(k->e, d); - DPUTC(d, ':'); - mp_writedstr(k->u.m, d, 10); - rc = 1; - break; - case KENC_STRUCT: { - sym_iter i; - key_struct *ks; - char del = 0; - size_t n = d->len; - - DPUTS(d, "struct:["); - for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) { - size_t o = d->len; - if (del) - DPUTC(d, del); - DPUTS(d, SYM_NAME(ks)); - DPUTC(d, '='); - if (!key_write(&ks->k, d, kf)) - d->len = o; - else { - del = ','; - rc = 1; - } - } - if (!rc) - d->len = n; - else - DPUTC(d, ']'); - } break; - } - DPUTZ(d); - - return (rc); -} - -/*----- Binary encoding ---------------------------------------------------*/ - -/* --- @key_decode@ --- * - * - * Arguments: @const void *p@ = pointer to buffer to read - * @size_t sz@ = size of the buffer - * @key_data *k@ = pointer to key data block to write to - * - * Returns: Zero if everything worked, nonzero otherwise. - * - * Use: Decodes a binary representation of a key. - */ - -int key_decode(const void *p, size_t sz, key_data *k) -{ - const octet *q = p; - size_t psz; - unsigned e; - - /* --- Parse the header information --- * - * - * Make sure the size matches external reality. Security holes have been - * known to creep in without this sort of check. (No, this isn't an after- - * the-fact patch-up.) - */ - - e = LOAD16(q); - psz = LOAD16(q + 2); - if (psz + 4 > sz) - return (-1); - k->e = e; - - /* --- Now decide what to do --- */ - - switch (e & KF_ENCMASK) { - - /* --- Plain binary data --- */ - - case KENC_BINARY: - case KENC_ENCRYPT: - k->u.k.k = sub_alloc(psz); - memcpy(k->u.k.k, q + 4, psz); - k->u.k.sz = psz; - break; - - /* --- Multiprecision integer data --- */ - - case KENC_MP: - k->u.m = mp_loadb(MP_NEW, q + 4, psz); - if (k->e & KF_BURN) - mp_burn(k->u.m); - break; - - /* --- Structured key data --- */ - - case KENC_STRUCT: { - dstr d = DSTR_INIT; - key_struct *ks; - unsigned f; - - if ((k->e & ~KF_ENCMASK) || (psz & 3)) - return (-1); - q += 4; - sym_create(&k->u.s); - - while (psz) { - - /* --- Read the tag string --- */ - - DRESET(&d); - sz = LOAD8(q); - if (sz >= psz) - goto fail; - DPUTM(&d, q + 1, sz); - DPUTZ(&d); - sz = (sz + 4) & ~3; - q += sz; psz -= sz; - - /* --- Read the encoding and size --- */ - - e = LOAD16(q); - sz = (LOAD16(q + 2) + 7) & ~3; - if (sz > psz) - goto fail; - - /* --- Create a table node and fill it in --- */ - - ks = sym_find(&k->u.s, d.buf, d.len + 1, sizeof(*ks), &f); - if (f) - goto fail; - if (key_decode(q, sz, &ks->k)) { - sym_remove(&k->u.s, ks); - goto fail; - } - psz -= sz; - q += sz; - } - dstr_destroy(&d); - break; - - /* --- Tidy up after a failure --- */ - - fail: - dstr_destroy(&d); - key_destroy(k); - return (-1); - } break; - - /* --- Everything else --- */ - - default: - return (-1); - } - - /* --- OK, that was good --- */ - - return (0); -} - -/* --- @key_encode@ --- * - * - * Arguments: @key_data *k@ = pointer to key data block - * @dstr *d@ = pointer to destination string - * @const key_filter *kf@ = pointer to key selection block - * - * Returns: Nonzero if an item was actually written. - * - * Use: Encodes a key block as binary data. - */ - -int key_encode(key_data *k, dstr *d, const key_filter *kf) -{ - int rc = 0; - if (!KEY_MATCH(k, kf)) - return (0); - switch (k->e & KF_ENCMASK) { - case KENC_BINARY: - case KENC_ENCRYPT: { - char *p; - - DENSURE(d, (k->u.k.sz + 7) & ~3); - p = d->buf + d->len; - STORE16(p, k->e); - STORE16(p + 2, k->u.k.sz); - d->len += 4; - DPUTM(d, k->u.k.k, k->u.k.sz); - rc = 1; - } break; - - case KENC_MP: { - char *p; - size_t sz = mp_octets(k->u.m); - - DENSURE(d, (sz + 7) & ~3); - p = d->buf + d->len; - STORE16(p, k->e); - STORE16(p + 2, sz); - mp_storeb(k->u.m, p + 4, sz); - d->len += sz + 4; - rc = 1; - } break; - - case KENC_STRUCT: { - size_t n; - char *p; - key_struct *ks; - sym_iter i; - - n = d->len; - DENSURE(d, 4); - p = d->buf + n; - STORE16(p, k->e & KF_ENCMASK); - d->len += 4; - for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) { - size_t o = d->len; - DENSURE(d, 1); - *(octet *)(d->buf + d->len++) = strlen(SYM_NAME(ks)); - DPUTS(d, SYM_NAME(ks)); - while (d->len & 3) - DPUTC(d, 0); - if (key_encode(&ks->k, d, kf)) - rc = 1; - else - d->len = o; - } - if (!rc) - d->len = n; - else { - p = d->buf + n + 2; - n = d->len - n - 4; - STORE16(p, n); - } - } break; - } - while (d->len & 3) - DPUTC(d, 0); - return (rc); -} - /*----- That's all, folks -------------------------------------------------*/