1caf54c4fSMartin Matuska /*- 2*bd66c1b4SMartin Matuska * SPDX-License-Identifier: BSD-2-Clause 3*bd66c1b4SMartin Matuska * 4caf54c4fSMartin Matuska * Copyright (c) 2003-2007 Tim Kientzle 5caf54c4fSMartin Matuska * All rights reserved. 6caf54c4fSMartin Matuska */ 7caf54c4fSMartin Matuska 8caf54c4fSMartin Matuska 9caf54c4fSMartin Matuska #include "cpio_platform.h" 10caf54c4fSMartin Matuska 11caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H 12caf54c4fSMartin Matuska #include <errno.h> 13caf54c4fSMartin Matuska #endif 14caf54c4fSMartin Matuska #ifdef HAVE_GRP_H 15caf54c4fSMartin Matuska #include <grp.h> 16caf54c4fSMartin Matuska #endif 17caf54c4fSMartin Matuska #ifdef HAVE_PWD_H 18caf54c4fSMartin Matuska #include <pwd.h> 19caf54c4fSMartin Matuska #endif 20caf54c4fSMartin Matuska #include <stdio.h> 21caf54c4fSMartin Matuska #ifdef HAVE_STDLIB_H 22caf54c4fSMartin Matuska #include <stdlib.h> 23caf54c4fSMartin Matuska #endif 24caf54c4fSMartin Matuska #ifdef HAVE_STRING_H 25caf54c4fSMartin Matuska #include <string.h> 26caf54c4fSMartin Matuska #endif 27caf54c4fSMartin Matuska 28caf54c4fSMartin Matuska #include "cpio.h" 29caf54c4fSMartin Matuska #include "err.h" 30caf54c4fSMartin Matuska 31caf54c4fSMartin Matuska /* 32caf54c4fSMartin Matuska * Short options for cpio. Please keep this sorted. 33caf54c4fSMartin Matuska */ 34ddce862aSMartin Matuska static const char *short_options = "067AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuVvW:yZz"; 35caf54c4fSMartin Matuska 36caf54c4fSMartin Matuska /* 37caf54c4fSMartin Matuska * Long options for cpio. Please keep this sorted. 38caf54c4fSMartin Matuska */ 39caf54c4fSMartin Matuska static const struct option { 40caf54c4fSMartin Matuska const char *name; 41caf54c4fSMartin Matuska int required; /* 1 if this option requires an argument */ 42caf54c4fSMartin Matuska int equivalent; /* Equivalent short option. */ 43caf54c4fSMartin Matuska } cpio_longopts[] = { 44acc60b03SMartin Matuska { "b64encode", 0, OPTION_B64ENCODE }, 45ddce862aSMartin Matuska { "binary", 0, '7' }, 46caf54c4fSMartin Matuska { "create", 0, 'o' }, 47cfa49a9bSMartin Matuska { "dereference", 0, 'L' }, 486c95142eSMartin Matuska { "dot", 0, 'V' }, 49caf54c4fSMartin Matuska { "extract", 0, 'i' }, 50caf54c4fSMartin Matuska { "file", 1, 'F' }, 51caf54c4fSMartin Matuska { "format", 1, 'H' }, 52acc60b03SMartin Matuska { "grzip", 0, OPTION_GRZIP }, 53caf54c4fSMartin Matuska { "help", 0, 'h' }, 54caf54c4fSMartin Matuska { "insecure", 0, OPTION_INSECURE }, 55caf54c4fSMartin Matuska { "link", 0, 'l' }, 56caf54c4fSMartin Matuska { "list", 0, 't' }, 57acc60b03SMartin Matuska { "lrzip", 0, OPTION_LRZIP }, 58cdf63a70SMartin Matuska { "lz4", 0, OPTION_LZ4 }, 59caf54c4fSMartin Matuska { "lzma", 0, OPTION_LZMA }, 60acc60b03SMartin Matuska { "lzop", 0, OPTION_LZOP }, 61caf54c4fSMartin Matuska { "make-directories", 0, 'd' }, 62caf54c4fSMartin Matuska { "no-preserve-owner", 0, OPTION_NO_PRESERVE_OWNER }, 63caf54c4fSMartin Matuska { "null", 0, '0' }, 64caf54c4fSMartin Matuska { "numeric-uid-gid", 0, 'n' }, 65caf54c4fSMartin Matuska { "owner", 1, 'R' }, 66cdf63a70SMartin Matuska { "passphrase", 1, OPTION_PASSPHRASE }, 67caf54c4fSMartin Matuska { "pass-through", 0, 'p' }, 68caf54c4fSMartin Matuska { "preserve-modification-time", 0, 'm' }, 69caf54c4fSMartin Matuska { "preserve-owner", 0, OPTION_PRESERVE_OWNER }, 70ddce862aSMartin Matuska { "pwb", 0, '6' }, 71caf54c4fSMartin Matuska { "quiet", 0, OPTION_QUIET }, 72caf54c4fSMartin Matuska { "unconditional", 0, 'u' }, 73acc60b03SMartin Matuska { "uuencode", 0, OPTION_UUENCODE }, 74caf54c4fSMartin Matuska { "verbose", 0, 'v' }, 75caf54c4fSMartin Matuska { "version", 0, OPTION_VERSION }, 76caf54c4fSMartin Matuska { "xz", 0, 'J' }, 775c831a5bSMartin Matuska { "zstd", 0, OPTION_ZSTD }, 78caf54c4fSMartin Matuska { NULL, 0, 0 } 79caf54c4fSMartin Matuska }; 80caf54c4fSMartin Matuska 81caf54c4fSMartin Matuska /* 82caf54c4fSMartin Matuska * I used to try to select platform-provided getopt() or 83caf54c4fSMartin Matuska * getopt_long(), but that caused a lot of headaches. In particular, 84caf54c4fSMartin Matuska * I couldn't consistently use long options in the test harness 85caf54c4fSMartin Matuska * because not all platforms have getopt_long(). That in turn led to 86caf54c4fSMartin Matuska * overuse of the -W hack in the test harness, which made it rough to 87caf54c4fSMartin Matuska * run the test harness against GNU cpio. (I periodically run the 88caf54c4fSMartin Matuska * test harness here against GNU cpio as a sanity-check. Yes, 89caf54c4fSMartin Matuska * I've found a couple of bugs in GNU cpio that way.) 90caf54c4fSMartin Matuska */ 91caf54c4fSMartin Matuska int 92caf54c4fSMartin Matuska cpio_getopt(struct cpio *cpio) 93caf54c4fSMartin Matuska { 94caf54c4fSMartin Matuska enum { state_start = 0, state_next_word, state_short, state_long }; 95caf54c4fSMartin Matuska static int state = state_start; 96caf54c4fSMartin Matuska static char *opt_word; 97caf54c4fSMartin Matuska 9813d826ffSMartin Matuska const struct option *popt, *match, *match2; 9913d826ffSMartin Matuska const char *p, *long_prefix; 100caf54c4fSMartin Matuska size_t optlength; 10113d826ffSMartin Matuska int opt; 10213d826ffSMartin Matuska int required; 103caf54c4fSMartin Matuska 10413d826ffSMartin Matuska again: 10513d826ffSMartin Matuska match = NULL; 10613d826ffSMartin Matuska match2 = NULL; 10713d826ffSMartin Matuska long_prefix = "--"; 10813d826ffSMartin Matuska opt = '?'; 10913d826ffSMartin Matuska required = 0; 1106c95142eSMartin Matuska cpio->argument = NULL; 111caf54c4fSMartin Matuska 112caf54c4fSMartin Matuska /* First time through, initialize everything. */ 113caf54c4fSMartin Matuska if (state == state_start) { 114caf54c4fSMartin Matuska /* Skip program name. */ 115caf54c4fSMartin Matuska ++cpio->argv; 116caf54c4fSMartin Matuska --cpio->argc; 117caf54c4fSMartin Matuska state = state_next_word; 118caf54c4fSMartin Matuska } 119caf54c4fSMartin Matuska 120caf54c4fSMartin Matuska /* 121caf54c4fSMartin Matuska * We're ready to look at the next word in argv. 122caf54c4fSMartin Matuska */ 123caf54c4fSMartin Matuska if (state == state_next_word) { 124caf54c4fSMartin Matuska /* No more arguments, so no more options. */ 125caf54c4fSMartin Matuska if (cpio->argv[0] == NULL) 126caf54c4fSMartin Matuska return (-1); 127caf54c4fSMartin Matuska /* Doesn't start with '-', so no more options. */ 128caf54c4fSMartin Matuska if (cpio->argv[0][0] != '-') 129caf54c4fSMartin Matuska return (-1); 130caf54c4fSMartin Matuska /* "--" marks end of options; consume it and return. */ 131caf54c4fSMartin Matuska if (strcmp(cpio->argv[0], "--") == 0) { 132caf54c4fSMartin Matuska ++cpio->argv; 133caf54c4fSMartin Matuska --cpio->argc; 134caf54c4fSMartin Matuska return (-1); 135caf54c4fSMartin Matuska } 136caf54c4fSMartin Matuska /* Get next word for parsing. */ 137caf54c4fSMartin Matuska opt_word = *cpio->argv++; 138caf54c4fSMartin Matuska --cpio->argc; 139caf54c4fSMartin Matuska if (opt_word[1] == '-') { 140caf54c4fSMartin Matuska /* Set up long option parser. */ 141caf54c4fSMartin Matuska state = state_long; 142caf54c4fSMartin Matuska opt_word += 2; /* Skip leading '--' */ 143caf54c4fSMartin Matuska } else { 144caf54c4fSMartin Matuska /* Set up short option parser. */ 145caf54c4fSMartin Matuska state = state_short; 146caf54c4fSMartin Matuska ++opt_word; /* Skip leading '-' */ 147caf54c4fSMartin Matuska } 148caf54c4fSMartin Matuska } 149caf54c4fSMartin Matuska 150caf54c4fSMartin Matuska /* 151caf54c4fSMartin Matuska * We're parsing a group of POSIX-style single-character options. 152caf54c4fSMartin Matuska */ 153caf54c4fSMartin Matuska if (state == state_short) { 154caf54c4fSMartin Matuska /* Peel next option off of a group of short options. */ 155caf54c4fSMartin Matuska opt = *opt_word++; 156caf54c4fSMartin Matuska if (opt == '\0') { 157caf54c4fSMartin Matuska /* End of this group; recurse to get next option. */ 158caf54c4fSMartin Matuska state = state_next_word; 15913d826ffSMartin Matuska goto again; 160caf54c4fSMartin Matuska } 161caf54c4fSMartin Matuska 162caf54c4fSMartin Matuska /* Does this option take an argument? */ 163caf54c4fSMartin Matuska p = strchr(short_options, opt); 164caf54c4fSMartin Matuska if (p == NULL) 165caf54c4fSMartin Matuska return ('?'); 166caf54c4fSMartin Matuska if (p[1] == ':') 167caf54c4fSMartin Matuska required = 1; 168caf54c4fSMartin Matuska 169caf54c4fSMartin Matuska /* If it takes an argument, parse that. */ 170caf54c4fSMartin Matuska if (required) { 171caf54c4fSMartin Matuska /* If arg is run-in, opt_word already points to it. */ 172caf54c4fSMartin Matuska if (opt_word[0] == '\0') { 173caf54c4fSMartin Matuska /* Otherwise, pick up the next word. */ 174caf54c4fSMartin Matuska opt_word = *cpio->argv; 175caf54c4fSMartin Matuska if (opt_word == NULL) { 176caf54c4fSMartin Matuska lafe_warnc(0, 177caf54c4fSMartin Matuska "Option -%c requires an argument", 178caf54c4fSMartin Matuska opt); 179caf54c4fSMartin Matuska return ('?'); 180caf54c4fSMartin Matuska } 181caf54c4fSMartin Matuska ++cpio->argv; 182caf54c4fSMartin Matuska --cpio->argc; 183caf54c4fSMartin Matuska } 184caf54c4fSMartin Matuska if (opt == 'W') { 185caf54c4fSMartin Matuska state = state_long; 186caf54c4fSMartin Matuska long_prefix = "-W "; /* For clearer errors. */ 187caf54c4fSMartin Matuska } else { 188caf54c4fSMartin Matuska state = state_next_word; 1896c95142eSMartin Matuska cpio->argument = opt_word; 190caf54c4fSMartin Matuska } 191caf54c4fSMartin Matuska } 192caf54c4fSMartin Matuska } 193caf54c4fSMartin Matuska 194caf54c4fSMartin Matuska /* We're reading a long option, including -W long=arg convention. */ 195caf54c4fSMartin Matuska if (state == state_long) { 196caf54c4fSMartin Matuska /* After this long option, we'll be starting a new word. */ 197caf54c4fSMartin Matuska state = state_next_word; 198caf54c4fSMartin Matuska 199caf54c4fSMartin Matuska /* Option name ends at '=' if there is one. */ 200caf54c4fSMartin Matuska p = strchr(opt_word, '='); 201caf54c4fSMartin Matuska if (p != NULL) { 202caf54c4fSMartin Matuska optlength = (size_t)(p - opt_word); 2036c95142eSMartin Matuska cpio->argument = (char *)(uintptr_t)(p + 1); 204caf54c4fSMartin Matuska } else { 205caf54c4fSMartin Matuska optlength = strlen(opt_word); 206caf54c4fSMartin Matuska } 207caf54c4fSMartin Matuska 208caf54c4fSMartin Matuska /* Search the table for an unambiguous match. */ 209caf54c4fSMartin Matuska for (popt = cpio_longopts; popt->name != NULL; popt++) { 210caf54c4fSMartin Matuska /* Short-circuit if first chars don't match. */ 211caf54c4fSMartin Matuska if (popt->name[0] != opt_word[0]) 212caf54c4fSMartin Matuska continue; 213caf54c4fSMartin Matuska /* If option is a prefix of name in table, record it.*/ 214caf54c4fSMartin Matuska if (strncmp(opt_word, popt->name, optlength) == 0) { 215caf54c4fSMartin Matuska match2 = match; /* Record up to two matches. */ 216caf54c4fSMartin Matuska match = popt; 217caf54c4fSMartin Matuska /* If it's an exact match, we're done. */ 218caf54c4fSMartin Matuska if (strlen(popt->name) == optlength) { 219caf54c4fSMartin Matuska match2 = NULL; /* Forget the others. */ 220caf54c4fSMartin Matuska break; 221caf54c4fSMartin Matuska } 222caf54c4fSMartin Matuska } 223caf54c4fSMartin Matuska } 224caf54c4fSMartin Matuska 225caf54c4fSMartin Matuska /* Fail if there wasn't a unique match. */ 226caf54c4fSMartin Matuska if (match == NULL) { 227caf54c4fSMartin Matuska lafe_warnc(0, 228caf54c4fSMartin Matuska "Option %s%s is not supported", 229caf54c4fSMartin Matuska long_prefix, opt_word); 230caf54c4fSMartin Matuska return ('?'); 231caf54c4fSMartin Matuska } 232caf54c4fSMartin Matuska if (match2 != NULL) { 233caf54c4fSMartin Matuska lafe_warnc(0, 234caf54c4fSMartin Matuska "Ambiguous option %s%s (matches --%s and --%s)", 235caf54c4fSMartin Matuska long_prefix, opt_word, match->name, match2->name); 236caf54c4fSMartin Matuska return ('?'); 237caf54c4fSMartin Matuska } 238caf54c4fSMartin Matuska 239caf54c4fSMartin Matuska /* We've found a unique match; does it need an argument? */ 240caf54c4fSMartin Matuska if (match->required) { 241caf54c4fSMartin Matuska /* Argument required: get next word if necessary. */ 2426c95142eSMartin Matuska if (cpio->argument == NULL) { 2436c95142eSMartin Matuska cpio->argument = *cpio->argv; 2446c95142eSMartin Matuska if (cpio->argument == NULL) { 245caf54c4fSMartin Matuska lafe_warnc(0, 246caf54c4fSMartin Matuska "Option %s%s requires an argument", 247caf54c4fSMartin Matuska long_prefix, match->name); 248caf54c4fSMartin Matuska return ('?'); 249caf54c4fSMartin Matuska } 250caf54c4fSMartin Matuska ++cpio->argv; 251caf54c4fSMartin Matuska --cpio->argc; 252caf54c4fSMartin Matuska } 253caf54c4fSMartin Matuska } else { 254caf54c4fSMartin Matuska /* Argument forbidden: fail if there is one. */ 2556c95142eSMartin Matuska if (cpio->argument != NULL) { 256caf54c4fSMartin Matuska lafe_warnc(0, 257caf54c4fSMartin Matuska "Option %s%s does not allow an argument", 258caf54c4fSMartin Matuska long_prefix, match->name); 259caf54c4fSMartin Matuska return ('?'); 260caf54c4fSMartin Matuska } 261caf54c4fSMartin Matuska } 262caf54c4fSMartin Matuska return (match->equivalent); 263caf54c4fSMartin Matuska } 264caf54c4fSMartin Matuska 265caf54c4fSMartin Matuska return (opt); 266caf54c4fSMartin Matuska } 267caf54c4fSMartin Matuska 268caf54c4fSMartin Matuska 269caf54c4fSMartin Matuska /* 270caf54c4fSMartin Matuska * Parse the argument to the -R or --owner flag. 271caf54c4fSMartin Matuska * 272caf54c4fSMartin Matuska * The format is one of the following: 273caf54c4fSMartin Matuska * <username|uid> - Override user but not group 274caf54c4fSMartin Matuska * <username>: - Override both, group is user's default group 275caf54c4fSMartin Matuska * <uid>: - Override user but not group 276caf54c4fSMartin Matuska * <username|uid>:<groupname|gid> - Override both 277caf54c4fSMartin Matuska * :<groupname|gid> - Override group but not user 278caf54c4fSMartin Matuska * 279caf54c4fSMartin Matuska * Where uid/gid are decimal representations and groupname/username 280caf54c4fSMartin Matuska * are names to be looked up in system database. Note that we try 281caf54c4fSMartin Matuska * to look up an argument as a name first, then try numeric parsing. 282caf54c4fSMartin Matuska * 283caf54c4fSMartin Matuska * A period can be used instead of the colon. 284caf54c4fSMartin Matuska * 285caf54c4fSMartin Matuska * Sets uid/gid return as appropriate, -1 indicates uid/gid not specified. 286584ad8fdSMartin Matuska * TODO: If the spec uses uname/gname, then return those to the caller 287584ad8fdSMartin Matuska * as well. If the spec provides uid/gid, just return names as NULL. 288caf54c4fSMartin Matuska * 289caf54c4fSMartin Matuska * Returns NULL if no error, otherwise returns error string for display. 290caf54c4fSMartin Matuska * 291caf54c4fSMartin Matuska */ 292*bd66c1b4SMartin Matuska int 293*bd66c1b4SMartin Matuska owner_parse(const char *spec, struct cpio_owner *owner, const char **errmsg) 294caf54c4fSMartin Matuska { 295caf54c4fSMartin Matuska static char errbuff[128]; 296caf54c4fSMartin Matuska const char *u, *ue, *g; 297caf54c4fSMartin Matuska 298*bd66c1b4SMartin Matuska owner->uid = -1; 299*bd66c1b4SMartin Matuska owner->gid = -1; 300caf54c4fSMartin Matuska 301*bd66c1b4SMartin Matuska owner->uname = NULL; 302*bd66c1b4SMartin Matuska owner->gname = NULL; 303*bd66c1b4SMartin Matuska 304*bd66c1b4SMartin Matuska if (spec[0] == '\0') { 305*bd66c1b4SMartin Matuska *errmsg = "Invalid empty user/group spec"; 306*bd66c1b4SMartin Matuska return (-1); 307*bd66c1b4SMartin Matuska } 308caf54c4fSMartin Matuska 309caf54c4fSMartin Matuska /* 310caf54c4fSMartin Matuska * Split spec into [user][:.][group] 311caf54c4fSMartin Matuska * u -> first char of username, NULL if no username 312caf54c4fSMartin Matuska * ue -> first char after username (colon, period, or \0) 313caf54c4fSMartin Matuska * g -> first char of group name 314caf54c4fSMartin Matuska */ 315caf54c4fSMartin Matuska if (*spec == ':' || *spec == '.') { 316caf54c4fSMartin Matuska /* If spec starts with ':' or '.', then just group. */ 317caf54c4fSMartin Matuska ue = u = NULL; 318caf54c4fSMartin Matuska g = spec + 1; 319caf54c4fSMartin Matuska } else { 320caf54c4fSMartin Matuska /* Otherwise, [user] or [user][:] or [user][:][group] */ 321caf54c4fSMartin Matuska ue = u = spec; 322caf54c4fSMartin Matuska while (*ue != ':' && *ue != '.' && *ue != '\0') 323caf54c4fSMartin Matuska ++ue; 324caf54c4fSMartin Matuska g = ue; 325caf54c4fSMartin Matuska if (*g != '\0') /* Skip : or . to find first char of group. */ 326caf54c4fSMartin Matuska ++g; 327caf54c4fSMartin Matuska } 328caf54c4fSMartin Matuska 329caf54c4fSMartin Matuska if (u != NULL) { 330caf54c4fSMartin Matuska /* Look up user: ue is first char after end of user. */ 331caf54c4fSMartin Matuska char *user; 332caf54c4fSMartin Matuska struct passwd *pwent; 333caf54c4fSMartin Matuska 334*bd66c1b4SMartin Matuska user = malloc(ue - u + 1); 335caf54c4fSMartin Matuska if (user == NULL) 336*bd66c1b4SMartin Matuska goto alloc_error; 337caf54c4fSMartin Matuska memcpy(user, u, ue - u); 338caf54c4fSMartin Matuska user[ue - u] = '\0'; 339caf54c4fSMartin Matuska if ((pwent = getpwnam(user)) != NULL) { 340*bd66c1b4SMartin Matuska owner->uid = pwent->pw_uid; 341*bd66c1b4SMartin Matuska owner->uname = strdup(pwent->pw_name); 342*bd66c1b4SMartin Matuska if (owner->uname == NULL) { 343*bd66c1b4SMartin Matuska free(user); 344*bd66c1b4SMartin Matuska goto alloc_error; 345*bd66c1b4SMartin Matuska } 346caf54c4fSMartin Matuska if (*ue != '\0') 347*bd66c1b4SMartin Matuska owner->gid = pwent->pw_gid; 348caf54c4fSMartin Matuska } else { 349caf54c4fSMartin Matuska char *end; 350caf54c4fSMartin Matuska errno = 0; 351*bd66c1b4SMartin Matuska owner->uid = (int)strtoul(user, &end, 10); 352caf54c4fSMartin Matuska if (errno || *end != '\0') { 353caf54c4fSMartin Matuska snprintf(errbuff, sizeof(errbuff), 354caf54c4fSMartin Matuska "Couldn't lookup user ``%s''", user); 355caf54c4fSMartin Matuska errbuff[sizeof(errbuff) - 1] = '\0'; 356fd082e96SMartin Matuska free(user); 357*bd66c1b4SMartin Matuska *errmsg = errbuff; 358*bd66c1b4SMartin Matuska return (-1); 359caf54c4fSMartin Matuska } 360caf54c4fSMartin Matuska } 361caf54c4fSMartin Matuska free(user); 362caf54c4fSMartin Matuska } 363caf54c4fSMartin Matuska 364caf54c4fSMartin Matuska if (*g != '\0') { 365caf54c4fSMartin Matuska struct group *grp; 366caf54c4fSMartin Matuska if ((grp = getgrnam(g)) != NULL) { 367*bd66c1b4SMartin Matuska owner->gid = grp->gr_gid; 368*bd66c1b4SMartin Matuska owner->gname = strdup(grp->gr_name); 369*bd66c1b4SMartin Matuska if (owner->gname == NULL) { 370*bd66c1b4SMartin Matuska free(owner->uname); 371*bd66c1b4SMartin Matuska owner->uname = NULL; 372*bd66c1b4SMartin Matuska goto alloc_error; 373*bd66c1b4SMartin Matuska } 374caf54c4fSMartin Matuska } else { 375caf54c4fSMartin Matuska char *end; 376caf54c4fSMartin Matuska errno = 0; 377*bd66c1b4SMartin Matuska owner->gid = (int)strtoul(g, &end, 10); 378caf54c4fSMartin Matuska if (errno || *end != '\0') { 379caf54c4fSMartin Matuska snprintf(errbuff, sizeof(errbuff), 380caf54c4fSMartin Matuska "Couldn't lookup group ``%s''", g); 381caf54c4fSMartin Matuska errbuff[sizeof(errbuff) - 1] = '\0'; 382*bd66c1b4SMartin Matuska *errmsg = errbuff; 383*bd66c1b4SMartin Matuska return (-1); 384caf54c4fSMartin Matuska } 385caf54c4fSMartin Matuska } 386caf54c4fSMartin Matuska } 387*bd66c1b4SMartin Matuska return (0); 388*bd66c1b4SMartin Matuska alloc_error: 389*bd66c1b4SMartin Matuska *errmsg = "Couldn't allocate memory"; 390*bd66c1b4SMartin Matuska return (-1); 391caf54c4fSMartin Matuska } 392