summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rpcsvc/nis/utils/nis_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/rpcsvc/nis/utils/nis_util.c')
-rw-r--r--usr/src/cmd/rpcsvc/nis/utils/nis_util.c869
1 files changed, 869 insertions, 0 deletions
diff --git a/usr/src/cmd/rpcsvc/nis/utils/nis_util.c b/usr/src/cmd/rpcsvc/nis/utils/nis_util.c
new file mode 100644
index 0000000000..f1907da713
--- /dev/null
+++ b/usr/src/cmd/rpcsvc/nis/utils/nis_util.c
@@ -0,0 +1,869 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * nis_util.c
+ *
+ * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpcsvc/nis.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <stdlib.h>
+
+/*
+ * nisname_index()
+ *
+ * Return pointer to place in string s of first match with char c.
+ * Do not match between quotes ("...").
+ *
+ * Totally rewritten as part of the fix for bug# 1263305.
+ * XXX - This can now probably be replaced by strchr_quotes().
+ */
+char *
+nisname_index(char *s, char c)
+{
+ do {
+ if (*s == '"') {
+ s++;
+ while (*s && *s != '"')
+ s++;
+ } else if (*s == c)
+ return (s);
+ } while (*s++);
+ return (NULL);
+}
+
+/*
+ * Parse a passed name into a basename and search criteria.
+ * if there is no criteria present the *crit == 0. You must
+ * pass in allocated data for the three strings.
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+int
+nisname_split(name, base, crit, max_len)
+ char *name;
+ char *base;
+ char *crit;
+ int max_len;
+{
+ register char *p, *q;
+
+ p = name;
+ while (*p && (isspace(*p)))
+ p++;
+ if (*p != '[') {
+ *crit = 0;
+ return (strlcpy(base, p, max_len) >= max_len);
+ }
+
+ /* it has a criteria, copy the whole thing in */
+ if (strlcpy(crit, p, max_len) >= max_len)
+ return (1);
+ q = nisname_index(crit, ']');
+ if (! q) {
+ *crit = 0; /* error condition */
+ *base = 0;
+ return (1);
+ }
+ q++;
+ if (*q == ',') {
+ *q = 0;
+ q++;
+ }
+ if (strlcpy(base, q, max_len) >= max_len)
+ return (1);
+ *q = 0; /* just in case there wasn't a comma */
+
+ return (0);
+}
+
+bool_t
+nis_verifycred(n, flags)
+ nis_name n;
+ uint_t flags;
+{
+ nis_result *res;
+ int err;
+ char dname[NIS_MAXNAMELEN];
+
+ (void) snprintf(dname, sizeof (dname), "[cname=%s],cred.org_dir.%s", n,
+ nis_domain_of(n));
+ res = nis_list(dname, flags, NULL, NULL);
+ err = (res->status == NIS_SUCCESS);
+ nis_freeresult(res);
+ return (err);
+}
+
+#define NIS_ALL_ACC (NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|NIS_DESTROY_ACC)
+
+static int
+parse_rights_field(rights, shift, p)
+ uint_t *rights;
+ int shift;
+ char *p;
+{
+ int set;
+
+ while (*p && (*p != ',')) {
+ switch (*p) {
+ case '=':
+ *rights &= ~(NIS_ALL_ACC << shift);
+ case '+':
+ set = 1;
+ break;
+ case '-':
+ set = 0;
+ break;
+ default:
+ return (0);
+ }
+ for (p++; *p && (*p != ',') && (*p != '=') && (*p != '+') &&
+ (*p != '-'); p++) {
+ switch (*p) {
+ case 'r':
+ if (set)
+ *rights |= (NIS_READ_ACC << shift);
+ else
+ *rights &= ~(NIS_READ_ACC << shift);
+ break;
+ case 'm':
+ if (set)
+ *rights |= (NIS_MODIFY_ACC << shift);
+ else
+ *rights &= ~(NIS_MODIFY_ACC << shift);
+ break;
+ case 'c':
+ if (set)
+ *rights |= (NIS_CREATE_ACC << shift);
+ else
+ *rights &= ~(NIS_CREATE_ACC << shift);
+ break;
+ case 'd':
+ if (set)
+ *rights |= (NIS_DESTROY_ACC << shift);
+ else
+ *rights &= ~(NIS_DESTROY_ACC << shift);
+ break;
+ default:
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
+
+#define NIS_NOBODY_FLD 1
+#define NIS_OWNER_FLD 2
+#define NIS_GROUP_FLD 4
+#define NIS_WORLD_FLD 8
+#define NIS_ALL_FLD NIS_OWNER_FLD|NIS_GROUP_FLD|NIS_WORLD_FLD
+
+int
+parse_rights(rights, p)
+ uint_t *rights;
+ char *p;
+{
+ uint_t f;
+
+ if (p)
+ while (*p) {
+ for (f = 0; (*p != '=') && (*p != '+') && (*p != '-');
+ p++)
+ switch (*p) {
+ case 'n':
+ f |= NIS_NOBODY_FLD;
+ break;
+ case 'o':
+ f |= NIS_OWNER_FLD;
+ break;
+ case 'g':
+ f |= NIS_GROUP_FLD;
+ break;
+ case 'w':
+ f |= NIS_WORLD_FLD;
+ break;
+ case 'a':
+ f |= NIS_ALL_FLD;
+ break;
+ default:
+ return (0);
+ }
+ if (f == 0)
+ f = NIS_ALL_FLD;
+
+ if ((f & NIS_NOBODY_FLD) &&
+ !parse_rights_field(rights, 24, p))
+ return (0);
+
+ if ((f & NIS_OWNER_FLD) &&
+ !parse_rights_field(rights, 16, p))
+ return (0);
+
+ if ((f & NIS_GROUP_FLD) &&
+ !parse_rights_field(rights, 8, p))
+ return (0);
+
+ if ((f & NIS_WORLD_FLD) &&
+ !parse_rights_field(rights, 0, p))
+ return (0);
+
+ while (*(++p))
+ if (*p == ',') {
+ p++;
+ break;
+ }
+ }
+ return (1);
+}
+
+
+int
+parse_flags(flags, p)
+ uint_t *flags;
+ char *p;
+{
+ if (p) {
+ while (*p) {
+ switch (*(p++)) {
+ case 'B':
+ *flags |= TA_BINARY;
+ break;
+ case 'X':
+ *flags |= TA_XDR;
+ break;
+ case 'S':
+ *flags |= TA_SEARCHABLE;
+ break;
+ case 'I':
+ *flags |= TA_CASE;
+ break;
+ case 'C':
+ *flags |= TA_CRYPT;
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+ } else {
+ fprintf(stderr,
+ "Invalid table schema: At least one column must be searchable.\n");
+ exit(1);
+ }
+}
+
+
+int
+parse_time(time, p)
+ uint32_t *time;
+ char *p;
+{
+ char *s;
+ uint32_t x;
+
+ *time = 0;
+
+ if (p)
+ while (*p) {
+ if (!isdigit(*p))
+ return (0);
+ x = strtol(p, &s, 10);
+ switch (*s) {
+ case '\0':
+ (*time) += x;
+ p = s;
+ break;
+ case 's':
+ case 'S':
+ (*time) += x;
+ p = s+1;
+ break;
+ case 'm':
+ case 'M':
+ (*time) += x*60;
+ p = s+1;
+ break;
+ case 'h':
+ case 'H':
+ (*time) += x*(60*60);
+ p = s+1;
+ break;
+ case 'd':
+ case 'D':
+ (*time) += x*(24*60*60);
+ p = s+1;
+ break;
+ default:
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+
+static int
+nis_getsubopt(optionsp, tokens, sep, valuep)
+ char **optionsp;
+ char * const *tokens;
+ const int sep; /* if this is a char we get an alignment error */
+ char **valuep;
+{
+ register char *s = *optionsp, *p, *q;
+ register int i, optlen;
+
+ *valuep = NULL;
+ if (*s == '\0')
+ return (-1);
+ q = strchr(s, (char)sep); /* find next option */
+ if (q == NULL) {
+ q = s + strlen(s);
+ } else {
+ *q++ = '\0'; /* mark end and point to next */
+ }
+ p = strchr(s, '='); /* find value */
+ if (p == NULL) {
+ optlen = strlen(s);
+ *valuep = NULL;
+ } else {
+ optlen = p - s;
+ *valuep = ++p;
+ }
+ for (i = 0; tokens[i] != NULL; i++) {
+ if ((optlen == strlen(tokens[i])) &&
+ (strncmp(s, tokens[i], optlen) == 0)) {
+ /* point to next option only if success */
+ *optionsp = q;
+ return (i);
+ }
+ }
+ /* no match, point value at option and return error */
+ *valuep = s;
+ return (-1);
+}
+
+
+nis_object nis_default_obj;
+
+/*
+ * We record the source of the defaults.
+ * 0 => default
+ * 1 => from NIS_DEFAULTS env variable
+ * 2 => from arg passed to nis_defaults_init
+ */
+#define NIS_SRC_DEFAULT 0
+#define NIS_SRC_ENV 1
+#define NIS_SRC_ARG 2
+
+int nis_default_owner_src = NIS_SRC_DEFAULT;
+int nis_default_group_src = NIS_SRC_DEFAULT;
+int nis_default_access_src = NIS_SRC_DEFAULT;
+int nis_default_ttl_src = NIS_SRC_DEFAULT;
+
+static char *nis_defaults_tokens[] = {
+ "owner",
+ "group",
+ "access",
+ "ttl",
+ 0
+};
+
+#define T_OWNER 0
+#define T_GROUP 1
+#define T_ACCESS 2
+#define T_TTL 3
+
+static int
+nis_defaults_set(optstr, src)
+ char *optstr;
+ int src;
+{
+ char str[1024], *p, *v;
+ int i;
+
+ if (strlcpy(str, optstr, sizeof (str)) >= sizeof (str))
+ return (0);
+ p = str;
+
+ while ((i = nis_getsubopt(&p, nis_defaults_tokens, ':', &v)) != -1) {
+ switch (i) {
+ case T_OWNER:
+ if (v == 0 || v[strlen(v)-1] != '.')
+ return (0);
+ nis_default_obj.zo_owner = strdup(v);
+ nis_default_owner_src = src;
+ break;
+ case T_GROUP:
+ if (v == 0 || v[strlen(v)-1] != '.')
+ return (0);
+ nis_default_obj.zo_group = strdup(v);
+ nis_default_group_src = src;
+ break;
+ case T_ACCESS:
+ if ((v == 0) ||
+ (!parse_rights(&(nis_default_obj.zo_access), v)))
+ return (0);
+ nis_default_access_src = src;
+ break;
+ case T_TTL:
+ if ((v == 0) ||
+ !(parse_time(&(nis_default_obj.zo_ttl), v)))
+ return (0);
+ nis_default_ttl_src = src;
+ break;
+ }
+ }
+
+ if (*p)
+ return (0);
+
+ return (1);
+}
+
+extern char *getenv();
+
+int
+nis_defaults_init(optstr)
+ char *optstr;
+{
+ char *envstr;
+
+ /* XXX calling this multiple times may leak memory */
+ memset((char *)&nis_default_obj, 0, sizeof (nis_default_obj));
+
+ nis_default_obj.zo_owner = nis_local_principal();
+ nis_default_obj.zo_group = nis_local_group();
+ nis_default_obj.zo_access = DEFAULT_RIGHTS;
+ nis_default_obj.zo_ttl = 12 * 60 * 60;
+
+ if (envstr = getenv("NIS_DEFAULTS"))
+ if (!nis_defaults_set(envstr, NIS_SRC_ENV)) {
+ fprintf(stderr,
+ "can't parse NIS_DEFAULTS environment variable.\n");
+ return (0);
+ }
+
+ if (optstr)
+ if (!nis_defaults_set(optstr, NIS_SRC_ARG)) {
+ fprintf(stderr, "can't parse nis_defaults argument.\n");
+ return (0);
+ }
+
+ return (1);
+}
+
+
+
+/*
+ * Converts an NIS+ entry object for a passwd table to its
+ * pwent structure.
+ * XXX: This function returns a pointer to a static structure.
+ */
+static struct passwd *
+nis_object_to_pwent(obj, error)
+ nis_object *obj;
+ nis_error *error;
+{
+ static struct passwd pw;
+ static char spacebuf[1024]; /* The pwent structure points to this */
+ static char nullstring; /* used for NULL data */
+ char *tmp;
+ char *end;
+
+ memset((void *)&pw, 0, sizeof (struct passwd));
+ memset((void *)&spacebuf[0], 0, 1024);
+ tmp = &spacebuf[0];
+ end = tmp + sizeof (spacebuf);
+
+ if ((obj->zo_data.zo_type != NIS_ENTRY_OBJ) ||
+ (obj->EN_data.en_cols.en_cols_len < 8)) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ }
+ if (ENTRY_LEN(obj, 0) == 0) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 0),
+ end > tmp ? end - tmp : 0);
+ pw.pw_name = tmp;
+ tmp += strlen(pw.pw_name) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 1) == 0) {
+ pw.pw_passwd = &nullstring;
+ } else {
+ /* XXX: Should I be returning X here? */
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 1),
+ end > tmp ? end - tmp : 0);
+ pw.pw_passwd = tmp;
+ tmp += strlen(pw.pw_passwd) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 2) == 0) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ }
+ pw.pw_uid = atoi(ENTRY_VAL(obj, 2));
+
+ if (ENTRY_LEN(obj, 3) == 0)
+ pw.pw_gid = 0; /* Is this default value? */
+ else
+ pw.pw_gid = atoi(ENTRY_VAL(obj, 3));
+
+ if (ENTRY_LEN(obj, 4) == 0) {
+ pw.pw_gecos = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 4),
+ end > tmp ? end - tmp : 0);
+ pw.pw_gecos = tmp;
+ tmp += strlen(pw.pw_gecos) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 5) == 0) {
+ pw.pw_dir = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 5),
+ end > tmp ? end - tmp : 0);
+ pw.pw_dir = tmp;
+ tmp += strlen(pw.pw_dir) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 6) == 0) {
+ pw.pw_shell = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 6),
+ end > tmp ? end - tmp : 0);
+ pw.pw_shell = tmp;
+ tmp += strlen(pw.pw_shell) + 1;
+ }
+
+ pw.pw_age = &nullstring;
+ pw.pw_comment = &nullstring;
+ *error = NIS_SUCCESS;
+ return (&pw);
+}
+
+
+/*
+ * This will go to the NIS+ master to get the data. This code
+ * is ugly because the internals of the switch had to be opened
+ * up here. Wish there was a way to pass a MASTER_ONLY flag
+ * to getpwuid() and all such getXbyY() calls. Some of this code
+ * is being copied from the NIS+ switch backend.
+ *
+ * XXX: We will not bother to make this MT-safe. If any of the callers
+ * for this function want to use getpwuid_r(), then a corresponding
+ * function will have to written.
+ */
+struct passwd *
+getpwuid_nisplus_master(domain, uid, error)
+ char *domain;
+ uid_t uid;
+ nis_error *error;
+{
+ struct passwd *passwd_ent;
+ nis_result *res;
+ char namebuf[NIS_MAXNAMELEN];
+ uint_t flags;
+
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "[uid=%ld],passwd.org_dir.%s", uid, domain);
+ flags = EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH|USE_DGRAM|MASTER_ONLY;
+ res = nis_list(namebuf, flags, 0, 0);
+ if (res == NULL) {
+ *error = NIS_NOMEMORY;
+ return (NULL);
+ }
+ if (res->status != NIS_SUCCESS) {
+ nis_freeresult(res);
+ *error = res->status;
+ return (NULL);
+ }
+ if (NIS_RES_NUMOBJ(res) == 0) {
+ nis_freeresult(res);
+ *error = NIS_NOTFOUND;
+ return (NULL);
+ }
+
+ passwd_ent = nis_object_to_pwent(NIS_RES_OBJECT(res), error);
+ nis_freeresult(res);
+ return (passwd_ent);
+}
+
+/*
+ * Converts an NIS+ entry object for a shadow table to its
+ * spwent structure. We only fill in the sp_namp and sp_pwdp fields.
+ * XXX: This function returns a pointer to a static structure.
+ */
+static struct spwd *
+nis_object_to_spwent(obj, error)
+ nis_object *obj;
+ nis_error *error;
+{
+ static struct spwd spw;
+ static char spacebuf[1024]; /* The pwent structure points to this */
+ static char nullstring; /* used for NULL data */
+ char *tmp;
+ char *end;
+
+ memset((void *)&spw, 0, sizeof (struct spwd));
+ memset((void *)&spacebuf[0], 0, 1024);
+ tmp = &spacebuf[0];
+ end = tmp + sizeof (spacebuf);
+
+ if ((obj->zo_data.zo_type != NIS_ENTRY_OBJ) ||
+ (obj->EN_data.en_cols.en_cols_len < 8)) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ }
+ if (ENTRY_LEN(obj, 0) == 0) {
+ *error = NIS_INVALIDOBJ;
+ return (NULL);
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 0),
+ end > tmp ? end - tmp : 0);
+ spw.sp_namp = tmp;
+ tmp += strlen(spw.sp_namp) + 1;
+ }
+
+ if (ENTRY_LEN(obj, 1) == 0) {
+ spw.sp_pwdp = &nullstring;
+ } else {
+ (void) strlcpy(tmp, ENTRY_VAL(obj, 1),
+ end > tmp ? end - tmp : 0);
+ spw.sp_pwdp = tmp;
+ tmp += strlen(spw.sp_pwdp) + 1;
+ }
+
+ *error = NIS_SUCCESS;
+ return (&spw);
+}
+
+
+/*
+ * This will go to the NIS+ master to get the data. This code
+ * is ugly because the internals of the switch had to be opened
+ * up here. Wish there was a way to pass a MASTER_ONLY flag
+ * to getspnam() and all such getXbyY() calls. Some of this code
+ * is being copied from the NIS+ switch backend.
+ *
+ * XXX: We will not bother to make this MT-safe. If any of the callers
+ * for this function want to use getpwuid_r(), then a corresponding
+ * function will have to written.
+ */
+struct spwd *
+getspnam_nisplus_master(domain, name, error)
+ char *domain;
+ char *name;
+ nis_error *error;
+{
+ struct spwd *shadow_ent;
+ nis_result *res;
+ char namebuf[NIS_MAXNAMELEN];
+ uint_t flags;
+
+ (void) snprintf(namebuf, sizeof (namebuf),
+ "[name=%s],passwd.org_dir.%s", name, domain);
+ flags = EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH|USE_DGRAM|MASTER_ONLY;
+ res = nis_list(namebuf, flags, 0, 0);
+ if (res == NULL) {
+ *error = NIS_NOMEMORY;
+ return (NULL);
+ }
+ if (res->status != NIS_SUCCESS) {
+ nis_freeresult(res);
+ *error = res->status;
+ return (NULL);
+ }
+ if (NIS_RES_NUMOBJ(res) == 0) {
+ nis_freeresult(res);
+ *error = NIS_NOTFOUND;
+ return (NULL);
+ }
+
+ shadow_ent = nis_object_to_spwent(NIS_RES_OBJECT(res), error);
+ nis_freeresult(res);
+ return (shadow_ent);
+}
+
+/* begin bug# 1263305 */
+
+/*
+ * __nis_quote_key()
+ *
+ * Enclose NIS+ terminating characters ']' and ',' within '"' and
+ * escape '"' by doubling it so the string is safe to pass to
+ * nis_list(3N) and associated routines. For example, a src string
+ * of "foo[]bar" will result in a dst string of ""foo["]"bar""
+ */
+char *
+__nis_quote_key(const char *src, char *dst, int dstsize)
+{
+
+ char *dstorig = dst;
+ int dstleft;
+
+ dstleft = dstsize - 4;
+ for (; *src != '\0' && dstleft > 0; src++) {
+ switch (*src) {
+ case ']':
+ case ',':
+ /* quote the character */
+ *dst++ = '"';
+ *dst++ = *src;
+ *dst++ = '"';
+ dstleft -= 3;
+ break;
+
+ case '"':
+ /* double the quote */
+ *dst++ = '"';
+ dstleft--;
+ /* fall through... */
+
+ default:
+ *dst++ = *src;
+ dstleft--;
+ break;
+ }
+ }
+ *dst = '\0';
+
+ if (*src)
+ fprintf(stderr,
+ "nis_quote_key: warning: src string too long\n");
+
+ return (dstorig);
+}
+
+/*
+ * *str* routines below originated in libc.
+ */
+
+/*
+ * strpbrk_quotes()
+ *
+ * Like strpbrk except it will not match target chars inside quoted
+ * strings ("...").
+ */
+char *
+strpbrk_quotes(char *string, char *brkset)
+{
+ register const char *p;
+
+ do {
+ for (p = brkset; *p != '\0' && *string != '"' && *p != *string;
+ ++p)
+ ;
+
+ if (*string == '"') {
+ string++;
+ while (*string != '\0' && *string != '"')
+ string++;
+ } else if (*p != '\0')
+ return ((char *)string);
+ }
+ while (*string++);
+ return (NULL);
+}
+
+
+/*
+ * strtok_r_quotes()
+ *
+ * uses strpbrk_quotes and strspn to break string into tokens on
+ * sequentially subsequent calls. returns NULL when no
+ * non-separator characters remain.
+ * `subsequent' calls are calls with first argument NULL.
+ *
+ */
+
+static char *
+strtok_r_quotes(char *string, char *sepset, char **lasts)
+{
+ char *q, *r;
+
+ /* first or subsequent call */
+ if (string == NULL)
+ string = *lasts;
+
+ if (string == 0) /* return if no tokens remaining */
+ return (NULL);
+
+ q = string + strspn(string, sepset); /* skip leading separators */
+
+ if (*q == '\0') /* return if no tokens remaining */
+ return (NULL);
+
+ if ((r = strpbrk_quotes(q, sepset)) == NULL) /* move past token */
+ *lasts = 0; /* indicate this is last token */
+ else {
+ *r = '\0';
+ *lasts = r+1;
+ }
+ return (q);
+}
+
+
+/*
+ * strtok_quotes()
+ *
+ * Like strtok except it will not match target chars within quoted
+ * strings ("...").
+ */
+char *
+strtok_quotes(char *string, char *sepset)
+{
+ static char *lasts;
+
+ return (strtok_r_quotes(string, sepset, &lasts));
+}
+
+/*
+ * strchr_quotes()
+ *
+ * Like strchr but will not match within quoted strings ("...").
+ */
+char *
+strchr_quotes(char *s, char c)
+{
+ do {
+ if (*s == '"') {
+ s++;
+ while (*s && *s != '"')
+ s++;
+ } else if (*s == c)
+ return (s);
+ } while (*s++);
+ return (NULL);
+}
+
+/* end bug# 1263305 */