1 |
|
|
/* $OpenBSD: common.c,v 1.41 2016/02/29 17:26:01 jca Exp $ */ |
2 |
|
|
/* $NetBSD: common.c,v 1.21 2000/08/09 14:28:50 itojun Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1983, 1993 |
6 |
|
|
* The Regents of the University of California. All rights reserved. |
7 |
|
|
* (c) UNIX System Laboratories, Inc. |
8 |
|
|
* All or some portions of this file are derived from material licensed |
9 |
|
|
* to the University of California by American Telephone and Telegraph |
10 |
|
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with |
11 |
|
|
* the permission of UNIX System Laboratories, Inc. |
12 |
|
|
* |
13 |
|
|
* Redistribution and use in source and binary forms, with or without |
14 |
|
|
* modification, are permitted provided that the following conditions |
15 |
|
|
* are met: |
16 |
|
|
* 1. Redistributions of source code must retain the above copyright |
17 |
|
|
* notice, this list of conditions and the following disclaimer. |
18 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
19 |
|
|
* notice, this list of conditions and the following disclaimer in the |
20 |
|
|
* documentation and/or other materials provided with the distribution. |
21 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
22 |
|
|
* may be used to endorse or promote products derived from this software |
23 |
|
|
* without specific prior written permission. |
24 |
|
|
* |
25 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
26 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
27 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
28 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
29 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
30 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
31 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
32 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
33 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
34 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
35 |
|
|
* SUCH DAMAGE. |
36 |
|
|
*/ |
37 |
|
|
|
38 |
|
|
#include <sys/stat.h> |
39 |
|
|
#include <sys/time.h> |
40 |
|
|
|
41 |
|
|
#include <sys/socket.h> |
42 |
|
|
#include <netinet/in.h> |
43 |
|
|
#include <arpa/inet.h> |
44 |
|
|
#include <netdb.h> |
45 |
|
|
|
46 |
|
|
#include <dirent.h> |
47 |
|
|
#include <errno.h> |
48 |
|
|
#include <fcntl.h> |
49 |
|
|
#include <unistd.h> |
50 |
|
|
#include <limits.h> |
51 |
|
|
#include <stdlib.h> |
52 |
|
|
#include <stdio.h> |
53 |
|
|
#include <string.h> |
54 |
|
|
#include <signal.h> |
55 |
|
|
#include <stdarg.h> |
56 |
|
|
#include <ifaddrs.h> |
57 |
|
|
#include "lp.h" |
58 |
|
|
#include "pathnames.h" |
59 |
|
|
|
60 |
|
|
/* |
61 |
|
|
* Routines and data common to all the line printer functions. |
62 |
|
|
*/ |
63 |
|
|
|
64 |
|
|
char *AF; /* accounting file */ |
65 |
|
|
long BR; /* baud rate if lp is a tty */ |
66 |
|
|
char *CF; /* name of cifplot filter (per job) */ |
67 |
|
|
char *DF; /* name of tex filter (per job) */ |
68 |
|
|
long DU; /* daemon user-id */ |
69 |
|
|
char *FF; /* form feed string */ |
70 |
|
|
char *GF; /* name of graph(1G) filter (per job) */ |
71 |
|
|
long HL; /* print header last */ |
72 |
|
|
char *IF; /* name of input filter (created per job) */ |
73 |
|
|
char *LF; /* log file for error messages */ |
74 |
|
|
char *LO; /* lock file name */ |
75 |
|
|
char *LP; /* line printer device name */ |
76 |
|
|
long MC; /* maximum number of copies allowed */ |
77 |
|
|
char *MS; /* stty flags to set if lp is a tty */ |
78 |
|
|
long MX; /* maximum number of blocks to copy */ |
79 |
|
|
char *NF; /* name of ditroff filter (per job) */ |
80 |
|
|
char *OF; /* name of output filter (created once) */ |
81 |
|
|
long PL; /* page length */ |
82 |
|
|
long PW; /* page width */ |
83 |
|
|
long PX; /* page width in pixels */ |
84 |
|
|
long PY; /* page length in pixels */ |
85 |
|
|
char *RF; /* name of fortran text filter (per job) */ |
86 |
|
|
char *RG; /* restricted group */ |
87 |
|
|
char *RM; /* remote machine name */ |
88 |
|
|
char *RP; /* remote printer name */ |
89 |
|
|
long RS; /* restricted to those with local accounts */ |
90 |
|
|
long RW; /* open LP for reading and writing */ |
91 |
|
|
long SB; /* short banner instead of normal header */ |
92 |
|
|
long SC; /* suppress multiple copies */ |
93 |
|
|
char *SD; /* spool directory */ |
94 |
|
|
long SF; /* suppress FF on each print job */ |
95 |
|
|
long SH; /* suppress header page */ |
96 |
|
|
char *ST; /* status file name */ |
97 |
|
|
char *TF; /* name of troff filter (per job) */ |
98 |
|
|
char *TR; /* trailer string to be output when Q empties */ |
99 |
|
|
char *VF; /* name of vplot filter (per job) */ |
100 |
|
|
|
101 |
|
|
char line[BUFSIZ]; |
102 |
|
|
int remote; /* true if sending files to a remote host */ |
103 |
|
|
|
104 |
|
|
static int compar(const void *, const void *); |
105 |
|
|
|
106 |
|
|
/* |
107 |
|
|
* Create a TCP connection to host "rhost" at port "rport". |
108 |
|
|
* If rport == 0, then use the printer service port. |
109 |
|
|
* Most of this code comes from rcmd.c. |
110 |
|
|
*/ |
111 |
|
|
int |
112 |
|
|
getport(char *rhost, int rport) |
113 |
|
|
{ |
114 |
|
|
struct addrinfo hints, *res, *r; |
115 |
|
|
u_int timo = 1; |
116 |
|
|
int s, lport = IPPORT_RESERVED - 1; |
117 |
|
|
int error; |
118 |
|
|
int refuse, trial; |
119 |
|
|
char pbuf[NI_MAXSERV]; |
120 |
|
|
|
121 |
|
|
/* |
122 |
|
|
* Get the host address and port number to connect to. |
123 |
|
|
*/ |
124 |
|
|
if (rhost == NULL) |
125 |
|
|
fatal("no remote host to connect to"); |
126 |
|
|
memset(&hints, 0, sizeof(hints)); |
127 |
|
|
hints.ai_family = PF_UNSPEC; |
128 |
|
|
hints.ai_socktype = SOCK_STREAM; |
129 |
|
|
if (rport) |
130 |
|
|
snprintf(pbuf, sizeof(pbuf), "%d", rport); |
131 |
|
|
else |
132 |
|
|
snprintf(pbuf, sizeof(pbuf), "printer"); |
133 |
|
|
siginterrupt(SIGINT, 1); |
134 |
|
|
error = getaddrinfo(rhost, pbuf, &hints, &res); |
135 |
|
|
siginterrupt(SIGINT, 0); |
136 |
|
|
if (error) |
137 |
|
|
fatal("printer/tcp: %s", gai_strerror(error)); |
138 |
|
|
|
139 |
|
|
/* |
140 |
|
|
* Try connecting to the server. |
141 |
|
|
*/ |
142 |
|
|
retry: |
143 |
|
|
s = -1; |
144 |
|
|
refuse = trial = 0; |
145 |
|
|
for (r = res; r; r = r->ai_next) { |
146 |
|
|
trial++; |
147 |
|
|
retryport: |
148 |
|
|
PRIV_START; |
149 |
|
|
s = rresvport_af(&lport, r->ai_family); |
150 |
|
|
PRIV_END; |
151 |
|
|
if (s < 0) { |
152 |
|
|
/* fall back to non-privileged port */ |
153 |
|
|
if (errno != EACCES || |
154 |
|
|
(s = socket(r->ai_family, SOCK_STREAM, 0)) < 0) { |
155 |
|
|
freeaddrinfo(res); |
156 |
|
|
return(-1); |
157 |
|
|
} |
158 |
|
|
} |
159 |
|
|
siginterrupt(SIGINT, 1); |
160 |
|
|
if (connect(s, r->ai_addr, r->ai_addrlen) < 0) { |
161 |
|
|
error = errno; |
162 |
|
|
siginterrupt(SIGINT, 0); |
163 |
|
|
(void)close(s); |
164 |
|
|
s = -1; |
165 |
|
|
errno = error; |
166 |
|
|
if (errno == EADDRINUSE) { |
167 |
|
|
lport--; |
168 |
|
|
goto retryport; |
169 |
|
|
} else if (errno == ECONNREFUSED) |
170 |
|
|
refuse++; |
171 |
|
|
continue; |
172 |
|
|
} else { |
173 |
|
|
siginterrupt(SIGINT, 0); |
174 |
|
|
break; |
175 |
|
|
} |
176 |
|
|
} |
177 |
|
|
if (s < 0 && trial == refuse && timo <= 16) { |
178 |
|
|
sleep(timo); |
179 |
|
|
timo *= 2; |
180 |
|
|
goto retry; |
181 |
|
|
} |
182 |
|
|
if (res) |
183 |
|
|
freeaddrinfo(res); |
184 |
|
|
|
185 |
|
|
/* Don't worry if we get an error from setsockopt(). */ |
186 |
|
|
trial = 1; |
187 |
|
|
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &trial, sizeof(trial)); |
188 |
|
|
|
189 |
|
|
return(s); |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
/* |
193 |
|
|
* Getline reads a line from the control file cfp, removes tabs, converts |
194 |
|
|
* new-line to null and leaves it in line. |
195 |
|
|
* Returns 0 at EOF or the number of characters read. |
196 |
|
|
*/ |
197 |
|
|
int |
198 |
|
|
get_line(FILE *cfp) |
199 |
|
|
{ |
200 |
|
|
int linel = 0; |
201 |
|
|
char *lp = line; |
202 |
|
|
int c; |
203 |
|
|
|
204 |
|
|
while ((c = getc(cfp)) != '\n' && linel+1<sizeof(line)) { |
205 |
|
|
if (c == EOF) |
206 |
|
|
return(0); |
207 |
|
|
if (c == '\t') { |
208 |
|
|
do { |
209 |
|
|
*lp++ = ' '; |
210 |
|
|
linel++; |
211 |
|
|
} while ((linel & 07) != 0 && linel+1 < sizeof(line)); |
212 |
|
|
continue; |
213 |
|
|
} |
214 |
|
|
*lp++ = c; |
215 |
|
|
linel++; |
216 |
|
|
} |
217 |
|
|
*lp++ = '\0'; |
218 |
|
|
return(linel); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
/* |
222 |
|
|
* Scan the current directory and make a list of daemon files sorted by |
223 |
|
|
* creation time. |
224 |
|
|
* Return the number of entries and a pointer to the list. |
225 |
|
|
*/ |
226 |
|
|
int |
227 |
|
|
getq(struct queue ***namelist) |
228 |
|
|
{ |
229 |
|
|
struct dirent *d; |
230 |
|
|
struct queue *q, **queue = NULL; |
231 |
|
|
size_t nitems = 0, arraysz; |
232 |
|
|
struct stat stbuf; |
233 |
|
|
DIR *dirp; |
234 |
|
|
|
235 |
|
|
PRIV_START; |
236 |
|
|
dirp = opendir(SD); |
237 |
|
|
PRIV_END; |
238 |
|
|
if (dirp == NULL) |
239 |
|
|
return(-1); |
240 |
|
|
if (fstat(dirfd(dirp), &stbuf) < 0) |
241 |
|
|
goto errdone; |
242 |
|
|
|
243 |
|
|
/* |
244 |
|
|
* Estimate the array size by taking the size of the directory file |
245 |
|
|
* and dividing it by a multiple of the minimum size entry. |
246 |
|
|
*/ |
247 |
|
|
arraysz = (stbuf.st_size / 24); |
248 |
|
|
queue = calloc(arraysz, sizeof(struct queue *)); |
249 |
|
|
if (queue == NULL) |
250 |
|
|
goto errdone; |
251 |
|
|
|
252 |
|
|
while ((d = readdir(dirp)) != NULL) { |
253 |
|
|
if (d->d_name[0] != 'c' || d->d_name[1] != 'f') |
254 |
|
|
continue; /* daemon control files only */ |
255 |
|
|
PRIV_START; |
256 |
|
|
if (stat(d->d_name, &stbuf) < 0) { |
257 |
|
|
PRIV_END; |
258 |
|
|
continue; /* Doesn't exist */ |
259 |
|
|
} |
260 |
|
|
PRIV_END; |
261 |
|
|
q = malloc(sizeof(struct queue)); |
262 |
|
|
if (q == NULL) |
263 |
|
|
goto errdone; |
264 |
|
|
q->q_time = stbuf.st_mtime; |
265 |
|
|
strlcpy(q->q_name, d->d_name, sizeof(q->q_name)); |
266 |
|
|
|
267 |
|
|
/* |
268 |
|
|
* Check to make sure the array has space left and |
269 |
|
|
* realloc the maximum size. |
270 |
|
|
*/ |
271 |
|
|
if (nitems == arraysz) { |
272 |
|
|
struct queue **newqueue; |
273 |
|
|
newqueue = reallocarray(queue, |
274 |
|
|
arraysz, 2 * sizeof(struct queue *)); |
275 |
|
|
if (newqueue == NULL) { |
276 |
|
|
free(q); |
277 |
|
|
goto errdone; |
278 |
|
|
} |
279 |
|
|
arraysz *= 2; |
280 |
|
|
queue = newqueue; |
281 |
|
|
} |
282 |
|
|
queue[nitems++] = q; |
283 |
|
|
} |
284 |
|
|
closedir(dirp); |
285 |
|
|
if (nitems) |
286 |
|
|
qsort(queue, nitems, sizeof(struct queue *), compar); |
287 |
|
|
*namelist = queue; |
288 |
|
|
return(nitems); |
289 |
|
|
|
290 |
|
|
errdone: |
291 |
|
|
if (queue != NULL) { |
292 |
|
|
while (nitems--) |
293 |
|
|
free(queue[nitems]); |
294 |
|
|
free(queue); |
295 |
|
|
} |
296 |
|
|
closedir(dirp); |
297 |
|
|
return(-1); |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
/* |
301 |
|
|
* Compare modification times. |
302 |
|
|
*/ |
303 |
|
|
static int |
304 |
|
|
compar(const void *v1, const void *v2) |
305 |
|
|
{ |
306 |
|
|
struct queue *p1 = *(struct queue **)v1; |
307 |
|
|
struct queue *p2 = *(struct queue **)v2; |
308 |
|
|
|
309 |
|
|
return(p1->q_time - p2->q_time); |
310 |
|
|
} |
311 |
|
|
|
312 |
|
|
/* |
313 |
|
|
* Figure out whether the local machine is the same |
314 |
|
|
* as the remote machine (RM) entry (if it exists). |
315 |
|
|
*/ |
316 |
|
|
char * |
317 |
|
|
checkremote(void) |
318 |
|
|
{ |
319 |
|
|
char lname[NI_MAXHOST], rname[NI_MAXHOST]; |
320 |
|
|
struct addrinfo hints, *res, *res0; |
321 |
|
|
static char errbuf[128]; |
322 |
|
|
int error; |
323 |
|
|
struct ifaddrs *ifap, *ifa; |
324 |
|
|
const int niflags = NI_NUMERICHOST; |
325 |
|
|
#ifdef __KAME__ |
326 |
|
|
struct sockaddr_in6 sin6; |
327 |
|
|
struct sockaddr_in6 *sin6p; |
328 |
|
|
#endif |
329 |
|
|
|
330 |
|
|
remote = 0; /* assume printer is local on failure */ |
331 |
|
|
|
332 |
|
|
if (RM == NULL || *RM == '\0') |
333 |
|
|
return NULL; |
334 |
|
|
|
335 |
|
|
/* get the local interface addresses */ |
336 |
|
|
siginterrupt(SIGINT, 1); |
337 |
|
|
if (getifaddrs(&ifap) < 0) { |
338 |
|
|
(void)snprintf(errbuf, sizeof(errbuf), |
339 |
|
|
"unable to get local interface address: %s", |
340 |
|
|
strerror(errno)); |
341 |
|
|
siginterrupt(SIGINT, 0); |
342 |
|
|
return errbuf; |
343 |
|
|
} |
344 |
|
|
siginterrupt(SIGINT, 0); |
345 |
|
|
|
346 |
|
|
/* get the remote host addresses (RM) */ |
347 |
|
|
memset(&hints, 0, sizeof(hints)); |
348 |
|
|
hints.ai_flags = AI_CANONNAME; |
349 |
|
|
hints.ai_family = PF_UNSPEC; |
350 |
|
|
hints.ai_socktype = SOCK_STREAM; |
351 |
|
|
res = NULL; |
352 |
|
|
siginterrupt(SIGINT, 1); |
353 |
|
|
error = getaddrinfo(RM, NULL, &hints, &res0); |
354 |
|
|
siginterrupt(SIGINT, 0); |
355 |
|
|
if (error) { |
356 |
|
|
(void)snprintf(errbuf, sizeof(errbuf), |
357 |
|
|
"unable to resolve remote machine %s: %s", |
358 |
|
|
RM, gai_strerror(error)); |
359 |
|
|
freeifaddrs(ifap); |
360 |
|
|
return errbuf; |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
remote = 1; /* assume printer is remote */ |
364 |
|
|
|
365 |
|
|
for (res = res0; res; res = res->ai_next) { |
366 |
|
|
siginterrupt(SIGINT, 1); |
367 |
|
|
error = getnameinfo(res->ai_addr, res->ai_addrlen, |
368 |
|
|
rname, sizeof(rname), NULL, 0, niflags); |
369 |
|
|
siginterrupt(SIGINT, 0); |
370 |
|
|
if (error != 0) |
371 |
|
|
continue; |
372 |
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
373 |
|
|
#ifdef __KAME__ |
374 |
|
|
sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; |
375 |
|
|
if (ifa->ifa_addr->sa_family == AF_INET6 && |
376 |
|
|
ifa->ifa_addr->sa_len == sizeof(sin6) && |
377 |
|
|
IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr) && |
378 |
|
|
*(u_int16_t *)&sin6p->sin6_addr.s6_addr[2]) { |
379 |
|
|
/* kame scopeid hack */ |
380 |
|
|
memcpy(&sin6, ifa->ifa_addr, sizeof(sin6)); |
381 |
|
|
sin6.sin6_scope_id = |
382 |
|
|
ntohs(*(u_int16_t *)&sin6p->sin6_addr.s6_addr[2]); |
383 |
|
|
sin6.sin6_addr.s6_addr[2] = 0; |
384 |
|
|
sin6.sin6_addr.s6_addr[3] = 0; |
385 |
|
|
siginterrupt(SIGINT, 1); |
386 |
|
|
error = getnameinfo((struct sockaddr *)&sin6, |
387 |
|
|
sin6.sin6_len, lname, sizeof(lname), |
388 |
|
|
NULL, 0, niflags); |
389 |
|
|
siginterrupt(SIGINT, 0); |
390 |
|
|
if (error != 0) |
391 |
|
|
continue; |
392 |
|
|
} else |
393 |
|
|
#endif |
394 |
|
|
siginterrupt(SIGINT, 1); |
395 |
|
|
error = getnameinfo(ifa->ifa_addr, |
396 |
|
|
ifa->ifa_addr->sa_len, lname, sizeof(lname), NULL, |
397 |
|
|
0, niflags); |
398 |
|
|
siginterrupt(SIGINT, 0); |
399 |
|
|
if (error != 0) |
400 |
|
|
continue; |
401 |
|
|
|
402 |
|
|
if (strcmp(rname, lname) == 0) { |
403 |
|
|
remote = 0; |
404 |
|
|
goto done; |
405 |
|
|
} |
406 |
|
|
} |
407 |
|
|
} |
408 |
|
|
done: |
409 |
|
|
freeaddrinfo(res0); |
410 |
|
|
freeifaddrs(ifap); |
411 |
|
|
return NULL; |
412 |
|
|
} |
413 |
|
|
|
414 |
|
|
__dead void |
415 |
|
|
fatal(const char *msg, ...) |
416 |
|
|
{ |
417 |
|
|
extern char *__progname; |
418 |
|
|
va_list ap; |
419 |
|
|
|
420 |
|
|
va_start(ap, msg); |
421 |
|
|
if (from != host) |
422 |
|
|
(void)printf("%s: ", host); |
423 |
|
|
(void)printf("%s: ", __progname); |
424 |
|
|
if (printer) |
425 |
|
|
(void)printf("%s: ", printer); |
426 |
|
|
(void)vprintf(msg, ap); |
427 |
|
|
va_end(ap); |
428 |
|
|
(void)putchar('\n'); |
429 |
|
|
exit(1); |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
int |
433 |
|
|
safe_open(const char *path, int flags, mode_t mode) |
434 |
|
|
{ |
435 |
|
|
int fd, serrno; |
436 |
|
|
struct stat stbuf; |
437 |
|
|
|
438 |
|
|
if ((fd = open(path, flags|O_NONBLOCK, mode)) < 0 || |
439 |
|
|
fstat(fd, &stbuf) < 0) { |
440 |
|
|
if (fd >= 0) { |
441 |
|
|
serrno = errno; |
442 |
|
|
close(fd); |
443 |
|
|
errno = serrno; |
444 |
|
|
} |
445 |
|
|
return (-1); |
446 |
|
|
} |
447 |
|
|
if (!S_ISREG(stbuf.st_mode)) { |
448 |
|
|
close(fd); |
449 |
|
|
errno = EACCES; |
450 |
|
|
return (-1); |
451 |
|
|
} |
452 |
|
|
if (mode) |
453 |
|
|
(void)fchmod(fd, mode); |
454 |
|
|
return (fd); |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
/* |
458 |
|
|
* Make sure there's some work to do before forking off a child - lpd |
459 |
|
|
* Check to see if anything in queue - lpq |
460 |
|
|
*/ |
461 |
|
|
int |
462 |
|
|
ckqueue(char *cap) |
463 |
|
|
{ |
464 |
|
|
struct dirent *d; |
465 |
|
|
DIR *dirp; |
466 |
|
|
char *spooldir; |
467 |
|
|
|
468 |
|
|
if (cgetstr(cap, "sd", &spooldir) >= 0) { |
469 |
|
|
dirp = opendir(spooldir); |
470 |
|
|
free(spooldir); |
471 |
|
|
} else |
472 |
|
|
dirp = opendir(_PATH_DEFSPOOL); |
473 |
|
|
|
474 |
|
|
if (dirp == NULL) |
475 |
|
|
return (-1); |
476 |
|
|
while ((d = readdir(dirp)) != NULL) { |
477 |
|
|
if (d->d_name[0] == 'c' && d->d_name[1] == 'f') { |
478 |
|
|
closedir(dirp); |
479 |
|
|
return (1); /* found a cf file */ |
480 |
|
|
} |
481 |
|
|
} |
482 |
|
|
closedir(dirp); |
483 |
|
|
return (0); |
484 |
|
|
} |