Mercurial > illumos > onarm
annotate usr/src/cmd/fmli/oh/detect.c @ 4:1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Mon, 31 Aug 2009 14:38:03 +0900 |
parents | c9caec207d52 |
children |
rev | line source |
---|---|
0 | 1 /* |
2 * CDDL HEADER START | |
3 * | |
4 * The contents of this file are subject to the terms of the | |
5 * Common Development and Distribution License, Version 1.0 only | |
6 * (the "License"). You may not use this file except in compliance | |
7 * with the License. | |
8 * | |
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
10 * or http://www.opensolaris.org/os/licensing. | |
11 * See the License for the specific language governing permissions | |
12 * and limitations under the License. | |
13 * | |
14 * When distributing Covered Code, include this CDDL HEADER in each | |
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
16 * If applicable, add the following below this CDDL HEADER, with the | |
17 * fields enclosed by brackets "[]" replaced with your own identifying | |
18 * information: Portions Copyright [yyyy] [name of copyright owner] | |
19 * | |
20 * CDDL HEADER END | |
21 */ | |
22 | |
23 /* | |
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. | |
25 * Use is subject to license terms. | |
26 */ | |
27 | |
28 /* Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ | |
29 /* All Rights Reserved */ | |
30 | |
31 | |
4
1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
Koji Uno <koji.uno@sun.com>
parents:
0
diff
changeset
|
32 #pragma ident "%Z%%M% %I% %E% SMI" |
0 | 33 |
34 #include <stdio.h> | |
35 #include <string.h> | |
36 #include <fcntl.h> | |
37 #include <sys/types.h> | |
38 #include <sys/stat.h> | |
39 /*#include <sys/dir.h> this is file system dependent. abs */ | |
40 #include <sys/times.h> | |
41 #include <ctype.h> | |
42 #include "wish.h" | |
43 #include "var_arrays.h" | |
44 #include "typetab.h" | |
45 #include "detabdefs.h" | |
46 #include "partabdefs.h" | |
47 #include "optabdefs.h" | |
48 #include "parse.h" | |
49 #include "sizes.h" | |
50 | |
51 #define ACC_NOTSET 1 | |
52 #define ACC_OKREAD 0 | |
53 #define ACC_NOREAD -1 | |
54 #define ck_readable(X) (access(X, 4)) | |
55 #define NULLSTR "" | |
56 | |
57 /* PGSHFT should be PNUMSHF from <sys/immu.h> see below abs 9/15/99 */ | |
58 /* #define PGSHFT 64 kludge to detect core files */ | |
59 #define LOOKED_AT_OEH 1 | |
60 #define LOOKED_AT_BYTES 2 | |
61 | |
62 extern struct one_part Parts[MAXPARTS]; | |
63 extern struct odft_entry Detab[MAXODFT]; | |
64 | |
65 | |
66 static int Seen_non_printable; | |
67 static int Seen_eighth_bit; | |
68 static int Already_looked; | |
69 static struct oeh Oeh; | |
70 | |
71 int Pathlen; | |
72 | |
73 #ifndef WISH | |
74 void det_mail_in(), det_mail_out(); | |
75 #endif | |
76 static int look_at_bytes(); | |
77 static int magic_heuristics(); | |
78 static int external_heuristics(); | |
79 static int oeu_heuristics(); | |
80 struct opt_entry *obj_to_parts(); | |
81 static bool exist_heuristics(); | |
82 static bool part_heuristics(); | |
83 | |
84 /* The heuristics program drives off of the detection table (defined in | |
85 * detab.c). It cycles through this table, executing heuristics commands | |
86 * as it goes. There are basically 4 kinds of heuristics: | |
87 * | |
88 * Heuristics based on object part names | |
89 * Heuristics based on magic numbers | |
90 * Heuristics based on user-defined functions | |
91 * Heuristics based on internal functions | |
92 * | |
93 * The most efficient method is part-names, the least efficient is | |
94 * user-defined functions since they require a fork(). | |
95 * For this reason, it is probably best for user-defined functions to come | |
96 * last if possible. | |
97 */ | |
98 | |
99 int | |
100 heuristics(path, stray) | |
101 char *path; | |
102 char stray[][FILE_NAME_SIZ]; | |
103 { /* begin heuristics */ | |
104 struct stat sbuf; | |
105 char buf[2048]; /* xed header size */ | |
106 register int i; | |
107 int psize = strlen(path) + 1; | |
108 int size = array_len(stray); | |
109 long docmask = 0L; | |
110 char pathbuf[PATHSIZ]; | |
111 bool is_directory, determined; | |
112 int heur; | |
113 int accessible; | |
114 | |
115 strcpy(pathbuf, path); | |
116 strcat(pathbuf, "/"); | |
117 Pathlen = psize; | |
118 | |
119 for (i = 0; i < size; i++) { | |
120 if (stray[i][0] == '\0') | |
121 continue; /* already determined by other heuristics */ | |
122 /* below, 3 is for: "/" & prefixes */ | |
123 if (psize + (int)strlen(stray[i]) + 3 > PATHSIZ) /* EFT k16 */ | |
124 continue; /* ignore - path too big */ | |
125 strcpy(pathbuf+psize, stray[i]); | |
126 if (stat(pathbuf, &sbuf) == -1) { | |
127 #ifdef _DEBUG | |
128 _debug(stderr, "can't stat %s\n", pathbuf); | |
129 #endif | |
130 continue; | |
131 } | |
132 | |
133 /* MUST be a directory to be check for exist_heuristics; | |
134 * Directories will ONLY be checked for exist_heuristics, part_- | |
135 * heuristics and shell and exec functions. (No magic or internal | |
136 * (oeu,ascii,core,archive,mailin/out) functions will be run.) | |
137 */ | |
138 if (sbuf.st_mode & 040000) | |
139 is_directory = TRUE; | |
140 else | |
141 is_directory = FALSE; | |
142 | |
143 if ( sbuf.st_mode & 04000 ) /* narrow screen file */ | |
144 docmask = M_NAR; | |
145 else | |
146 docmask = 0L; | |
147 | |
148 determined = FALSE; | |
149 accessible = ACC_NOTSET; | |
150 Already_looked = 0; | |
151 for (heur = 0; !determined && Detab[heur].objtype[0]; heur++) { | |
152 switch (Detab[heur].func_type) { | |
153 case F_DPARTS: | |
154 if (is_directory == FALSE) | |
155 continue; | |
156 if (exist_heuristics(path, stray[i], Detab[heur].objtype, | |
157 Detab[heur].defmask, Detab[heur].defodi, | |
158 sbuf.st_mtime)) | |
159 determined = TRUE; | |
160 break; | |
161 case F_PARTS: | |
162 if (part_heuristics(path, stray, i, Detab[heur].objtype, | |
163 Detab[heur].defmask, Detab[heur].defodi, | |
164 sbuf.st_mtime, NULL)) | |
165 determined = TRUE; | |
166 break; | |
167 case F_MAGIC: | |
168 if (is_directory == TRUE) | |
169 continue; | |
170 if (accessible == ACC_NOTSET) | |
171 accessible = ck_readable(pathbuf); | |
172 if (accessible == ACC_NOREAD) | |
173 break; | |
174 if (magic_heuristics(path, stray[i], Detab[heur].objtype, | |
175 Detab[heur].defmask, Detab[heur].defodi, | |
176 sbuf.st_mtime, Detab[heur].magic_offset, | |
177 Detab[heur].magic_bytes)) | |
178 determined = TRUE; | |
179 break; | |
180 case F_SHELL: | |
181 case F_EXEC: | |
182 if (external_heuristics(path, stray[i], Detab[heur].defmask, | |
183 Detab[heur].defodi, sbuf.st_mtime, NULL)) | |
184 determined = TRUE; | |
185 break; | |
186 case F_INT: | |
187 if (is_directory == TRUE) | |
188 continue; | |
189 switch (Detab[heur].intern_func) { | |
190 case IDF_ZLASC: /* zero length ascii */ | |
191 if (sbuf.st_size == 0) { | |
192 /* file pathsize already tested at top of this fcn */ | |
193 ott_make_entry(stray[i], stray[i], Detab[heur].objtype, | |
194 docmask|Detab[heur].defmask, Detab[heur].defodi, | |
195 sbuf.st_mtime); | |
196 stray[i][0] = '\0'; | |
197 determined = TRUE; | |
198 } | |
199 break; | |
200 case IDF_ASC: | |
201 if (accessible == ACC_NOTSET) | |
202 accessible = ck_readable(pathbuf); | |
203 if (accessible == ACC_NOREAD) | |
204 break; | |
205 look_at_bytes(path, stray[i]); | |
206 if (! Seen_non_printable) { | |
207 /* file pathsize already tested at top of this fcn */ | |
208 ott_make_entry(stray[i], stray[i], Detab[heur].objtype, | |
209 docmask|Detab[heur].defmask, Detab[heur].defodi, | |
210 sbuf.st_mtime); | |
211 stray[i][0] = '\0'; | |
212 determined = TRUE; | |
213 } | |
214 break; | |
215 case IDF_TRANS: | |
216 if (accessible == ACC_NOTSET) | |
217 accessible = ck_readable(pathbuf); | |
218 if (accessible == ACC_NOREAD) | |
219 break; | |
220 if (oeu_heuristics(path, stray[i], Detab[heur].objtype, | |
221 Detab[heur].defmask, Detab[heur].defodi, | |
222 sbuf.st_mtime)) { | |
223 determined = TRUE; | |
224 } | |
225 break; | |
226 case IDF_CORE: | |
227 /* if a file is named "core" and it is at least 3 pages long | |
228 * and it is an even multiple of a page size, and it has at | |
229 * least one byte within the first five hundred with the | |
230 * eighth bit set, then it is probably a core file. | |
231 * >> This sounds nice but you can't do this with PGSHFT = 64 | |
232 * >> which causes the code below to do nothing more than generate | |
233 * >> compiler warnings. you could replace PGSHFT with PNUMSHFT | |
234 * >> from <sys/immu.h> but this introduces machine dependencies | |
235 * >> and may still get into trouble when memory management changes. | |
236 * >> since no one but the compiler has complained, I commented out | |
237 * >> the code. abs 9/15/88 | |
238 */ | |
239 if (accessible == ACC_NOTSET) | |
240 accessible = ck_readable(pathbuf); | |
241 if (accessible == ACC_NOREAD) | |
242 break; | |
243 look_at_bytes(path, stray[i]); | |
244 if (strcmp(stray[i], "core") == 0 && Seen_non_printable | |
245 /* && sbuf.st_size >= (1<<PGSHFT)*3 && ! (sbuf.st_size % (1<<PGSHFT) ) */ | |
246 ) | |
247 { | |
248 /* file pathsize already tested at top of this fcn */ | |
249 ott_make_entry(stray[i],stray[i],Detab[heur].objtype, | |
250 Detab[heur].defmask,Detab[heur].defodi, | |
251 sbuf.st_mtime); | |
252 stray[i][0] = '\0'; | |
253 determined = TRUE; | |
254 } | |
255 break; | |
256 case IDF_ARCH: | |
257 if (accessible == ACC_NOTSET) | |
258 accessible = ck_readable(pathbuf); | |
259 if (accessible == ACC_NOREAD) | |
260 break; | |
261 look_at_bytes(path, stray[i]); | |
262 if (Seen_non_printable && has_suffix(stray[i], ".a") && | |
263 strncmp(buf, "!<arch>", 7) == 0) { | |
264 /* file pathsize already tested at top of this fcn */ | |
265 ott_make_entry(stray[i], stray[i], Detab[heur].objtype, | |
266 Detab[heur].defmask,Detab[heur].defodi, | |
267 sbuf.st_mtime); | |
268 stray[i][0] = '\0'; | |
269 determined = TRUE; | |
270 } | |
271 break; | |
272 case IDF_ENCRYPT: | |
273 if (accessible == ACC_NOTSET) | |
274 accessible = ck_readable(pathbuf); | |
275 if (accessible == ACC_NOREAD) | |
276 break; | |
277 if (oeu_heuristics(path, stray[i], NULL, | |
278 Detab[heur].defmask, Detab[heur].defodi, | |
279 sbuf.st_mtime)) { | |
280 determined = TRUE; | |
281 } | |
282 break; | |
283 case IDF_UNKNOWN: | |
284 /* file pathsize already tested at top of this fcn */ | |
285 ott_make_entry(stray[i], stray[i], Detab[heur].objtype, | |
286 Detab[heur].defmask, Detab[heur].defodi, | |
287 sbuf.st_mtime); | |
288 stray[i][0] = '\0'; | |
289 determined = TRUE; | |
290 break; | |
291 #ifndef WISH | |
292 case IDF_MAIL_IN: | |
293 if (part_heuristics(path, stray, i, Detab[heur].objtype, | |
294 Detab[heur].defmask, Detab[heur].defodi, | |
295 sbuf.st_mtime, det_mail_in)) { | |
296 determined = TRUE; | |
297 } | |
298 break; | |
299 case IDF_MAIL_OUT: | |
300 if (part_heuristics(path, stray, i, Detab[heur].objtype, | |
301 Detab[heur].defmask, Detab[heur].defodi, | |
302 sbuf.st_mtime, det_mail_out)) { | |
303 determined = TRUE; | |
304 } | |
305 break; | |
306 #endif | |
307 #ifdef _DEBUG | |
308 default: | |
309 _debug(stderr, "no such func: %d\n", Detab[heur].intern_func); | |
310 #endif | |
311 } | |
312 } | |
313 } | |
314 } | |
315 return(0); | |
316 } | |
317 | |
318 static bool | |
319 | |
320 exist_heuristics(path, name, objtype, mask, odi, mtime) | |
321 char *path, *name, *objtype; | |
322 long mask; | |
323 char *odi; | |
324 time_t mtime; /* EFT abs k16 */ | |
325 { | |
326 register int i; | |
327 struct opt_entry *partab; | |
328 int part_offset, numparts; | |
329 char *base; | |
330 char *pattern; | |
331 char *part_construct(); | |
332 int found[MAXOBJPARTS]; | |
333 char pathbuf[PATHSIZ]; | |
334 char *part_match(); | |
335 | |
336 /* get the parts table associated with objtype */ | |
337 | |
338 if ((partab = obj_to_parts(objtype)) == NULL) | |
339 return(FALSE); | |
340 part_offset = partab->part_offset; | |
341 numparts = partab->numparts; | |
342 | |
343 if ((base = part_match(name, Parts[part_offset].part_template)) == NULL) | |
344 return(FALSE); | |
345 | |
346 found[0] = 1; | |
347 for (i = 1; i < numparts; i++) | |
348 found[i] = -1; | |
349 | |
350 for (i = 1; i < numparts; i++) { | |
351 pattern = part_construct(base, Parts[part_offset+i].part_template); | |
352 /* if any part's path is > PATHSIZ, do not display it */ | |
353 if ((int)strlen(pattern) + Pathlen + 3 > PATHSIZ) /* EFT k16 */ | |
354 return(FALSE); | |
355 sprintf(pathbuf, "%s/%s", path, pattern); | |
356 if (access(pathbuf, 0) == -1) { /* exists ? */ | |
357 if (!(Parts[part_offset+i].part_flags & PRT_OPT)) | |
358 return(FALSE); | |
359 } else { | |
360 found[i] = 1; | |
361 } | |
362 } | |
363 /* file pathsize already tested in heuristics() - this uses "name" */ | |
364 ott_make_entry(name, base, objtype, mask|partab->int_class, odi, mtime); | |
365 | |
366 for (i = 1; i < numparts; i++) { | |
367 if (found[i] == 1) | |
368 /* file pathsize already tested when each part found */ | |
369 ott_make_entry(part_construct(base, | |
370 Parts[part_offset+i].part_template), | |
371 NULLSTR, NULL, mask|partab->int_class, NULL, mtime); | |
372 } | |
373 | |
374 return(TRUE); | |
375 } | |
376 | |
377 static bool | |
378 part_heuristics(path, stray, index, objtype, mask, odi, mtime, info_func) | |
379 char *path; | |
380 char stray[][FILE_NAME_SIZ]; | |
381 char *objtype; | |
382 int index; | |
383 long mask; | |
384 char *odi; | |
385 time_t mtime; /* EFT abs k16 */ | |
386 void (*info_func)(); | |
387 { | |
388 register int i, j; | |
389 int found[MAXOBJPARTS]; | |
390 struct opt_entry *partab; | |
391 int part_offset, numparts; | |
392 int size = array_len(stray); | |
393 char *p, base[PNAMESIZ]; | |
394 char fullpath[PATHSIZ]; | |
395 char *dname; | |
396 char *part_match(); | |
397 | |
398 /* get the parts table associated with objtype */ | |
399 | |
400 if ((partab = obj_to_parts(objtype)) == NULL) | |
401 return(FALSE); | |
402 part_offset = partab->part_offset; | |
403 numparts = partab->numparts; | |
404 | |
405 for (i = 0; i < numparts; i++) | |
406 found[i] = -1; | |
407 | |
408 /* look for the entry index in the table, in reverse order since | |
409 * the more restrictive names are at the end (for example, the first | |
410 * parts template is often unrestricted). | |
411 */ | |
412 | |
413 for (i = numparts-1; i >= 0; i--) | |
414 if (p = part_match(stray[index], Parts[part_offset+i].part_template)) { | |
415 found[i] = index; | |
416 strcpy(base, p); | |
417 break; | |
418 } | |
419 | |
420 if (!p) /* was not found */ | |
421 return(FALSE); | |
422 | |
423 /* if any part's path is > PATHSIZ, do not display it */ | |
424 if ((found[i] != -1) && | |
425 ((int)strlen(stray[found[i]]) + Pathlen + 3 > PATHSIZ)) /* EFT k16 */ | |
426 return(FALSE); | |
427 | |
428 /* scan through the rest of the parts, looking in the stray | |
429 * array for each one. If a required part is ever not found, | |
430 * or if the name is > PATHSIZ, | |
431 * then immediately return FALSE. | |
432 */ | |
433 | |
434 for (i = 0; i < numparts; i++) { | |
435 /* don't look for an already found part */ | |
436 if (found[i] != -1) | |
437 continue; | |
438 for (j = 0; j < size; j++) { | |
439 if (stray[j][0] == '\0' || j == index) | |
440 continue; | |
441 if ((p=part_match(stray[j], Parts[part_offset+i].part_template)) && | |
442 strcmp(p, base) == 0) { | |
443 found[i] = j; | |
444 break; | |
445 } | |
446 } | |
447 | |
448 /* if a required part is not found, then return FALSE */ | |
449 | |
450 if (found[i] == -1 && !(Parts[part_offset+i].part_flags & PRT_OPT)) | |
451 return(FALSE); | |
452 /* if any part's path is > PATHSIZ, do not display it */ | |
453 if ((found[i] != -1) && | |
454 ((int)strlen(stray[found[i]]) + Pathlen + 3 > PATHSIZ)) /*EFT k16*/ | |
455 return(FALSE); | |
456 } | |
457 | |
458 /* at this point, we should have all the parts, so we will go | |
459 * through the found array and make entries for each part. | |
460 */ | |
461 | |
462 j = 0; | |
463 while (found[j] == -1) | |
464 j++; | |
465 | |
466 if (info_func != NULL) { | |
467 strcpy(fullpath, path); | |
468 strcat(fullpath, "/"); | |
469 strcat(fullpath, stray[found[j]]); | |
470 (*info_func)(fullpath, &dname, &odi, &mask, &mtime); | |
471 } else { | |
472 if (base && *base) | |
473 dname = base; | |
474 else | |
475 dname = stray[found[j]]; | |
476 } | |
477 /* file pathsize already tested when each part found */ | |
478 ott_make_entry(stray[found[j]], dname, objtype, mask|partab->int_class, odi, mtime); | |
479 stray[found[j]][0] = '\0'; | |
480 | |
481 for (i = j+1; i < numparts; i++) { | |
482 if (found[i] != -1) { | |
483 /* file pathsize already tested when each part found */ | |
484 ott_make_entry(stray[found[i]], NULL, NULL, | |
485 mask|partab->int_class, NULL, mtime); | |
486 stray[found[i]][0] = '\0'; | |
487 } | |
488 } | |
489 | |
490 return(TRUE); | |
491 } | |
492 | |
493 static int | |
494 look_at_bytes(path, file) | |
495 char *path, *file; | |
496 { | |
497 char buf[PATHSIZ]; | |
498 register char *p; | |
499 register int numread; | |
500 register int fd; | |
501 | |
502 if (Already_looked & LOOKED_AT_BYTES) | |
503 return (0); | |
504 | |
505 Already_looked |= LOOKED_AT_BYTES; | |
506 Seen_eighth_bit = Seen_non_printable = FALSE; | |
507 sprintf(buf, "%s/%s", path, file); | |
508 if ((fd = open(buf, O_RDONLY)) < 0) | |
509 return (0); | |
510 numread = read(fd, buf, sizeof(buf)); | |
511 close(fd); | |
512 | |
513 for (p = buf; numread > 0; numread--, p++) | |
514 if (!isprint(*p) && !isspace(*p) && *p != '\7' && *p != '\b') { | |
515 Seen_non_printable = TRUE; | |
516 if (!isascii(*p)) | |
517 Seen_eighth_bit = TRUE; | |
518 } | |
519 return (0); | |
520 } | |
521 | |
522 static int | |
523 magic_heuristics(path, name, objtype, mask, odi, mtime, offsets, bytes) | |
524 char *path, *name, *objtype; | |
525 long mask; | |
526 char *odi; | |
527 time_t mtime; /* EFT abs k16 */ | |
528 long *offsets; | |
529 char *bytes; | |
530 { | |
531 FILE *fp; | |
532 register int i; | |
533 char buf[PATHSIZ]; | |
534 | |
535 /* file pathsize already tested in heuristics() */ | |
536 sprintf(buf, "%s/%s", path, name); | |
537 if ((fp = fopen(buf, "r")) == NULL) | |
538 return(0); | |
539 | |
540 for (i = 0; offsets[i] != -1; i++) { | |
541 /* if the next offset is equal to the previous plus one, no need | |
542 * to seek | |
543 */ | |
544 if (i == 0 || offsets[i-1] != offsets[i] - 1) { | |
545 if (fseek(fp, offsets[i], 0) != 0) { | |
546 fclose(fp); | |
547 return(0); | |
548 } | |
549 } | |
550 if (getc(fp) != bytes[i]) { | |
551 fclose(fp); | |
552 return(0); | |
553 } | |
554 } | |
555 | |
556 fclose(fp); | |
557 ott_make_entry(name, name, objtype, mask, odi, mtime); | |
558 name[0] = 0; | |
559 | |
560 return(1); | |
561 } | |
562 | |
563 | |
564 /* currently unimplemented */ | |
565 | |
566 static int | |
567 external_heuristics() | |
568 { | |
569 return(0); | |
570 } | |
571 | |
572 static int | |
573 oeu_heuristics(path, name, objtype, defmask, defodi, mtime) | |
574 char *path, *name, *objtype; | |
575 long defmask; | |
576 char *defodi; | |
577 time_t mtime; /* EFT abs k16 */ | |
578 { | |
579 char fullpath_or_odi[PATHSIZ]; | |
580 | |
581 /* file pathsize already tested in heuristics() */ | |
582 sprintf(fullpath_or_odi, "%s/%s", path, name); | |
583 if (look_at_oeh(fullpath_or_odi) != 0) { | |
584 return(0); | |
585 } | |
586 | |
587 if (!objtype) { /* any encrypted object */ | |
588 if (Oeh.encrytest) | |
589 objtype = Oeh.num; | |
590 else | |
591 return (0); | |
592 } | |
593 /* reuse fullpath_or_odi variable */ | |
594 strcpy(fullpath_or_odi, "TYPE="); | |
595 strcat(fullpath_or_odi, Oeh.type); | |
596 ott_make_entry(name, name, objtype, | |
597 defmask, (defodi&&*defodi)?defodi:fullpath_or_odi, mtime); | |
598 return(1); | |
599 } | |
600 | |
601 int | |
602 look_at_oeh(path) | |
603 char *path; | |
604 { | |
605 static int oeh_retcode; | |
606 | |
607 if (Already_looked & LOOKED_AT_OEH) | |
608 return(oeh_retcode); | |
609 | |
610 #ifdef WISH | |
611 oeh_retcode = oeucheck(path, &Oeh, READ_HEADER); | |
612 #else | |
613 oeh_retcode = oeuparse(path, &Oeh, READ_HEADER); | |
614 #endif | |
615 #ifdef _DEBUG | |
616 _debug(stderr, "oeuparse(%s) returned %d\n", path, oeh_retcode); | |
617 #endif | |
618 Already_looked |= LOOKED_AT_OEH; | |
619 return(oeh_retcode); | |
620 } |