Commit | Line | Data |
---|---|---|
7172eb74 DF |
1 | /* |
2 | * Copyright (c) 1983 Regents of the University of California. | |
0718fb71 KB |
3 | * All rights reserved. |
4 | * | |
6d936b27 | 5 | * %sccs.include.redist.c% |
7172eb74 DF |
6 | */ |
7 | ||
b6b5501c | 8 | #ifndef lint |
e385b216 | 9 | static char sccsid[] = "@(#)docmd.c 5.7 (Berkeley) %G%"; |
0718fb71 | 10 | #endif /* not lint */ |
b6b5501c RC |
11 | |
12 | #include "defs.h" | |
0fccdfef | 13 | #include <setjmp.h> |
bbdda833 | 14 | #include <netdb.h> |
b6b5501c | 15 | |
0fccdfef | 16 | FILE *lfp; /* log file for recording files updated */ |
4085f36f | 17 | struct subcmd *subcmds; /* list of sub-commands for current cmd */ |
0fccdfef RC |
18 | jmp_buf env; |
19 | ||
20 | int cleanup(); | |
21 | int lostconn(); | |
22 | ||
23 | /* | |
24 | * Do the commands in cmds (initialized by yyparse). | |
25 | */ | |
78a18ca3 RC |
26 | docmds(dhosts, argc, argv) |
27 | char **dhosts; | |
0fccdfef RC |
28 | int argc; |
29 | char **argv; | |
30 | { | |
31 | register struct cmd *c; | |
86d7a5c5 RC |
32 | register struct namelist *f; |
33 | register char **cpp; | |
0fccdfef RC |
34 | extern struct cmd *cmds; |
35 | ||
36 | signal(SIGHUP, cleanup); | |
37 | signal(SIGINT, cleanup); | |
38 | signal(SIGQUIT, cleanup); | |
39 | signal(SIGTERM, cleanup); | |
40 | ||
41 | for (c = cmds; c != NULL; c = c->c_next) { | |
e4b2c01a | 42 | if (dhosts != NULL && *dhosts != NULL) { |
78a18ca3 RC |
43 | for (cpp = dhosts; *cpp; cpp++) |
44 | if (strcmp(c->c_name, *cpp) == 0) | |
45 | goto fndhost; | |
46 | continue; | |
47 | } | |
48 | fndhost: | |
86d7a5c5 RC |
49 | if (argc) { |
50 | for (cpp = argv; *cpp; cpp++) { | |
51 | if (c->c_label != NULL && | |
4085f36f | 52 | strcmp(c->c_label, *cpp) == 0) { |
86d7a5c5 RC |
53 | cpp = NULL; |
54 | goto found; | |
55 | } | |
56 | for (f = c->c_files; f != NULL; f = f->n_next) | |
57 | if (strcmp(f->n_name, *cpp) == 0) | |
58 | goto found; | |
59 | } | |
60 | continue; | |
61 | } else | |
62 | cpp = NULL; | |
63 | found: | |
0fccdfef RC |
64 | switch (c->c_type) { |
65 | case ARROW: | |
86d7a5c5 | 66 | doarrow(cpp, c->c_files, c->c_name, c->c_cmds); |
0fccdfef RC |
67 | break; |
68 | case DCOLON: | |
86d7a5c5 | 69 | dodcolon(cpp, c->c_files, c->c_name, c->c_cmds); |
0fccdfef RC |
70 | break; |
71 | default: | |
72 | fatal("illegal command type %d\n", c->c_type); | |
73 | } | |
74 | } | |
75 | closeconn(); | |
76 | } | |
b6b5501c RC |
77 | |
78 | /* | |
82572cb6 | 79 | * Process commands for sending files to other machines. |
b6b5501c | 80 | */ |
86d7a5c5 RC |
81 | doarrow(filev, files, rhost, cmds) |
82 | char **filev; | |
0fccdfef | 83 | struct namelist *files; |
9d560577 | 84 | char *rhost; |
0fccdfef | 85 | struct subcmd *cmds; |
b6b5501c | 86 | { |
0fccdfef RC |
87 | register struct namelist *f; |
88 | register struct subcmd *sc; | |
86d7a5c5 | 89 | register char **cpp; |
a3e6fd64 | 90 | int n, ddir, opts = options; |
b6b5501c RC |
91 | |
92 | if (debug) | |
9d560577 | 93 | printf("doarrow(%x, %s, %x)\n", files, rhost, cmds); |
b6b5501c | 94 | |
024fde5b RC |
95 | if (files == NULL) { |
96 | error("no files to be updated\n"); | |
97 | return; | |
98 | } | |
e8109cf8 | 99 | |
4085f36f | 100 | subcmds = cmds; |
0fccdfef | 101 | ddir = files->n_next != NULL; /* destination is a directory */ |
d0bd9bfc RC |
102 | if (nflag) |
103 | printf("updating host %s\n", rhost); | |
104 | else { | |
346dad94 | 105 | if (setjmp(env)) |
0fccdfef RC |
106 | goto done; |
107 | signal(SIGPIPE, lostconn); | |
9d560577 | 108 | if (!makeconn(rhost)) |
0fccdfef | 109 | return; |
e385b216 EA |
110 | if ((lfp = fopen(tempfile, "w")) == NULL) { |
111 | fatal("cannot open %s\n", tempfile); | |
0fccdfef | 112 | exit(1); |
b6b5501c | 113 | } |
0fccdfef RC |
114 | } |
115 | for (f = files; f != NULL; f = f->n_next) { | |
86d7a5c5 | 116 | if (filev) { |
0fccdfef | 117 | for (cpp = filev; *cpp; cpp++) |
86d7a5c5 | 118 | if (strcmp(f->n_name, *cpp) == 0) |
0fccdfef RC |
119 | goto found; |
120 | if (!nflag) | |
121 | (void) fclose(lfp); | |
122 | continue; | |
b6b5501c | 123 | } |
0fccdfef | 124 | found: |
0fccdfef RC |
125 | n = 0; |
126 | for (sc = cmds; sc != NULL; sc = sc->sc_next) { | |
127 | if (sc->sc_type != INSTALL) | |
128 | continue; | |
129 | n++; | |
130 | install(f->n_name, sc->sc_name, | |
131 | sc->sc_name == NULL ? 0 : ddir, sc->sc_options); | |
a3e6fd64 | 132 | opts = sc->sc_options; |
b6b5501c | 133 | } |
0fccdfef RC |
134 | if (n == 0) |
135 | install(f->n_name, NULL, 0, options); | |
b6b5501c | 136 | } |
0fccdfef RC |
137 | done: |
138 | if (!nflag) { | |
346dad94 | 139 | (void) signal(SIGPIPE, cleanup); |
0fccdfef RC |
140 | (void) fclose(lfp); |
141 | lfp = NULL; | |
142 | } | |
143 | for (sc = cmds; sc != NULL; sc = sc->sc_next) | |
144 | if (sc->sc_type == NOTIFY) | |
e385b216 | 145 | notify(tempfile, rhost, sc->sc_args, 0); |
a3e6fd64 | 146 | if (!nflag) { |
e385b216 | 147 | (void) unlink(tempfile); |
a3e6fd64 RC |
148 | for (; ihead != NULL; ihead = ihead->nextp) { |
149 | free(ihead); | |
150 | if ((opts & IGNLNKS) || ihead->count == 0) | |
151 | continue; | |
152 | log(lfp, "%s: Warning: missing links\n", | |
153 | ihead->pathname); | |
154 | } | |
155 | } | |
b6b5501c RC |
156 | } |
157 | ||
158 | /* | |
159 | * Create a connection to the rdist server on the machine rhost. | |
160 | */ | |
161 | makeconn(rhost) | |
162 | char *rhost; | |
163 | { | |
6e6d779a | 164 | register char *ruser, *cp; |
0fccdfef | 165 | static char *cur_host = NULL; |
bbdda833 RC |
166 | static int port = -1; |
167 | char tuser[20]; | |
6e6d779a | 168 | int n; |
b6b5501c | 169 | extern char user[]; |
80babee1 | 170 | extern int userid; |
b6b5501c | 171 | |
0fccdfef RC |
172 | if (debug) |
173 | printf("makeconn(%s)\n", rhost); | |
174 | ||
346dad94 RC |
175 | if (cur_host != NULL && rem >= 0) { |
176 | if (strcmp(cur_host, rhost) == 0) | |
177 | return(1); | |
178 | closeconn(); | |
346dad94 | 179 | } |
bbdda833 RC |
180 | cur_host = rhost; |
181 | cp = index(rhost, '@'); | |
182 | if (cp != NULL) { | |
183 | char c = *cp; | |
184 | ||
185 | *cp = '\0'; | |
186 | strncpy(tuser, rhost, sizeof(tuser)-1); | |
187 | *cp = c; | |
188 | rhost = cp + 1; | |
189 | ruser = tuser; | |
190 | if (*ruser == '\0') | |
191 | ruser = user; | |
192 | else if (!okname(ruser)) | |
b6b5501c RC |
193 | return(0); |
194 | } else | |
195 | ruser = user; | |
0fccdfef RC |
196 | if (!qflag) |
197 | printf("updating host %s\n", rhost); | |
567e65c3 | 198 | (void) sprintf(buf, "%s -Server%s", _PATH_RDIST, qflag ? " -q" : ""); |
bbdda833 RC |
199 | if (port < 0) { |
200 | struct servent *sp; | |
201 | ||
202 | if ((sp = getservbyname("shell", "tcp")) == NULL) | |
203 | fatal("shell/tcp: unknown service"); | |
204 | port = sp->s_port; | |
205 | } | |
b6b5501c RC |
206 | |
207 | if (debug) { | |
bbdda833 | 208 | printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser); |
b6b5501c RC |
209 | printf("buf = %s\n", buf); |
210 | } | |
211 | ||
82572cb6 | 212 | fflush(stdout); |
80babee1 | 213 | setreuid(userid, 0); |
bbdda833 | 214 | rem = rcmd(&rhost, port, user, ruser, buf, 0); |
80babee1 | 215 | setreuid(0, userid); |
b6b5501c RC |
216 | if (rem < 0) |
217 | return(0); | |
6e6d779a RC |
218 | cp = buf; |
219 | if (read(rem, cp, 1) != 1) | |
220 | lostconn(); | |
221 | if (*cp == 'V') { | |
222 | do { | |
223 | if (read(rem, cp, 1) != 1) | |
224 | lostconn(); | |
225 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); | |
226 | *--cp = '\0'; | |
227 | cp = buf; | |
228 | n = 0; | |
229 | while (*cp >= '0' && *cp <= '9') | |
230 | n = (n * 10) + (*cp++ - '0'); | |
0392d90c | 231 | if (*cp == '\0' && n == VERSION) |
6e6d779a | 232 | return(1); |
a3e6fd64 RC |
233 | error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n); |
234 | } else | |
235 | error("connection failed: version numbers don't match\n"); | |
0392d90c | 236 | closeconn(); |
6e6d779a | 237 | return(0); |
b6b5501c RC |
238 | } |
239 | ||
0fccdfef RC |
240 | /* |
241 | * Signal end of previous connection. | |
242 | */ | |
243 | closeconn() | |
244 | { | |
9d560577 RC |
245 | if (debug) |
246 | printf("closeconn()\n"); | |
247 | ||
0fccdfef RC |
248 | if (rem >= 0) { |
249 | (void) write(rem, "\2\n", 2); | |
250 | (void) close(rem); | |
251 | rem = -1; | |
252 | } | |
253 | } | |
254 | ||
255 | lostconn() | |
256 | { | |
346dad94 RC |
257 | if (iamremote) |
258 | cleanup(); | |
259 | log(lfp, "rdist: lost connection\n"); | |
0fccdfef RC |
260 | longjmp(env, 1); |
261 | } | |
262 | ||
e8109cf8 RC |
263 | okname(name) |
264 | register char *name; | |
b6b5501c | 265 | { |
e8109cf8 RC |
266 | register char *cp = name; |
267 | register int c; | |
b6b5501c | 268 | |
e8109cf8 RC |
269 | do { |
270 | c = *cp; | |
271 | if (c & 0200) | |
272 | goto bad; | |
273 | if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') | |
274 | goto bad; | |
275 | cp++; | |
276 | } while (*cp); | |
277 | return(1); | |
278 | bad: | |
279 | error("invalid user name %s\n", name); | |
280 | return(0); | |
82572cb6 RC |
281 | } |
282 | ||
d6bccb44 RC |
283 | time_t lastmod; |
284 | FILE *tfp; | |
285 | extern char target[], *tp; | |
3024eb6f | 286 | |
82572cb6 RC |
287 | /* |
288 | * Process commands for comparing files to time stamp files. | |
289 | */ | |
86d7a5c5 RC |
290 | dodcolon(filev, files, stamp, cmds) |
291 | char **filev; | |
0fccdfef RC |
292 | struct namelist *files; |
293 | char *stamp; | |
294 | struct subcmd *cmds; | |
82572cb6 | 295 | { |
0fccdfef RC |
296 | register struct subcmd *sc; |
297 | register struct namelist *f; | |
82572cb6 | 298 | register char **cpp; |
f7770429 RC |
299 | struct timeval tv[2]; |
300 | struct timezone tz; | |
82572cb6 | 301 | struct stat stb; |
82572cb6 RC |
302 | |
303 | if (debug) | |
0fccdfef | 304 | printf("dodcolon()\n"); |
82572cb6 | 305 | |
d6bccb44 | 306 | if (files == NULL) { |
024fde5b RC |
307 | error("no files to be updated\n"); |
308 | return; | |
309 | } | |
0fccdfef | 310 | if (stat(stamp, &stb) < 0) { |
5f91cf7b | 311 | error("%s: %s\n", stamp, strerror(errno)); |
d6bccb44 RC |
312 | return; |
313 | } | |
314 | if (debug) | |
0fccdfef | 315 | printf("%s: %d\n", stamp, stb.st_mtime); |
3db8aa13 | 316 | |
4085f36f | 317 | subcmds = cmds; |
3db8aa13 RC |
318 | lastmod = stb.st_mtime; |
319 | if (nflag || (options & VERIFY)) | |
320 | tfp = NULL; | |
321 | else { | |
e385b216 | 322 | if ((tfp = fopen(tempfile, "w")) == NULL) { |
5f91cf7b | 323 | error("%s: %s\n", stamp, strerror(errno)); |
d6bccb44 RC |
324 | return; |
325 | } | |
3db8aa13 RC |
326 | (void) gettimeofday(&tv[0], &tz); |
327 | tv[1] = tv[0]; | |
328 | (void) utimes(stamp, tv); | |
329 | } | |
f7770429 | 330 | |
0fccdfef | 331 | for (f = files; f != NULL; f = f->n_next) { |
86d7a5c5 | 332 | if (filev) { |
82572cb6 | 333 | for (cpp = filev; *cpp; cpp++) |
86d7a5c5 | 334 | if (strcmp(f->n_name, *cpp) == 0) |
82572cb6 RC |
335 | goto found; |
336 | continue; | |
337 | } | |
338 | found: | |
339 | tp = NULL; | |
0fccdfef | 340 | cmptime(f->n_name); |
82572cb6 | 341 | } |
f7770429 | 342 | |
d6bccb44 RC |
343 | if (tfp != NULL) |
344 | (void) fclose(tfp); | |
0fccdfef RC |
345 | for (sc = cmds; sc != NULL; sc = sc->sc_next) |
346 | if (sc->sc_type == NOTIFY) | |
e385b216 | 347 | notify(tempfile, NULL, sc->sc_args, lastmod); |
d6bccb44 | 348 | if (!nflag && !(options & VERIFY)) |
e385b216 | 349 | (void) unlink(tempfile); |
82572cb6 RC |
350 | } |
351 | ||
352 | /* | |
353 | * Compare the mtime of file to the list of time stamps. | |
354 | */ | |
355 | cmptime(name) | |
356 | char *name; | |
357 | { | |
82572cb6 RC |
358 | struct stat stb; |
359 | ||
360 | if (debug) | |
361 | printf("cmptime(%s)\n", name); | |
362 | ||
4085f36f | 363 | if (except(name)) |
82572cb6 RC |
364 | return; |
365 | ||
d6bccb44 RC |
366 | if (nflag) { |
367 | printf("comparing dates: %s\n", name); | |
368 | return; | |
369 | } | |
370 | ||
82572cb6 RC |
371 | /* |
372 | * first time cmptime() is called? | |
373 | */ | |
374 | if (tp == NULL) { | |
e8109cf8 RC |
375 | if (exptilde(target, name) == NULL) |
376 | return; | |
82572cb6 RC |
377 | tp = name = target; |
378 | while (*tp) | |
379 | tp++; | |
380 | } | |
381 | if (access(name, 4) < 0 || stat(name, &stb) < 0) { | |
5f91cf7b | 382 | error("%s: %s\n", name, strerror(errno)); |
82572cb6 RC |
383 | return; |
384 | } | |
385 | ||
386 | switch (stb.st_mode & S_IFMT) { | |
387 | case S_IFREG: | |
388 | break; | |
389 | ||
390 | case S_IFDIR: | |
391 | rcmptime(&stb); | |
392 | return; | |
393 | ||
394 | default: | |
395 | error("%s: not a plain file\n", name); | |
396 | return; | |
397 | } | |
398 | ||
d6bccb44 RC |
399 | if (stb.st_mtime > lastmod) |
400 | log(tfp, "new: %s\n", name); | |
82572cb6 RC |
401 | } |
402 | ||
403 | rcmptime(st) | |
404 | struct stat *st; | |
405 | { | |
406 | register DIR *d; | |
407 | register struct direct *dp; | |
408 | register char *cp; | |
409 | char *otp; | |
410 | int len; | |
411 | ||
412 | if (debug) | |
413 | printf("rcmptime(%x)\n", st); | |
414 | ||
415 | if ((d = opendir(target)) == NULL) { | |
5f91cf7b | 416 | error("%s: %s\n", target, strerror(errno)); |
82572cb6 RC |
417 | return; |
418 | } | |
419 | otp = tp; | |
420 | len = tp - target; | |
421 | while (dp = readdir(d)) { | |
422 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
423 | continue; | |
424 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
425 | error("%s/%s: Name too long\n", target, dp->d_name); | |
426 | continue; | |
427 | } | |
428 | tp = otp; | |
429 | *tp++ = '/'; | |
430 | cp = dp->d_name; | |
431 | while (*tp++ = *cp++) | |
432 | ; | |
433 | tp--; | |
434 | cmptime(target); | |
435 | } | |
436 | closedir(d); | |
437 | tp = otp; | |
438 | *tp = '\0'; | |
b6b5501c RC |
439 | } |
440 | ||
441 | /* | |
442 | * Notify the list of people the changes that were made. | |
f7770429 RC |
443 | * rhost == NULL if we are mailing a list of changes compared to at time |
444 | * stamp file. | |
b6b5501c | 445 | */ |
f7770429 | 446 | notify(file, rhost, to, lmod) |
3024eb6f | 447 | char *file, *rhost; |
0fccdfef | 448 | register struct namelist *to; |
f7770429 | 449 | time_t lmod; |
b6b5501c RC |
450 | { |
451 | register int fd, len; | |
452 | FILE *pf, *popen(); | |
453 | struct stat stb; | |
454 | ||
d6bccb44 | 455 | if ((options & VERIFY) || to == NULL) |
b6b5501c RC |
456 | return; |
457 | if (!qflag) { | |
82572cb6 | 458 | printf("notify "); |
3024eb6f RC |
459 | if (rhost) |
460 | printf("@%s ", rhost); | |
b6b5501c RC |
461 | prnames(to); |
462 | } | |
463 | if (nflag) | |
464 | return; | |
465 | ||
82572cb6 | 466 | if ((fd = open(file, 0)) < 0) { |
5f91cf7b | 467 | error("%s: %s\n", file, strerror(errno)); |
82572cb6 RC |
468 | return; |
469 | } | |
470 | if (fstat(fd, &stb) < 0) { | |
5f91cf7b | 471 | error("%s: %s\n", file, strerror(errno)); |
82572cb6 RC |
472 | (void) close(fd); |
473 | return; | |
474 | } | |
475 | if (stb.st_size == 0) { | |
476 | (void) close(fd); | |
b6b5501c RC |
477 | return; |
478 | } | |
479 | /* | |
480 | * Create a pipe to mailling program. | |
481 | */ | |
567e65c3 KB |
482 | (void)sprintf(buf, "%s -oi -t", _PATH_SENDMAIL); |
483 | pf = popen(buf, "w"); | |
3024eb6f | 484 | if (pf == NULL) { |
567e65c3 | 485 | error("notify: \"%s\" failed\n", _PATH_SENDMAIL); |
3024eb6f RC |
486 | (void) close(fd); |
487 | return; | |
488 | } | |
b6b5501c RC |
489 | /* |
490 | * Output the proper header information. | |
491 | */ | |
492 | fprintf(pf, "From: rdist (Remote distribution program)\n"); | |
493 | fprintf(pf, "To:"); | |
0fccdfef RC |
494 | if (!any('@', to->n_name) && rhost != NULL) |
495 | fprintf(pf, " %s@%s", to->n_name, rhost); | |
f7770429 | 496 | else |
0fccdfef RC |
497 | fprintf(pf, " %s", to->n_name); |
498 | to = to->n_next; | |
b6b5501c | 499 | while (to != NULL) { |
0fccdfef RC |
500 | if (!any('@', to->n_name) && rhost != NULL) |
501 | fprintf(pf, ", %s@%s", to->n_name, rhost); | |
82572cb6 | 502 | else |
0fccdfef RC |
503 | fprintf(pf, ", %s", to->n_name); |
504 | to = to->n_next; | |
b6b5501c RC |
505 | } |
506 | putc('\n', pf); | |
f7770429 RC |
507 | if (rhost != NULL) |
508 | fprintf(pf, "Subject: files updated by rdist from %s to %s\n", | |
509 | host, rhost); | |
510 | else | |
511 | fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod)); | |
b6b5501c RC |
512 | putc('\n', pf); |
513 | ||
514 | while ((len = read(fd, buf, BUFSIZ)) > 0) | |
515 | (void) fwrite(buf, 1, len, pf); | |
516 | (void) close(fd); | |
517 | (void) pclose(pf); | |
518 | } | |
519 | ||
b6b5501c | 520 | /* |
d6bccb44 | 521 | * Return true if name is in the list. |
b6b5501c | 522 | */ |
d6bccb44 | 523 | inlist(list, file) |
0fccdfef | 524 | struct namelist *list; |
b6b5501c RC |
525 | char *file; |
526 | { | |
0fccdfef | 527 | register struct namelist *nl; |
b6b5501c | 528 | |
0fccdfef RC |
529 | for (nl = list; nl != NULL; nl = nl->n_next) |
530 | if (!strcmp(file, nl->n_name)) | |
e8109cf8 | 531 | return(1); |
b6b5501c RC |
532 | return(0); |
533 | } | |
534 | ||
e8109cf8 | 535 | /* |
4085f36f | 536 | * Return TRUE if file is in the exception list. |
e8109cf8 | 537 | */ |
4085f36f RC |
538 | except(file) |
539 | char *file; | |
b6b5501c | 540 | { |
4085f36f RC |
541 | register struct subcmd *sc; |
542 | register struct namelist *nl; | |
b6b5501c | 543 | |
e8109cf8 | 544 | if (debug) |
4085f36f | 545 | printf("except(%s)\n", file); |
e8109cf8 | 546 | |
4085f36f RC |
547 | for (sc = subcmds; sc != NULL; sc = sc->sc_next) { |
548 | if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN) | |
e8109cf8 | 549 | continue; |
0fccdfef | 550 | for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) { |
4085f36f RC |
551 | if (sc->sc_type == EXCEPT) { |
552 | if (!strcmp(file, nl->n_name)) | |
553 | return(1); | |
554 | continue; | |
0fccdfef | 555 | } |
4085f36f RC |
556 | re_comp(nl->n_name); |
557 | if (re_exec(file) > 0) | |
558 | return(1); | |
e8109cf8 RC |
559 | } |
560 | } | |
4085f36f | 561 | return(0); |
b6b5501c RC |
562 | } |
563 | ||
564 | char * | |
565 | colon(cp) | |
566 | register char *cp; | |
567 | { | |
568 | ||
569 | while (*cp) { | |
570 | if (*cp == ':') | |
571 | return(cp); | |
572 | if (*cp == '/') | |
573 | return(0); | |
574 | cp++; | |
575 | } | |
576 | return(0); | |
577 | } |