1*2d543d20SAndroid Build Coastguard Worker /* Authors: Joshua Brindle <[email protected]>
2*2d543d20SAndroid Build Coastguard Worker * Jason Tang <[email protected]>
3*2d543d20SAndroid Build Coastguard Worker *
4*2d543d20SAndroid Build Coastguard Worker * Copyright (C) 2005-2006 Tresys Technology, LLC
5*2d543d20SAndroid Build Coastguard Worker *
6*2d543d20SAndroid Build Coastguard Worker * This library is free software; you can redistribute it and/or
7*2d543d20SAndroid Build Coastguard Worker * modify it under the terms of the GNU Lesser General Public
8*2d543d20SAndroid Build Coastguard Worker * License as published by the Free Software Foundation; either
9*2d543d20SAndroid Build Coastguard Worker * version 2.1 of the License, or (at your option) any later version.
10*2d543d20SAndroid Build Coastguard Worker *
11*2d543d20SAndroid Build Coastguard Worker * This library is distributed in the hope that it will be useful,
12*2d543d20SAndroid Build Coastguard Worker * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*2d543d20SAndroid Build Coastguard Worker * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14*2d543d20SAndroid Build Coastguard Worker * Lesser General Public License for more details.
15*2d543d20SAndroid Build Coastguard Worker *
16*2d543d20SAndroid Build Coastguard Worker * You should have received a copy of the GNU Lesser General Public
17*2d543d20SAndroid Build Coastguard Worker * License along with this library; if not, write to the Free Software
18*2d543d20SAndroid Build Coastguard Worker * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19*2d543d20SAndroid Build Coastguard Worker */
20*2d543d20SAndroid Build Coastguard Worker
21*2d543d20SAndroid Build Coastguard Worker #include <assert.h>
22*2d543d20SAndroid Build Coastguard Worker #include <ctype.h>
23*2d543d20SAndroid Build Coastguard Worker #include <stdarg.h>
24*2d543d20SAndroid Build Coastguard Worker #include <stdio.h>
25*2d543d20SAndroid Build Coastguard Worker #include <stdlib.h>
26*2d543d20SAndroid Build Coastguard Worker
27*2d543d20SAndroid Build Coastguard Worker #include <sepol/policydb/flask_types.h>
28*2d543d20SAndroid Build Coastguard Worker #include <sepol/policydb/policydb.h>
29*2d543d20SAndroid Build Coastguard Worker #include <sepol/policydb/util.h>
30*2d543d20SAndroid Build Coastguard Worker
31*2d543d20SAndroid Build Coastguard Worker #include "private.h"
32*2d543d20SAndroid Build Coastguard Worker
33*2d543d20SAndroid Build Coastguard Worker struct val_to_name {
34*2d543d20SAndroid Build Coastguard Worker unsigned int val;
35*2d543d20SAndroid Build Coastguard Worker const char *name;
36*2d543d20SAndroid Build Coastguard Worker };
37*2d543d20SAndroid Build Coastguard Worker
38*2d543d20SAndroid Build Coastguard Worker /* Add an unsigned integer to a dynamically reallocated array. *cnt
39*2d543d20SAndroid Build Coastguard Worker * is a reference pointer to the number of values already within array
40*2d543d20SAndroid Build Coastguard Worker * *a; it will be incremented upon successfully appending i. If *a is
41*2d543d20SAndroid Build Coastguard Worker * NULL then this function will create a new array (*cnt is reset to
42*2d543d20SAndroid Build Coastguard Worker * 0). Return 0 on success, -1 on out of memory. */
add_i_to_a(uint32_t i,uint32_t * cnt,uint32_t ** a)43*2d543d20SAndroid Build Coastguard Worker int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
44*2d543d20SAndroid Build Coastguard Worker {
45*2d543d20SAndroid Build Coastguard Worker uint32_t *new;
46*2d543d20SAndroid Build Coastguard Worker
47*2d543d20SAndroid Build Coastguard Worker if (cnt == NULL || *cnt == UINT32_MAX || a == NULL)
48*2d543d20SAndroid Build Coastguard Worker return -1;
49*2d543d20SAndroid Build Coastguard Worker
50*2d543d20SAndroid Build Coastguard Worker /* FIX ME: This is not very elegant! We use an array that we
51*2d543d20SAndroid Build Coastguard Worker * grow as new uint32_t are added to an array. But rather
52*2d543d20SAndroid Build Coastguard Worker * than be smart about it, for now we realloc() the array each
53*2d543d20SAndroid Build Coastguard Worker * time a new uint32_t is added! */
54*2d543d20SAndroid Build Coastguard Worker if (*a != NULL)
55*2d543d20SAndroid Build Coastguard Worker new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t));
56*2d543d20SAndroid Build Coastguard Worker else { /* empty list */
57*2d543d20SAndroid Build Coastguard Worker
58*2d543d20SAndroid Build Coastguard Worker *cnt = 0;
59*2d543d20SAndroid Build Coastguard Worker new = (uint32_t *) malloc(sizeof(uint32_t));
60*2d543d20SAndroid Build Coastguard Worker }
61*2d543d20SAndroid Build Coastguard Worker if (new == NULL) {
62*2d543d20SAndroid Build Coastguard Worker return -1;
63*2d543d20SAndroid Build Coastguard Worker }
64*2d543d20SAndroid Build Coastguard Worker new[*cnt] = i;
65*2d543d20SAndroid Build Coastguard Worker (*cnt)++;
66*2d543d20SAndroid Build Coastguard Worker *a = new;
67*2d543d20SAndroid Build Coastguard Worker return 0;
68*2d543d20SAndroid Build Coastguard Worker }
69*2d543d20SAndroid Build Coastguard Worker
perm_name(hashtab_key_t key,hashtab_datum_t datum,void * data)70*2d543d20SAndroid Build Coastguard Worker static int perm_name(hashtab_key_t key, hashtab_datum_t datum, void *data)
71*2d543d20SAndroid Build Coastguard Worker {
72*2d543d20SAndroid Build Coastguard Worker struct val_to_name *v = data;
73*2d543d20SAndroid Build Coastguard Worker perm_datum_t *perdatum;
74*2d543d20SAndroid Build Coastguard Worker
75*2d543d20SAndroid Build Coastguard Worker perdatum = (perm_datum_t *) datum;
76*2d543d20SAndroid Build Coastguard Worker
77*2d543d20SAndroid Build Coastguard Worker if (v->val == perdatum->s.value) {
78*2d543d20SAndroid Build Coastguard Worker v->name = key;
79*2d543d20SAndroid Build Coastguard Worker return 1;
80*2d543d20SAndroid Build Coastguard Worker }
81*2d543d20SAndroid Build Coastguard Worker
82*2d543d20SAndroid Build Coastguard Worker return 0;
83*2d543d20SAndroid Build Coastguard Worker }
84*2d543d20SAndroid Build Coastguard Worker
sepol_av_to_string(const policydb_t * policydbp,sepol_security_class_t tclass,sepol_access_vector_t av)85*2d543d20SAndroid Build Coastguard Worker char *sepol_av_to_string(const policydb_t *policydbp, sepol_security_class_t tclass,
86*2d543d20SAndroid Build Coastguard Worker sepol_access_vector_t av)
87*2d543d20SAndroid Build Coastguard Worker {
88*2d543d20SAndroid Build Coastguard Worker struct val_to_name v;
89*2d543d20SAndroid Build Coastguard Worker const class_datum_t *cladatum = policydbp->class_val_to_struct[tclass - 1];
90*2d543d20SAndroid Build Coastguard Worker uint32_t i;
91*2d543d20SAndroid Build Coastguard Worker int rc;
92*2d543d20SAndroid Build Coastguard Worker char *buffer = NULL, *p;
93*2d543d20SAndroid Build Coastguard Worker int len;
94*2d543d20SAndroid Build Coastguard Worker size_t remaining, size = 64;
95*2d543d20SAndroid Build Coastguard Worker
96*2d543d20SAndroid Build Coastguard Worker retry:
97*2d543d20SAndroid Build Coastguard Worker if (__builtin_mul_overflow(size, 2, &size))
98*2d543d20SAndroid Build Coastguard Worker goto err;
99*2d543d20SAndroid Build Coastguard Worker p = realloc(buffer, size);
100*2d543d20SAndroid Build Coastguard Worker if (!p)
101*2d543d20SAndroid Build Coastguard Worker goto err;
102*2d543d20SAndroid Build Coastguard Worker *p = '\0'; /* Just in case there are no permissions */
103*2d543d20SAndroid Build Coastguard Worker buffer = p;
104*2d543d20SAndroid Build Coastguard Worker remaining = size;
105*2d543d20SAndroid Build Coastguard Worker
106*2d543d20SAndroid Build Coastguard Worker for (i = 0; i < cladatum->permissions.nprim; i++) {
107*2d543d20SAndroid Build Coastguard Worker if (av & (UINT32_C(1) << i)) {
108*2d543d20SAndroid Build Coastguard Worker v.val = i + 1;
109*2d543d20SAndroid Build Coastguard Worker rc = hashtab_map(cladatum->permissions.table,
110*2d543d20SAndroid Build Coastguard Worker perm_name, &v);
111*2d543d20SAndroid Build Coastguard Worker if (!rc && cladatum->comdatum) {
112*2d543d20SAndroid Build Coastguard Worker rc = hashtab_map(cladatum->comdatum->
113*2d543d20SAndroid Build Coastguard Worker permissions.table, perm_name,
114*2d543d20SAndroid Build Coastguard Worker &v);
115*2d543d20SAndroid Build Coastguard Worker }
116*2d543d20SAndroid Build Coastguard Worker if (rc == 1) {
117*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, " %s", v.name);
118*2d543d20SAndroid Build Coastguard Worker if (len < 0)
119*2d543d20SAndroid Build Coastguard Worker goto err;
120*2d543d20SAndroid Build Coastguard Worker if ((size_t) len >= remaining)
121*2d543d20SAndroid Build Coastguard Worker goto retry;
122*2d543d20SAndroid Build Coastguard Worker p += len;
123*2d543d20SAndroid Build Coastguard Worker remaining -= len;
124*2d543d20SAndroid Build Coastguard Worker }
125*2d543d20SAndroid Build Coastguard Worker }
126*2d543d20SAndroid Build Coastguard Worker }
127*2d543d20SAndroid Build Coastguard Worker
128*2d543d20SAndroid Build Coastguard Worker return buffer;
129*2d543d20SAndroid Build Coastguard Worker
130*2d543d20SAndroid Build Coastguard Worker err:
131*2d543d20SAndroid Build Coastguard Worker free(buffer);
132*2d543d20SAndroid Build Coastguard Worker return NULL;
133*2d543d20SAndroid Build Coastguard Worker }
134*2d543d20SAndroid Build Coastguard Worker
135*2d543d20SAndroid Build Coastguard Worker #define next_bit_in_range(i, p) (((i) + 1 < sizeof(p)*8) && xperm_test(((i) + 1), p))
136*2d543d20SAndroid Build Coastguard Worker
sepol_extended_perms_to_string(const avtab_extended_perms_t * xperms)137*2d543d20SAndroid Build Coastguard Worker char *sepol_extended_perms_to_string(const avtab_extended_perms_t *xperms)
138*2d543d20SAndroid Build Coastguard Worker {
139*2d543d20SAndroid Build Coastguard Worker uint16_t value;
140*2d543d20SAndroid Build Coastguard Worker uint16_t low_bit;
141*2d543d20SAndroid Build Coastguard Worker uint16_t low_value;
142*2d543d20SAndroid Build Coastguard Worker unsigned int bit;
143*2d543d20SAndroid Build Coastguard Worker unsigned int in_range;
144*2d543d20SAndroid Build Coastguard Worker char *buffer = NULL, *p;
145*2d543d20SAndroid Build Coastguard Worker int len;
146*2d543d20SAndroid Build Coastguard Worker size_t remaining, size = 128;
147*2d543d20SAndroid Build Coastguard Worker
148*2d543d20SAndroid Build Coastguard Worker if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
149*2d543d20SAndroid Build Coastguard Worker && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)
150*2d543d20SAndroid Build Coastguard Worker && (xperms->specified != AVTAB_XPERMS_NLMSG))
151*2d543d20SAndroid Build Coastguard Worker return NULL;
152*2d543d20SAndroid Build Coastguard Worker
153*2d543d20SAndroid Build Coastguard Worker retry:
154*2d543d20SAndroid Build Coastguard Worker if (__builtin_mul_overflow(size, 2, &size))
155*2d543d20SAndroid Build Coastguard Worker goto err;
156*2d543d20SAndroid Build Coastguard Worker p = realloc(buffer, size);
157*2d543d20SAndroid Build Coastguard Worker if (!p)
158*2d543d20SAndroid Build Coastguard Worker goto err;
159*2d543d20SAndroid Build Coastguard Worker buffer = p;
160*2d543d20SAndroid Build Coastguard Worker remaining = size;
161*2d543d20SAndroid Build Coastguard Worker
162*2d543d20SAndroid Build Coastguard Worker if ((xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION)
163*2d543d20SAndroid Build Coastguard Worker || (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
164*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "ioctl { ");
165*2d543d20SAndroid Build Coastguard Worker } else {
166*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "nlmsg { ");
167*2d543d20SAndroid Build Coastguard Worker }
168*2d543d20SAndroid Build Coastguard Worker if (len < 0 || (size_t)len >= remaining)
169*2d543d20SAndroid Build Coastguard Worker goto err;
170*2d543d20SAndroid Build Coastguard Worker p += len;
171*2d543d20SAndroid Build Coastguard Worker remaining -= len;
172*2d543d20SAndroid Build Coastguard Worker
173*2d543d20SAndroid Build Coastguard Worker in_range = 0;
174*2d543d20SAndroid Build Coastguard Worker for (bit = 0; bit < sizeof(xperms->perms)*8; bit++) {
175*2d543d20SAndroid Build Coastguard Worker if (!xperm_test(bit, xperms->perms))
176*2d543d20SAndroid Build Coastguard Worker continue;
177*2d543d20SAndroid Build Coastguard Worker
178*2d543d20SAndroid Build Coastguard Worker if (in_range && next_bit_in_range(bit, xperms->perms)) {
179*2d543d20SAndroid Build Coastguard Worker /* continue until high value found */
180*2d543d20SAndroid Build Coastguard Worker continue;
181*2d543d20SAndroid Build Coastguard Worker } else if (next_bit_in_range(bit, xperms->perms)) {
182*2d543d20SAndroid Build Coastguard Worker /* low value */
183*2d543d20SAndroid Build Coastguard Worker low_bit = bit;
184*2d543d20SAndroid Build Coastguard Worker in_range = 1;
185*2d543d20SAndroid Build Coastguard Worker continue;
186*2d543d20SAndroid Build Coastguard Worker }
187*2d543d20SAndroid Build Coastguard Worker
188*2d543d20SAndroid Build Coastguard Worker if (xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION || xperms->specified == AVTAB_XPERMS_NLMSG) {
189*2d543d20SAndroid Build Coastguard Worker value = xperms->driver<<8 | bit;
190*2d543d20SAndroid Build Coastguard Worker if (in_range) {
191*2d543d20SAndroid Build Coastguard Worker low_value = xperms->driver<<8 | low_bit;
192*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "0x%hx-0x%hx ", low_value, value);
193*2d543d20SAndroid Build Coastguard Worker } else {
194*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "0x%hx ", value);
195*2d543d20SAndroid Build Coastguard Worker }
196*2d543d20SAndroid Build Coastguard Worker } else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
197*2d543d20SAndroid Build Coastguard Worker value = bit << 8;
198*2d543d20SAndroid Build Coastguard Worker if (in_range) {
199*2d543d20SAndroid Build Coastguard Worker low_value = low_bit << 8;
200*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "0x%hx-0x%hx ", low_value, (uint16_t) (value|0xff));
201*2d543d20SAndroid Build Coastguard Worker } else {
202*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "0x%hx-0x%hx ", value, (uint16_t) (value|0xff));
203*2d543d20SAndroid Build Coastguard Worker }
204*2d543d20SAndroid Build Coastguard Worker
205*2d543d20SAndroid Build Coastguard Worker }
206*2d543d20SAndroid Build Coastguard Worker
207*2d543d20SAndroid Build Coastguard Worker if (len < 0)
208*2d543d20SAndroid Build Coastguard Worker goto err;
209*2d543d20SAndroid Build Coastguard Worker if ((size_t) len >= remaining)
210*2d543d20SAndroid Build Coastguard Worker goto retry;
211*2d543d20SAndroid Build Coastguard Worker
212*2d543d20SAndroid Build Coastguard Worker p += len;
213*2d543d20SAndroid Build Coastguard Worker remaining -= len;
214*2d543d20SAndroid Build Coastguard Worker if (in_range)
215*2d543d20SAndroid Build Coastguard Worker in_range = 0;
216*2d543d20SAndroid Build Coastguard Worker }
217*2d543d20SAndroid Build Coastguard Worker
218*2d543d20SAndroid Build Coastguard Worker len = snprintf(p, remaining, "}");
219*2d543d20SAndroid Build Coastguard Worker if (len < 0)
220*2d543d20SAndroid Build Coastguard Worker goto err;
221*2d543d20SAndroid Build Coastguard Worker if ((size_t) len >= remaining)
222*2d543d20SAndroid Build Coastguard Worker goto retry;
223*2d543d20SAndroid Build Coastguard Worker
224*2d543d20SAndroid Build Coastguard Worker return buffer;
225*2d543d20SAndroid Build Coastguard Worker
226*2d543d20SAndroid Build Coastguard Worker err:
227*2d543d20SAndroid Build Coastguard Worker free(buffer);
228*2d543d20SAndroid Build Coastguard Worker return NULL;
229*2d543d20SAndroid Build Coastguard Worker }
230*2d543d20SAndroid Build Coastguard Worker
231*2d543d20SAndroid Build Coastguard Worker /*
232*2d543d20SAndroid Build Coastguard Worker * The tokenize and tokenize_str functions may be used to
233*2d543d20SAndroid Build Coastguard Worker * replace sscanf to read tokens from buffers.
234*2d543d20SAndroid Build Coastguard Worker */
235*2d543d20SAndroid Build Coastguard Worker
236*2d543d20SAndroid Build Coastguard Worker /* Read a token from a buffer */
tokenize_str(char delim,char ** str,const char ** ptr,size_t * len)237*2d543d20SAndroid Build Coastguard Worker static inline int tokenize_str(char delim, char **str, const char **ptr, size_t *len)
238*2d543d20SAndroid Build Coastguard Worker {
239*2d543d20SAndroid Build Coastguard Worker const char *tmp_buf = *ptr;
240*2d543d20SAndroid Build Coastguard Worker *str = NULL;
241*2d543d20SAndroid Build Coastguard Worker
242*2d543d20SAndroid Build Coastguard Worker while (**ptr != '\0') {
243*2d543d20SAndroid Build Coastguard Worker if (isspace(delim) && isspace(**ptr)) {
244*2d543d20SAndroid Build Coastguard Worker (*ptr)++;
245*2d543d20SAndroid Build Coastguard Worker break;
246*2d543d20SAndroid Build Coastguard Worker } else if (!isspace(delim) && **ptr == delim) {
247*2d543d20SAndroid Build Coastguard Worker (*ptr)++;
248*2d543d20SAndroid Build Coastguard Worker break;
249*2d543d20SAndroid Build Coastguard Worker }
250*2d543d20SAndroid Build Coastguard Worker
251*2d543d20SAndroid Build Coastguard Worker (*ptr)++;
252*2d543d20SAndroid Build Coastguard Worker }
253*2d543d20SAndroid Build Coastguard Worker
254*2d543d20SAndroid Build Coastguard Worker *len = *ptr - tmp_buf;
255*2d543d20SAndroid Build Coastguard Worker /* If the end of the string has not been reached, this will ensure the
256*2d543d20SAndroid Build Coastguard Worker * delimiter is not included when returning the token.
257*2d543d20SAndroid Build Coastguard Worker */
258*2d543d20SAndroid Build Coastguard Worker if (**ptr != '\0') {
259*2d543d20SAndroid Build Coastguard Worker (*len)--;
260*2d543d20SAndroid Build Coastguard Worker }
261*2d543d20SAndroid Build Coastguard Worker
262*2d543d20SAndroid Build Coastguard Worker *str = strndup(tmp_buf, *len);
263*2d543d20SAndroid Build Coastguard Worker if (!*str) {
264*2d543d20SAndroid Build Coastguard Worker return -1;
265*2d543d20SAndroid Build Coastguard Worker }
266*2d543d20SAndroid Build Coastguard Worker
267*2d543d20SAndroid Build Coastguard Worker /* Squash spaces if the delimiter is a whitespace character */
268*2d543d20SAndroid Build Coastguard Worker while (**ptr != '\0' && isspace(delim) && isspace(**ptr)) {
269*2d543d20SAndroid Build Coastguard Worker (*ptr)++;
270*2d543d20SAndroid Build Coastguard Worker }
271*2d543d20SAndroid Build Coastguard Worker
272*2d543d20SAndroid Build Coastguard Worker return 0;
273*2d543d20SAndroid Build Coastguard Worker }
274*2d543d20SAndroid Build Coastguard Worker
275*2d543d20SAndroid Build Coastguard Worker /*
276*2d543d20SAndroid Build Coastguard Worker * line_buf - Buffer containing string to tokenize.
277*2d543d20SAndroid Build Coastguard Worker * delim - The delimiter used to tokenize line_buf. A whitespace delimiter will
278*2d543d20SAndroid Build Coastguard Worker * be tokenized using isspace().
279*2d543d20SAndroid Build Coastguard Worker * num_args - The number of parameter entries to process.
280*2d543d20SAndroid Build Coastguard Worker * ... - A 'char **' for each parameter.
281*2d543d20SAndroid Build Coastguard Worker * returns - The number of items processed.
282*2d543d20SAndroid Build Coastguard Worker *
283*2d543d20SAndroid Build Coastguard Worker * This function calls tokenize_str() to do the actual string processing. The
284*2d543d20SAndroid Build Coastguard Worker * caller is responsible for calling free() on each additional argument. The
285*2d543d20SAndroid Build Coastguard Worker * function will not tokenize more than num_args and the last argument will
286*2d543d20SAndroid Build Coastguard Worker * contain the remaining content of line_buf. If the delimiter is any whitespace
287*2d543d20SAndroid Build Coastguard Worker * character, then all whitespace will be squashed.
288*2d543d20SAndroid Build Coastguard Worker */
tokenize(const char * line_buf,char delim,int num_args,...)289*2d543d20SAndroid Build Coastguard Worker int tokenize(const char *line_buf, char delim, int num_args, ...)
290*2d543d20SAndroid Build Coastguard Worker {
291*2d543d20SAndroid Build Coastguard Worker char **arg;
292*2d543d20SAndroid Build Coastguard Worker const char *buf_p;
293*2d543d20SAndroid Build Coastguard Worker int rc, items;
294*2d543d20SAndroid Build Coastguard Worker size_t arg_len = 0;
295*2d543d20SAndroid Build Coastguard Worker va_list ap;
296*2d543d20SAndroid Build Coastguard Worker
297*2d543d20SAndroid Build Coastguard Worker buf_p = line_buf;
298*2d543d20SAndroid Build Coastguard Worker
299*2d543d20SAndroid Build Coastguard Worker /* Process the arguments */
300*2d543d20SAndroid Build Coastguard Worker va_start(ap, num_args);
301*2d543d20SAndroid Build Coastguard Worker
302*2d543d20SAndroid Build Coastguard Worker for (items = 0; items < num_args && *buf_p != '\0'; items++) {
303*2d543d20SAndroid Build Coastguard Worker arg = va_arg(ap, char **);
304*2d543d20SAndroid Build Coastguard Worker
305*2d543d20SAndroid Build Coastguard Worker /* Save the remainder of the string in arg */
306*2d543d20SAndroid Build Coastguard Worker if (items == num_args - 1) {
307*2d543d20SAndroid Build Coastguard Worker *arg = strdup(buf_p);
308*2d543d20SAndroid Build Coastguard Worker if (*arg == NULL) {
309*2d543d20SAndroid Build Coastguard Worker goto exit;
310*2d543d20SAndroid Build Coastguard Worker }
311*2d543d20SAndroid Build Coastguard Worker
312*2d543d20SAndroid Build Coastguard Worker continue;
313*2d543d20SAndroid Build Coastguard Worker }
314*2d543d20SAndroid Build Coastguard Worker
315*2d543d20SAndroid Build Coastguard Worker rc = tokenize_str(delim, arg, &buf_p, &arg_len);
316*2d543d20SAndroid Build Coastguard Worker if (rc < 0) {
317*2d543d20SAndroid Build Coastguard Worker goto exit;
318*2d543d20SAndroid Build Coastguard Worker }
319*2d543d20SAndroid Build Coastguard Worker }
320*2d543d20SAndroid Build Coastguard Worker
321*2d543d20SAndroid Build Coastguard Worker exit:
322*2d543d20SAndroid Build Coastguard Worker va_end(ap);
323*2d543d20SAndroid Build Coastguard Worker return items;
324*2d543d20SAndroid Build Coastguard Worker }
325