1 |
|
|
/* $OpenBSD: getcap.c,v 1.34 2016/09/21 04:38:56 guenther Exp $ */ |
2 |
|
|
/*- |
3 |
|
|
* Copyright (c) 1992, 1993 |
4 |
|
|
* The Regents of the University of California. All rights reserved. |
5 |
|
|
* |
6 |
|
|
* This code is derived from software contributed to Berkeley by |
7 |
|
|
* Casey Leedom of Lawrence Livermore National Laboratory. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
|
* documentation and/or other materials provided with the distribution. |
17 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
18 |
|
|
* may be used to endorse or promote products derived from this software |
19 |
|
|
* without specific prior written permission. |
20 |
|
|
* |
21 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
22 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
25 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 |
|
|
* SUCH DAMAGE. |
32 |
|
|
*/ |
33 |
|
|
|
34 |
|
|
#include <sys/types.h> |
35 |
|
|
|
36 |
|
|
#include <ctype.h> |
37 |
|
|
#include <db.h> |
38 |
|
|
#include <errno.h> |
39 |
|
|
#include <fcntl.h> |
40 |
|
|
#include <limits.h> |
41 |
|
|
#include <stdio.h> |
42 |
|
|
#include <stdlib.h> |
43 |
|
|
#include <string.h> |
44 |
|
|
#include <unistd.h> |
45 |
|
|
|
46 |
|
|
#define BFRAG 1024 |
47 |
|
|
#define BSIZE 1024 |
48 |
|
|
#define ESC ('[' & 037) /* ASCII ESC */ |
49 |
|
|
#define MAX_RECURSION 32 /* maximum getent recursion */ |
50 |
|
|
#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ |
51 |
|
|
|
52 |
|
|
#define RECOK (char)0 |
53 |
|
|
#define TCERR (char)1 |
54 |
|
|
#define SHADOW (char)2 |
55 |
|
|
|
56 |
|
|
static size_t topreclen; /* toprec length */ |
57 |
|
|
static char *toprec; /* Additional record specified by cgetset() */ |
58 |
|
|
static int gottoprec; /* Flag indicating retrieval of toprecord */ |
59 |
|
|
|
60 |
|
|
static int cdbget(DB *, char **, const char *); |
61 |
|
|
static int getent(char **, u_int *, char **, FILE *, const char *, int, char *); |
62 |
|
|
static int nfcmp(const char *, char *); |
63 |
|
|
|
64 |
|
|
static int usedb = 1; |
65 |
|
|
|
66 |
|
|
/* |
67 |
|
|
* Cgetusedb() allows the user to specify whether or not to use a .db |
68 |
|
|
* version of the database file (if it exists) in preference to the |
69 |
|
|
* text version. By default, the getcap(3) routines will use a .db file. |
70 |
|
|
*/ |
71 |
|
|
int |
72 |
|
|
cgetusedb(int new_usedb) |
73 |
|
|
{ |
74 |
|
|
int old_usedb = usedb; |
75 |
|
|
|
76 |
|
|
usedb = new_usedb; |
77 |
|
|
return(old_usedb); |
78 |
|
|
} |
79 |
|
|
DEF_WEAK(cgetusedb); |
80 |
|
|
|
81 |
|
|
/* |
82 |
|
|
* Cgetset() allows the addition of a user specified buffer to be added |
83 |
|
|
* to the database array, in effect "pushing" the buffer on top of the |
84 |
|
|
* virtual database. 0 is returned on success, -1 on failure. |
85 |
|
|
*/ |
86 |
|
|
int |
87 |
|
|
cgetset(const char *ent) |
88 |
|
|
{ |
89 |
|
|
if (ent == NULL) { |
90 |
|
|
free(toprec); |
91 |
|
|
toprec = NULL; |
92 |
|
|
topreclen = 0; |
93 |
|
|
return (0); |
94 |
|
|
} |
95 |
|
|
topreclen = strlen(ent); |
96 |
|
|
if ((toprec = malloc(topreclen + 1)) == NULL) |
97 |
|
|
return (-1); |
98 |
|
|
gottoprec = 0; |
99 |
|
|
memcpy(toprec, ent, topreclen + 1); |
100 |
|
|
return (0); |
101 |
|
|
} |
102 |
|
|
DEF_WEAK(cgetset); |
103 |
|
|
|
104 |
|
|
/* |
105 |
|
|
* Cgetcap searches the capability record buf for the capability cap with |
106 |
|
|
* type `type'. A pointer to the value of cap is returned on success, NULL |
107 |
|
|
* if the requested capability couldn't be found. |
108 |
|
|
* |
109 |
|
|
* Specifying a type of ':' means that nothing should follow cap (:cap:). |
110 |
|
|
* In this case a pointer to the terminating ':' or NUL will be returned if |
111 |
|
|
* cap is found. |
112 |
|
|
* |
113 |
|
|
* If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) |
114 |
|
|
* return NULL. |
115 |
|
|
*/ |
116 |
|
|
char * |
117 |
|
|
cgetcap(char *buf, const char *cap, int type) |
118 |
|
|
{ |
119 |
|
|
char *bp; |
120 |
|
|
const char *cp; |
121 |
|
|
|
122 |
|
|
bp = buf; |
123 |
|
|
for (;;) { |
124 |
|
|
/* |
125 |
|
|
* Skip past the current capability field - it's either the |
126 |
|
|
* name field if this is the first time through the loop, or |
127 |
|
|
* the remainder of a field whose name failed to match cap. |
128 |
|
|
*/ |
129 |
|
|
for (;;) |
130 |
|
|
if (*bp == '\0') |
131 |
|
|
return (NULL); |
132 |
|
|
else |
133 |
|
|
if (*bp++ == ':') |
134 |
|
|
break; |
135 |
|
|
|
136 |
|
|
/* |
137 |
|
|
* Try to match (cap, type) in buf. |
138 |
|
|
*/ |
139 |
|
|
for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) |
140 |
|
|
continue; |
141 |
|
|
if (*cp != '\0') |
142 |
|
|
continue; |
143 |
|
|
if (*bp == '@') |
144 |
|
|
return (NULL); |
145 |
|
|
if (type == ':') { |
146 |
|
|
if (*bp != '\0' && *bp != ':') |
147 |
|
|
continue; |
148 |
|
|
return(bp); |
149 |
|
|
} |
150 |
|
|
if (*bp != type) |
151 |
|
|
continue; |
152 |
|
|
bp++; |
153 |
|
|
return (*bp == '@' ? NULL : bp); |
154 |
|
|
} |
155 |
|
|
/* NOTREACHED */ |
156 |
|
|
} |
157 |
|
|
DEF_WEAK(cgetcap); |
158 |
|
|
|
159 |
|
|
/* |
160 |
|
|
* Cgetent extracts the capability record name from the NULL terminated file |
161 |
|
|
* array db_array and returns a pointer to a malloc'd copy of it in buf. |
162 |
|
|
* Buf must be retained through all subsequent calls to cgetcap, cgetnum, |
163 |
|
|
* cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, |
164 |
|
|
* -1 if the requested record couldn't be found, -2 if a system error was |
165 |
|
|
* encountered (couldn't open/read a file, etc.), and -3 if a potential |
166 |
|
|
* reference loop is detected. |
167 |
|
|
*/ |
168 |
|
|
int |
169 |
|
|
cgetent(char **buf, char **db_array, const char *name) |
170 |
|
|
{ |
171 |
|
|
u_int dummy; |
172 |
|
|
|
173 |
|
|
return (getent(buf, &dummy, db_array, NULL, name, 0, NULL)); |
174 |
|
|
} |
175 |
|
|
DEF_WEAK(cgetent); |
176 |
|
|
|
177 |
|
|
/* |
178 |
|
|
* Getent implements the functions of cgetent. If fp is non-NULL, |
179 |
|
|
* *db_array has already been opened and fp is the open file descriptor. We |
180 |
|
|
* do this to save time and avoid using up file descriptors for tc= |
181 |
|
|
* recursions. |
182 |
|
|
* |
183 |
|
|
* Getent returns the same success/failure codes as cgetent. On success, a |
184 |
|
|
* pointer to a malloc'ed capability record with all tc= capabilities fully |
185 |
|
|
* expanded and its length (not including trailing ASCII NUL) are left in |
186 |
|
|
* *cap and *len. |
187 |
|
|
* |
188 |
|
|
* Basic algorithm: |
189 |
|
|
* + Allocate memory incrementally as needed in chunks of size BFRAG |
190 |
|
|
* for capability buffer. |
191 |
|
|
* + Recurse for each tc=name and interpolate result. Stop when all |
192 |
|
|
* names interpolated, a name can't be found, or depth exceeds |
193 |
|
|
* MAX_RECURSION. |
194 |
|
|
*/ |
195 |
|
|
static int |
196 |
|
|
getent(char **cap, u_int *len, char **db_array, FILE *fp, |
197 |
|
|
const char *name, int depth, char *nfield) |
198 |
|
|
{ |
199 |
|
|
DB *capdbp; |
200 |
|
|
char *r_end, *rp, **db_p; |
201 |
|
|
int myfd, eof, foundit, opened, retval, clen; |
202 |
|
|
char *record, *cbuf; |
203 |
|
|
int tc_not_resolved; |
204 |
|
|
char pbuf[PATH_MAX]; |
205 |
|
|
|
206 |
|
|
/* |
207 |
|
|
* Return with ``loop detected'' error if we've recursed more than |
208 |
|
|
* MAX_RECURSION times. |
209 |
|
|
*/ |
210 |
|
|
if (depth > MAX_RECURSION) |
211 |
|
|
return (-3); |
212 |
|
|
|
213 |
|
|
opened = 0; |
214 |
|
|
|
215 |
|
|
/* |
216 |
|
|
* Check if we have a top record from cgetset(). |
217 |
|
|
*/ |
218 |
|
|
if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { |
219 |
|
|
opened++; |
220 |
|
|
if ((record = malloc(topreclen + 1 + BFRAG)) == NULL) |
221 |
|
|
return (-2); |
222 |
|
|
memcpy(record, toprec, topreclen + 1); |
223 |
|
|
myfd = 0; |
224 |
|
|
db_p = db_array; |
225 |
|
|
rp = record + topreclen + 1; |
226 |
|
|
r_end = rp + BFRAG; |
227 |
|
|
goto tc_exp; |
228 |
|
|
} |
229 |
|
|
/* |
230 |
|
|
* Allocate first chunk of memory. |
231 |
|
|
*/ |
232 |
|
|
if ((record = malloc(BFRAG)) == NULL) |
233 |
|
|
return (-2); |
234 |
|
|
r_end = record + BFRAG; |
235 |
|
|
foundit = 0; |
236 |
|
|
/* |
237 |
|
|
* Loop through database array until finding the record. |
238 |
|
|
*/ |
239 |
|
|
|
240 |
|
|
for (db_p = db_array; *db_p != NULL; db_p++) { |
241 |
|
|
eof = 0; |
242 |
|
|
|
243 |
|
|
/* |
244 |
|
|
* Open database if not already open. |
245 |
|
|
*/ |
246 |
|
|
if (fp != NULL) { |
247 |
|
|
(void)fseek(fp, 0L, SEEK_SET); |
248 |
|
|
myfd = 0; |
249 |
|
|
opened++; |
250 |
|
|
} else { |
251 |
|
|
char *dbrecord; |
252 |
|
|
|
253 |
|
|
clen = snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); |
254 |
|
|
if (clen != -1 && clen < sizeof(pbuf) && usedb && |
255 |
|
|
(capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))) { |
256 |
|
|
opened++; |
257 |
|
|
retval = cdbget(capdbp, &dbrecord, name); |
258 |
|
|
if (retval < 0) { |
259 |
|
|
/* no record available */ |
260 |
|
|
(void)capdbp->close(capdbp); |
261 |
|
|
continue; |
262 |
|
|
} |
263 |
|
|
free(record); |
264 |
|
|
/* save the data; close frees it */ |
265 |
|
|
clen = strlen(dbrecord); |
266 |
|
|
if ((cbuf = malloc(clen + 1)) == NULL) |
267 |
|
|
return (-2); |
268 |
|
|
memcpy(cbuf, dbrecord, clen + 1); |
269 |
|
|
if (capdbp->close(capdbp) < 0) { |
270 |
|
|
free(cbuf); |
271 |
|
|
return (-2); |
272 |
|
|
} |
273 |
|
|
/* assume tc='s have been expanded??? */ |
274 |
|
|
*len = clen; |
275 |
|
|
*cap = cbuf; |
276 |
|
|
return (retval); |
277 |
|
|
} else { |
278 |
|
|
fp = fopen(*db_p, "re"); |
279 |
|
|
if (fp == NULL) { |
280 |
|
|
/* No error on unfound file. */ |
281 |
|
|
continue; |
282 |
|
|
} |
283 |
|
|
myfd = 1; |
284 |
|
|
opened++; |
285 |
|
|
} |
286 |
|
|
} |
287 |
|
|
/* |
288 |
|
|
* Find the requested capability record ... |
289 |
|
|
*/ |
290 |
|
|
{ |
291 |
|
|
char buf[BUFSIZ]; |
292 |
|
|
char *b_end, *bp; |
293 |
|
|
int c; |
294 |
|
|
|
295 |
|
|
/* |
296 |
|
|
* Loop invariants: |
297 |
|
|
* There is always room for one more character in record. |
298 |
|
|
* R_end always points just past end of record. |
299 |
|
|
* Rp always points just past last character in record. |
300 |
|
|
* B_end always points just past last character in buf. |
301 |
|
|
* Bp always points at next character in buf. |
302 |
|
|
*/ |
303 |
|
|
b_end = buf; |
304 |
|
|
bp = buf; |
305 |
|
|
for (;;) { |
306 |
|
|
|
307 |
|
|
/* |
308 |
|
|
* Read in a line implementing (\, newline) |
309 |
|
|
* line continuation. |
310 |
|
|
*/ |
311 |
|
|
rp = record; |
312 |
|
|
for (;;) { |
313 |
|
|
if (bp >= b_end) { |
314 |
|
|
size_t n; |
315 |
|
|
|
316 |
|
|
n = fread(buf, 1, sizeof(buf), fp); |
317 |
|
|
if (n == 0) { |
318 |
|
|
eof = feof(fp); |
319 |
|
|
if (myfd) |
320 |
|
|
(void)fclose(fp); |
321 |
|
|
if (eof) { |
322 |
|
|
fp = NULL; |
323 |
|
|
break; |
324 |
|
|
} |
325 |
|
|
free(record); |
326 |
|
|
return (-2); |
327 |
|
|
} |
328 |
|
|
b_end = buf+n; |
329 |
|
|
bp = buf; |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
c = *bp++; |
333 |
|
|
if (c == '\n') { |
334 |
|
|
if (rp > record && *(rp-1) == '\\') { |
335 |
|
|
rp--; |
336 |
|
|
continue; |
337 |
|
|
} else |
338 |
|
|
break; |
339 |
|
|
} |
340 |
|
|
*rp++ = c; |
341 |
|
|
|
342 |
|
|
/* |
343 |
|
|
* Enforce loop invariant: if no room |
344 |
|
|
* left in record buffer, try to get |
345 |
|
|
* some more. |
346 |
|
|
*/ |
347 |
|
|
if (rp >= r_end) { |
348 |
|
|
size_t pos; |
349 |
|
|
size_t newsize; |
350 |
|
|
char *nrecord; |
351 |
|
|
|
352 |
|
|
pos = rp - record; |
353 |
|
|
newsize = r_end - record + BFRAG; |
354 |
|
|
nrecord = realloc(record, newsize); |
355 |
|
|
if (nrecord == NULL) { |
356 |
|
|
free(record); |
357 |
|
|
if (myfd) |
358 |
|
|
(void)fclose(fp); |
359 |
|
|
errno = ENOMEM; |
360 |
|
|
return (-2); |
361 |
|
|
} |
362 |
|
|
record = nrecord; |
363 |
|
|
r_end = record + newsize; |
364 |
|
|
rp = record + pos; |
365 |
|
|
} |
366 |
|
|
} |
367 |
|
|
/* loop invariant lets us do this */ |
368 |
|
|
*rp++ = '\0'; |
369 |
|
|
|
370 |
|
|
/* |
371 |
|
|
* If encountered EOF check next file. |
372 |
|
|
*/ |
373 |
|
|
if (eof) |
374 |
|
|
break; |
375 |
|
|
|
376 |
|
|
/* |
377 |
|
|
* Toss blank lines and comments. |
378 |
|
|
*/ |
379 |
|
|
if (*record == '\0' || *record == '#') |
380 |
|
|
continue; |
381 |
|
|
|
382 |
|
|
/* |
383 |
|
|
* See if this is the record we want ... |
384 |
|
|
*/ |
385 |
|
|
if (cgetmatch(record, name) == 0) { |
386 |
|
|
if (nfield == NULL || !nfcmp(nfield, record)) { |
387 |
|
|
foundit = 1; |
388 |
|
|
break; /* found it! */ |
389 |
|
|
} |
390 |
|
|
} |
391 |
|
|
} |
392 |
|
|
} |
393 |
|
|
if (foundit) |
394 |
|
|
break; |
395 |
|
|
} |
396 |
|
|
|
397 |
|
|
if (!foundit) { |
398 |
|
|
free(record); |
399 |
|
|
return (opened ? -1 : -2); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
|
/* |
403 |
|
|
* Got the capability record, but now we have to expand all tc=name |
404 |
|
|
* references in it ... |
405 |
|
|
*/ |
406 |
|
|
tc_exp: { |
407 |
|
|
char *s; |
408 |
|
|
u_int ilen; |
409 |
|
|
int diff, iret, tclen; |
410 |
|
|
char *ibuf, *icap, *scan, *tc, *tcstart, *tcend; |
411 |
|
|
|
412 |
|
|
/* |
413 |
|
|
* Loop invariants: |
414 |
|
|
* There is room for one more character in record. |
415 |
|
|
* R_end points just past end of record. |
416 |
|
|
* Rp points just past last character in record. |
417 |
|
|
* Scan points at remainder of record that needs to be |
418 |
|
|
* scanned for tc=name constructs. |
419 |
|
|
*/ |
420 |
|
|
scan = record; |
421 |
|
|
tc_not_resolved = 0; |
422 |
|
|
for (;;) { |
423 |
|
|
if ((tc = cgetcap(scan, "tc", '=')) == NULL) |
424 |
|
|
break; |
425 |
|
|
|
426 |
|
|
/* |
427 |
|
|
* Find end of tc=name and stomp on the trailing `:' |
428 |
|
|
* (if present) so we can use it to call ourselves. |
429 |
|
|
*/ |
430 |
|
|
s = tc; |
431 |
|
|
for (;;) { |
432 |
|
|
if (*s == '\0') |
433 |
|
|
break; |
434 |
|
|
else |
435 |
|
|
if (*s++ == ':') { |
436 |
|
|
*(s - 1) = '\0'; |
437 |
|
|
break; |
438 |
|
|
} |
439 |
|
|
} |
440 |
|
|
tcstart = tc - 3; |
441 |
|
|
tclen = s - tcstart; |
442 |
|
|
tcend = s; |
443 |
|
|
|
444 |
|
|
iret = getent(&icap, &ilen, db_p, fp, tc, depth+1, |
445 |
|
|
NULL); |
446 |
|
|
if (iret != 0) { |
447 |
|
|
/* an error */ |
448 |
|
|
if (iret < -1) { |
449 |
|
|
if (myfd) |
450 |
|
|
(void)fclose(fp); |
451 |
|
|
free(record); |
452 |
|
|
return (iret); |
453 |
|
|
} |
454 |
|
|
if (iret == 1) |
455 |
|
|
tc_not_resolved = 1; |
456 |
|
|
/* couldn't resolve tc */ |
457 |
|
|
if (iret == -1) { |
458 |
|
|
*(s - 1) = ':'; |
459 |
|
|
scan = s - 1; |
460 |
|
|
tc_not_resolved = 1; |
461 |
|
|
continue; |
462 |
|
|
|
463 |
|
|
} |
464 |
|
|
} |
465 |
|
|
/* not interested in name field of tc'ed record */ |
466 |
|
|
s = ibuf = icap; |
467 |
|
|
for (;;) |
468 |
|
|
if (*s == '\0') |
469 |
|
|
break; |
470 |
|
|
else |
471 |
|
|
if (*s++ == ':') |
472 |
|
|
break; |
473 |
|
|
ilen -= s - icap; |
474 |
|
|
icap = s; |
475 |
|
|
|
476 |
|
|
/* make sure interpolated record is `:'-terminated */ |
477 |
|
|
s += ilen; |
478 |
|
|
if (*(s-1) != ':') { |
479 |
|
|
*s = ':'; /* overwrite NUL with : */ |
480 |
|
|
ilen++; |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
/* |
484 |
|
|
* Make sure there's enough room to insert the |
485 |
|
|
* new record. |
486 |
|
|
*/ |
487 |
|
|
diff = ilen - tclen; |
488 |
|
|
if (diff >= r_end - rp) { |
489 |
|
|
u_int pos, tcpos, tcposend; |
490 |
|
|
size_t newsize; |
491 |
|
|
char *nrecord; |
492 |
|
|
|
493 |
|
|
pos = rp - record; |
494 |
|
|
newsize = r_end - record + diff + BFRAG; |
495 |
|
|
tcpos = tcstart - record; |
496 |
|
|
tcposend = tcend - record; |
497 |
|
|
nrecord = realloc(record, newsize); |
498 |
|
|
if (nrecord == NULL) { |
499 |
|
|
free(record); |
500 |
|
|
if (myfd) |
501 |
|
|
(void)fclose(fp); |
502 |
|
|
free(ibuf); |
503 |
|
|
errno = ENOMEM; |
504 |
|
|
return (-2); |
505 |
|
|
} |
506 |
|
|
record = nrecord; |
507 |
|
|
r_end = record + newsize; |
508 |
|
|
rp = record + pos; |
509 |
|
|
tcstart = record + tcpos; |
510 |
|
|
tcend = record + tcposend; |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
/* |
514 |
|
|
* Insert tc'ed record into our record. |
515 |
|
|
*/ |
516 |
|
|
s = tcstart + ilen; |
517 |
|
|
memmove(s, tcend, rp - tcend); |
518 |
|
|
memmove(tcstart, icap, ilen); |
519 |
|
|
rp += diff; |
520 |
|
|
free(ibuf); |
521 |
|
|
|
522 |
|
|
/* |
523 |
|
|
* Start scan on `:' so next cgetcap works properly |
524 |
|
|
* (cgetcap always skips first field). |
525 |
|
|
*/ |
526 |
|
|
scan = s-1; |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
} |
530 |
|
|
/* |
531 |
|
|
* Close file (if we opened it), give back any extra memory, and |
532 |
|
|
* return capability, length and success. |
533 |
|
|
*/ |
534 |
|
|
if (myfd) |
535 |
|
|
(void)fclose(fp); |
536 |
|
|
*len = rp - record - 1; /* don't count NUL */ |
537 |
|
|
if (r_end > rp) { |
538 |
|
|
char *nrecord; |
539 |
|
|
|
540 |
|
|
if ((nrecord = realloc(record, rp - record)) == NULL) { |
541 |
|
|
free(record); |
542 |
|
|
errno = ENOMEM; |
543 |
|
|
return (-2); |
544 |
|
|
} |
545 |
|
|
record = nrecord; |
546 |
|
|
} |
547 |
|
|
*cap = record; |
548 |
|
|
if (tc_not_resolved) |
549 |
|
|
return (1); |
550 |
|
|
return (0); |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
static int |
554 |
|
|
cdbget(DB *capdbp, char **bp, const char *name) |
555 |
|
|
{ |
556 |
|
|
DBT key, data; |
557 |
|
|
|
558 |
|
|
key.data = (void *)name; |
559 |
|
|
key.size = strlen(name); |
560 |
|
|
|
561 |
|
|
for (;;) { |
562 |
|
|
/* Get the reference. */ |
563 |
|
|
switch(capdbp->get(capdbp, &key, &data, 0)) { |
564 |
|
|
case -1: |
565 |
|
|
return (-2); |
566 |
|
|
case 1: |
567 |
|
|
return (-1); |
568 |
|
|
} |
569 |
|
|
|
570 |
|
|
/* If not an index to another record, leave. */ |
571 |
|
|
if (((char *)data.data)[0] != SHADOW) |
572 |
|
|
break; |
573 |
|
|
|
574 |
|
|
key.data = (char *)data.data + 1; |
575 |
|
|
key.size = data.size - 1; |
576 |
|
|
} |
577 |
|
|
|
578 |
|
|
*bp = (char *)data.data + 1; |
579 |
|
|
return (((char *)(data.data))[0] == TCERR ? 1 : 0); |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
/* |
583 |
|
|
* Cgetmatch will return 0 if name is one of the names of the capability |
584 |
|
|
* record buf, -1 if not. |
585 |
|
|
*/ |
586 |
|
|
int |
587 |
|
|
cgetmatch(char *buf, const char *name) |
588 |
|
|
{ |
589 |
|
|
char *bp; |
590 |
|
|
const char *np; |
591 |
|
|
|
592 |
|
|
if (*name == '\0') |
593 |
|
|
return (-1); |
594 |
|
|
/* |
595 |
|
|
* Start search at beginning of record. |
596 |
|
|
*/ |
597 |
|
|
bp = buf; |
598 |
|
|
for (;;) { |
599 |
|
|
/* |
600 |
|
|
* Try to match a record name. |
601 |
|
|
*/ |
602 |
|
|
np = name; |
603 |
|
|
for (;;) |
604 |
|
|
if (*np == '\0') { |
605 |
|
|
if (*bp == '|' || *bp == ':' || *bp == '\0') |
606 |
|
|
return (0); |
607 |
|
|
else |
608 |
|
|
break; |
609 |
|
|
} else |
610 |
|
|
if (*bp++ != *np++) |
611 |
|
|
break; |
612 |
|
|
|
613 |
|
|
/* |
614 |
|
|
* Match failed, skip to next name in record. |
615 |
|
|
*/ |
616 |
|
|
bp--; /* a '|' or ':' may have stopped the match */ |
617 |
|
|
for (;;) |
618 |
|
|
if (*bp == '\0' || *bp == ':') |
619 |
|
|
return (-1); /* match failed totally */ |
620 |
|
|
else |
621 |
|
|
if (*bp++ == '|') |
622 |
|
|
break; /* found next name */ |
623 |
|
|
} |
624 |
|
|
} |
625 |
|
|
DEF_WEAK(cgetmatch); |
626 |
|
|
|
627 |
|
|
int |
628 |
|
|
cgetfirst(char **buf, char **db_array) |
629 |
|
|
{ |
630 |
|
|
|
631 |
|
|
(void)cgetclose(); |
632 |
|
|
return (cgetnext(buf, db_array)); |
633 |
|
|
} |
634 |
|
|
DEF_WEAK(cgetfirst); |
635 |
|
|
|
636 |
|
|
static FILE *pfp; |
637 |
|
|
static int slash; |
638 |
|
|
static char **dbp; |
639 |
|
|
|
640 |
|
|
int |
641 |
|
|
cgetclose(void) |
642 |
|
|
{ |
643 |
|
|
|
644 |
|
|
if (pfp != NULL) { |
645 |
|
|
(void)fclose(pfp); |
646 |
|
|
pfp = NULL; |
647 |
|
|
} |
648 |
|
|
dbp = NULL; |
649 |
|
|
gottoprec = 0; |
650 |
|
|
slash = 0; |
651 |
|
|
return(0); |
652 |
|
|
} |
653 |
|
|
DEF_WEAK(cgetclose); |
654 |
|
|
|
655 |
|
|
/* |
656 |
|
|
* Cgetnext() gets either the first or next entry in the logical database |
657 |
|
|
* specified by db_array. It returns 0 upon completion of the database, 1 |
658 |
|
|
* upon returning an entry with more remaining, and -1 if an error occurs. |
659 |
|
|
*/ |
660 |
|
|
int |
661 |
|
|
cgetnext(char **cap, char **db_array) |
662 |
|
|
{ |
663 |
|
|
size_t len, otopreclen = topreclen; |
664 |
|
|
int c, serrno, status = -1; |
665 |
|
|
char buf[BUFSIZ], nbuf[BSIZE]; |
666 |
|
|
char *b_end, *bp, *r_end, *rp; |
667 |
|
|
char *record = NULL; |
668 |
|
|
char *otoprec = toprec; |
669 |
|
|
u_int dummy; |
670 |
|
|
off_t pos; |
671 |
|
|
|
672 |
|
|
if (dbp == NULL) |
673 |
|
|
dbp = db_array; |
674 |
|
|
|
675 |
|
|
if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) |
676 |
|
|
goto done; |
677 |
|
|
|
678 |
|
|
/* |
679 |
|
|
* Check if we have an unused top record from cgetset(). |
680 |
|
|
*/ |
681 |
|
|
if (toprec && !gottoprec) { |
682 |
|
|
gottoprec = 1; |
683 |
|
|
record = toprec; |
684 |
|
|
goto lookup; |
685 |
|
|
} |
686 |
|
|
|
687 |
|
|
/* |
688 |
|
|
* Allocate first chunk of memory. |
689 |
|
|
*/ |
690 |
|
|
if ((record = malloc(BFRAG)) == NULL) |
691 |
|
|
goto done; |
692 |
|
|
r_end = record + BFRAG; |
693 |
|
|
|
694 |
|
|
/* |
695 |
|
|
* Find the next capability record |
696 |
|
|
*/ |
697 |
|
|
/* |
698 |
|
|
* Loop invariants: |
699 |
|
|
* There is always room for one more character in record. |
700 |
|
|
* R_end always points just past end of record. |
701 |
|
|
* Rp always points just past last character in record. |
702 |
|
|
* B_end always points just past last character in buf. |
703 |
|
|
* Bp always points at next character in buf. |
704 |
|
|
*/ |
705 |
|
|
b_end = buf; |
706 |
|
|
bp = buf; |
707 |
|
|
for (;;) { |
708 |
|
|
/* |
709 |
|
|
* Read in a line implementing (\, newline) |
710 |
|
|
* line continuation. |
711 |
|
|
*/ |
712 |
|
|
rp = record; |
713 |
|
|
for (;;) { |
714 |
|
|
if (bp >= b_end) { |
715 |
|
|
size_t n; |
716 |
|
|
|
717 |
|
|
n = fread(buf, 1, sizeof(buf), pfp); |
718 |
|
|
if (n == 0) { |
719 |
|
|
if (ferror(pfp)) |
720 |
|
|
goto done; |
721 |
|
|
(void)fclose(pfp); |
722 |
|
|
pfp = NULL; |
723 |
|
|
if (*++dbp == NULL) { |
724 |
|
|
status = 0; |
725 |
|
|
goto done; |
726 |
|
|
} else if ((pfp = |
727 |
|
|
fopen(*dbp, "re")) == NULL) { |
728 |
|
|
goto done; |
729 |
|
|
} else |
730 |
|
|
continue; |
731 |
|
|
} |
732 |
|
|
b_end = buf + n; |
733 |
|
|
bp = buf; |
734 |
|
|
} |
735 |
|
|
|
736 |
|
|
c = *bp++; |
737 |
|
|
if (c == '\n') { |
738 |
|
|
if (rp > record && *(rp-1) == '\\') { |
739 |
|
|
rp--; |
740 |
|
|
continue; |
741 |
|
|
} else |
742 |
|
|
break; |
743 |
|
|
} |
744 |
|
|
*rp++ = c; |
745 |
|
|
|
746 |
|
|
/* |
747 |
|
|
* Enforce loop invariant: if no room |
748 |
|
|
* left in record buffer, try to get |
749 |
|
|
* some more. |
750 |
|
|
*/ |
751 |
|
|
if (rp >= r_end) { |
752 |
|
|
size_t newsize, off; |
753 |
|
|
char *nrecord; |
754 |
|
|
|
755 |
|
|
off = rp - record; |
756 |
|
|
newsize = r_end - record + BFRAG; |
757 |
|
|
nrecord = realloc(record, newsize); |
758 |
|
|
if (nrecord == NULL) |
759 |
|
|
goto done; |
760 |
|
|
record = nrecord; |
761 |
|
|
r_end = record + newsize; |
762 |
|
|
rp = record + off; |
763 |
|
|
} |
764 |
|
|
} |
765 |
|
|
/* loop invariant lets us do this */ |
766 |
|
|
*rp++ = '\0'; |
767 |
|
|
|
768 |
|
|
/* |
769 |
|
|
* If not blank or comment, set toprec and topreclen so |
770 |
|
|
* getent() doesn't have to re-parse the file to find it. |
771 |
|
|
*/ |
772 |
|
|
if (*record != '\0' && *record != '#') { |
773 |
|
|
/* Rewind to end of record */ |
774 |
|
|
fseeko(pfp, bp - b_end, SEEK_CUR); |
775 |
|
|
toprec = record; |
776 |
|
|
topreclen = rp - record; |
777 |
|
|
break; |
778 |
|
|
} |
779 |
|
|
} |
780 |
|
|
lookup: |
781 |
|
|
/* extract name from record */ |
782 |
|
|
len = strcspn(record, "|:"); |
783 |
|
|
memcpy(nbuf, record, len); |
784 |
|
|
nbuf[len] = '\0'; |
785 |
|
|
|
786 |
|
|
/* return value of getent() is one less than cgetnext() */ |
787 |
|
|
pos = ftello(pfp); |
788 |
|
|
status = getent(cap, &dummy, dbp, pfp, nbuf, 0, NULL) + 1; |
789 |
|
|
if (status > 0) |
790 |
|
|
fseeko(pfp, pos, SEEK_SET); |
791 |
|
|
done: |
792 |
|
|
serrno = errno; |
793 |
|
|
if (toprec != otoprec) { |
794 |
|
|
toprec = otoprec; |
795 |
|
|
topreclen = otopreclen; |
796 |
|
|
free(record); |
797 |
|
|
} |
798 |
|
|
if (status <= 0) |
799 |
|
|
(void)cgetclose(); |
800 |
|
|
errno = serrno; |
801 |
|
|
|
802 |
|
|
return (status); |
803 |
|
|
} |
804 |
|
|
DEF_WEAK(cgetnext); |
805 |
|
|
|
806 |
|
|
/* |
807 |
|
|
* Cgetstr retrieves the value of the string capability cap from the |
808 |
|
|
* capability record pointed to by buf. A pointer to a decoded, NUL |
809 |
|
|
* terminated, malloc'd copy of the string is returned in the char * |
810 |
|
|
* pointed to by str. The length of the string not including the trailing |
811 |
|
|
* NUL is returned on success, -1 if the requested string capability |
812 |
|
|
* couldn't be found, -2 if a system error was encountered (storage |
813 |
|
|
* allocation failure). |
814 |
|
|
*/ |
815 |
|
|
int |
816 |
|
|
cgetstr(char *buf, const char *cap, char **str) |
817 |
|
|
{ |
818 |
|
|
u_int m_room; |
819 |
|
|
char *bp, *mp; |
820 |
|
|
int len; |
821 |
|
|
char *mem; |
822 |
|
|
|
823 |
|
|
/* |
824 |
|
|
* Find string capability cap |
825 |
|
|
*/ |
826 |
|
|
bp = cgetcap(buf, cap, '='); |
827 |
|
|
if (bp == NULL) |
828 |
|
|
return (-1); |
829 |
|
|
|
830 |
|
|
/* |
831 |
|
|
* Conversion / storage allocation loop ... Allocate memory in |
832 |
|
|
* chunks SFRAG in size. |
833 |
|
|
*/ |
834 |
|
|
if ((mem = malloc(SFRAG)) == NULL) |
835 |
|
|
return (-2); /* couldn't even allocate the first fragment */ |
836 |
|
|
m_room = SFRAG; |
837 |
|
|
mp = mem; |
838 |
|
|
|
839 |
|
|
while (*bp != ':' && *bp != '\0') { |
840 |
|
|
/* |
841 |
|
|
* Loop invariants: |
842 |
|
|
* There is always room for one more character in mem. |
843 |
|
|
* Mp always points just past last character in mem. |
844 |
|
|
* Bp always points at next character in buf. |
845 |
|
|
*/ |
846 |
|
|
if (*bp == '^') { |
847 |
|
|
bp++; |
848 |
|
|
if (*bp == ':' || *bp == '\0') |
849 |
|
|
break; /* drop unfinished escape */ |
850 |
|
|
*mp++ = *bp++ & 037; |
851 |
|
|
} else if (*bp == '\\') { |
852 |
|
|
bp++; |
853 |
|
|
if (*bp == ':' || *bp == '\0') |
854 |
|
|
break; /* drop unfinished escape */ |
855 |
|
|
if ('0' <= *bp && *bp <= '7') { |
856 |
|
|
int n, i; |
857 |
|
|
|
858 |
|
|
n = 0; |
859 |
|
|
i = 3; /* maximum of three octal digits */ |
860 |
|
|
do { |
861 |
|
|
n = n * 8 + (*bp++ - '0'); |
862 |
|
|
} while (--i && '0' <= *bp && *bp <= '7'); |
863 |
|
|
*mp++ = n; |
864 |
|
|
} |
865 |
|
|
else switch (*bp++) { |
866 |
|
|
case 'b': case 'B': |
867 |
|
|
*mp++ = '\b'; |
868 |
|
|
break; |
869 |
|
|
case 't': case 'T': |
870 |
|
|
*mp++ = '\t'; |
871 |
|
|
break; |
872 |
|
|
case 'n': case 'N': |
873 |
|
|
*mp++ = '\n'; |
874 |
|
|
break; |
875 |
|
|
case 'f': case 'F': |
876 |
|
|
*mp++ = '\f'; |
877 |
|
|
break; |
878 |
|
|
case 'r': case 'R': |
879 |
|
|
*mp++ = '\r'; |
880 |
|
|
break; |
881 |
|
|
case 'e': case 'E': |
882 |
|
|
*mp++ = ESC; |
883 |
|
|
break; |
884 |
|
|
case 'c': case 'C': |
885 |
|
|
*mp++ = ':'; |
886 |
|
|
break; |
887 |
|
|
default: |
888 |
|
|
/* |
889 |
|
|
* Catches '\', '^', and |
890 |
|
|
* everything else. |
891 |
|
|
*/ |
892 |
|
|
*mp++ = *(bp-1); |
893 |
|
|
break; |
894 |
|
|
} |
895 |
|
|
} else |
896 |
|
|
*mp++ = *bp++; |
897 |
|
|
m_room--; |
898 |
|
|
|
899 |
|
|
/* |
900 |
|
|
* Enforce loop invariant: if no room left in current |
901 |
|
|
* buffer, try to get some more. |
902 |
|
|
*/ |
903 |
|
|
if (m_room == 0) { |
904 |
|
|
size_t size = mp - mem; |
905 |
|
|
char *nmem; |
906 |
|
|
|
907 |
|
|
if ((nmem = realloc(mem, size + SFRAG)) == NULL) { |
908 |
|
|
free(mem); |
909 |
|
|
return (-2); |
910 |
|
|
} |
911 |
|
|
mem = nmem; |
912 |
|
|
m_room = SFRAG; |
913 |
|
|
mp = mem + size; |
914 |
|
|
} |
915 |
|
|
} |
916 |
|
|
*mp++ = '\0'; /* loop invariant let's us do this */ |
917 |
|
|
m_room--; |
918 |
|
|
len = mp - mem - 1; |
919 |
|
|
|
920 |
|
|
/* |
921 |
|
|
* Give back any extra memory and return value and success. |
922 |
|
|
*/ |
923 |
|
|
if (m_room != 0) { |
924 |
|
|
char *nmem; |
925 |
|
|
|
926 |
|
|
if ((nmem = realloc(mem, mp - mem)) == NULL) { |
927 |
|
|
free(mem); |
928 |
|
|
return (-2); |
929 |
|
|
} |
930 |
|
|
mem = nmem; |
931 |
|
|
} |
932 |
|
|
*str = mem; |
933 |
|
|
return (len); |
934 |
|
|
} |
935 |
|
|
DEF_WEAK(cgetstr); |
936 |
|
|
|
937 |
|
|
/* |
938 |
|
|
* Cgetustr retrieves the value of the string capability cap from the |
939 |
|
|
* capability record pointed to by buf. The difference between cgetustr() |
940 |
|
|
* and cgetstr() is that cgetustr does not decode escapes but rather treats |
941 |
|
|
* all characters literally. A pointer to a NUL terminated malloc'd |
942 |
|
|
* copy of the string is returned in the char pointed to by str. The |
943 |
|
|
* length of the string not including the trailing NUL is returned on success, |
944 |
|
|
* -1 if the requested string capability couldn't be found, -2 if a system |
945 |
|
|
* error was encountered (storage allocation failure). |
946 |
|
|
*/ |
947 |
|
|
int |
948 |
|
|
cgetustr(char *buf, const char *cap, char **str) |
949 |
|
|
{ |
950 |
|
|
u_int m_room; |
951 |
|
|
char *bp, *mp; |
952 |
|
|
int len; |
953 |
|
|
char *mem; |
954 |
|
|
|
955 |
|
|
/* |
956 |
|
|
* Find string capability cap |
957 |
|
|
*/ |
958 |
|
|
if ((bp = cgetcap(buf, cap, '=')) == NULL) |
959 |
|
|
return (-1); |
960 |
|
|
|
961 |
|
|
/* |
962 |
|
|
* Conversion / storage allocation loop ... Allocate memory in |
963 |
|
|
* chunks SFRAG in size. |
964 |
|
|
*/ |
965 |
|
|
if ((mem = malloc(SFRAG)) == NULL) |
966 |
|
|
return (-2); /* couldn't even allocate the first fragment */ |
967 |
|
|
m_room = SFRAG; |
968 |
|
|
mp = mem; |
969 |
|
|
|
970 |
|
|
while (*bp != ':' && *bp != '\0') { |
971 |
|
|
/* |
972 |
|
|
* Loop invariants: |
973 |
|
|
* There is always room for one more character in mem. |
974 |
|
|
* Mp always points just past last character in mem. |
975 |
|
|
* Bp always points at next character in buf. |
976 |
|
|
*/ |
977 |
|
|
*mp++ = *bp++; |
978 |
|
|
m_room--; |
979 |
|
|
|
980 |
|
|
/* |
981 |
|
|
* Enforce loop invariant: if no room left in current |
982 |
|
|
* buffer, try to get some more. |
983 |
|
|
*/ |
984 |
|
|
if (m_room == 0) { |
985 |
|
|
size_t size = mp - mem; |
986 |
|
|
char *nmem; |
987 |
|
|
|
988 |
|
|
if ((nmem = realloc(mem, size + SFRAG)) == NULL) { |
989 |
|
|
free(mem); |
990 |
|
|
return (-2); |
991 |
|
|
} |
992 |
|
|
mem = nmem; |
993 |
|
|
m_room = SFRAG; |
994 |
|
|
mp = mem + size; |
995 |
|
|
} |
996 |
|
|
} |
997 |
|
|
*mp++ = '\0'; /* loop invariant let's us do this */ |
998 |
|
|
m_room--; |
999 |
|
|
len = mp - mem - 1; |
1000 |
|
|
|
1001 |
|
|
/* |
1002 |
|
|
* Give back any extra memory and return value and success. |
1003 |
|
|
*/ |
1004 |
|
|
if (m_room != 0) { |
1005 |
|
|
char *nmem; |
1006 |
|
|
|
1007 |
|
|
if ((nmem = realloc(mem, mp - mem)) == NULL) { |
1008 |
|
|
free(mem); |
1009 |
|
|
return (-2); |
1010 |
|
|
} |
1011 |
|
|
mem = nmem; |
1012 |
|
|
} |
1013 |
|
|
*str = mem; |
1014 |
|
|
return (len); |
1015 |
|
|
} |
1016 |
|
|
DEF_WEAK(cgetustr); |
1017 |
|
|
|
1018 |
|
|
/* |
1019 |
|
|
* Cgetnum retrieves the value of the numeric capability cap from the |
1020 |
|
|
* capability record pointed to by buf. The numeric value is returned in |
1021 |
|
|
* the long pointed to by num. 0 is returned on success, -1 if the requested |
1022 |
|
|
* numeric capability couldn't be found. |
1023 |
|
|
*/ |
1024 |
|
|
int |
1025 |
|
|
cgetnum(char *buf, const char *cap, long *num) |
1026 |
|
|
{ |
1027 |
|
|
long n; |
1028 |
|
|
int base, digit; |
1029 |
|
|
char *bp; |
1030 |
|
|
|
1031 |
|
|
/* |
1032 |
|
|
* Find numeric capability cap |
1033 |
|
|
*/ |
1034 |
|
|
bp = cgetcap(buf, cap, '#'); |
1035 |
|
|
if (bp == NULL) |
1036 |
|
|
return (-1); |
1037 |
|
|
|
1038 |
|
|
/* |
1039 |
|
|
* Look at value and determine numeric base: |
1040 |
|
|
* 0x... or 0X... hexadecimal, |
1041 |
|
|
* else 0... octal, |
1042 |
|
|
* else decimal. |
1043 |
|
|
*/ |
1044 |
|
|
if (*bp == '0') { |
1045 |
|
|
bp++; |
1046 |
|
|
if (*bp == 'x' || *bp == 'X') { |
1047 |
|
|
bp++; |
1048 |
|
|
base = 16; |
1049 |
|
|
} else |
1050 |
|
|
base = 8; |
1051 |
|
|
} else |
1052 |
|
|
base = 10; |
1053 |
|
|
|
1054 |
|
|
/* |
1055 |
|
|
* Conversion loop ... |
1056 |
|
|
*/ |
1057 |
|
|
n = 0; |
1058 |
|
|
for (;;) { |
1059 |
|
|
if ('0' <= *bp && *bp <= '9') |
1060 |
|
|
digit = *bp - '0'; |
1061 |
|
|
else if ('a' <= *bp && *bp <= 'f') |
1062 |
|
|
digit = 10 + *bp - 'a'; |
1063 |
|
|
else if ('A' <= *bp && *bp <= 'F') |
1064 |
|
|
digit = 10 + *bp - 'A'; |
1065 |
|
|
else |
1066 |
|
|
break; |
1067 |
|
|
|
1068 |
|
|
if (digit >= base) |
1069 |
|
|
break; |
1070 |
|
|
|
1071 |
|
|
n = n * base + digit; |
1072 |
|
|
bp++; |
1073 |
|
|
} |
1074 |
|
|
|
1075 |
|
|
/* |
1076 |
|
|
* Return value and success. |
1077 |
|
|
*/ |
1078 |
|
|
*num = n; |
1079 |
|
|
return (0); |
1080 |
|
|
} |
1081 |
|
|
DEF_WEAK(cgetnum); |
1082 |
|
|
|
1083 |
|
|
/* |
1084 |
|
|
* Compare name field of record. |
1085 |
|
|
*/ |
1086 |
|
|
static int |
1087 |
|
|
nfcmp(const char *nf, char *rec) |
1088 |
|
|
{ |
1089 |
|
|
char *cp, tmp; |
1090 |
|
|
int ret; |
1091 |
|
|
|
1092 |
|
|
for (cp = rec; *cp != ':'; cp++) |
1093 |
|
|
; |
1094 |
|
|
|
1095 |
|
|
tmp = *(cp + 1); |
1096 |
|
|
*(cp + 1) = '\0'; |
1097 |
|
|
ret = strcmp(nf, rec); |
1098 |
|
|
*(cp + 1) = tmp; |
1099 |
|
|
|
1100 |
|
|
return (ret); |
1101 |
|
|
} |