1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
27 * library interface to file
29 * the sum of the hacks {s5,v10,planix} is _____ than the parts
32 static const char id
[] = "\n@(#)$Id: magic library (AT&T Research) 2008-09-10 $\0\n";
34 static const char lib
[] = "libast:magic";
45 #define T(m) (*m?ERROR_translate(NiL,NiL,lib,m):m)
47 #define match(s,p) strgrpmatch(s,p,NiL,0,STR_LEFT|STR_RIGHT|STR_ICASE)
49 #define MAXNEST 10 /* { ... } nesting limit */
50 #define MINITEM 4 /* magic buffer rounding */
52 typedef struct /* identifier dictionary entry */
54 const char name
[16]; /* identifier name */
55 int value
; /* identifier value */
56 Dtlink_t link
; /* dictionary link */
59 typedef struct Edit
/* edit substitution */
61 struct Edit
* next
; /* next in list */
62 regex_t
* from
; /* from pattern */
67 typedef struct /* loop info */
69 struct Entry
* lab
; /* call this function */
70 int start
; /* start here */
71 int size
; /* increment by this amount */
72 int count
; /* dynamic loop count */
73 int offset
; /* dynamic offset */
76 typedef struct Entry
/* magic file entry */
78 struct Entry
* next
; /* next in list */
79 char* expr
; /* offset expression */
87 } value
; /* comparison value */
88 char* desc
; /* file description */
89 char* mime
; /* file mime type */
90 unsigned long offset
; /* offset in bytes */
91 unsigned long mask
; /* mask before compare */
92 char cont
; /* continuation operation */
93 char type
; /* datum type */
94 char op
; /* comparison operation */
95 char nest
; /* { or } nesting operation */
96 char swap
; /* forced swap order */
101 #if (CC_MAPS*CC_BIT) <= (CHAR_BIT*2)
102 typedef unsigned short Cctype_t
;
104 typedef unsigned long Cctype_t
;
108 #define CC_control 0x02
109 #define CC_latin 0x04
110 #define CC_binary 0x08
111 #define CC_utf_8 0x10
113 #define CC_notext CC_text /* CC_text is flipped before checking */
115 #define CC_MASK (CC_binary|CC_latin|CC_control|CC_text)
117 #define CCTYPE(c) (((c)>0240)?CC_binary:((c)>=0200)?CC_latin:((c)<040&&(c)!=007&&(c)!=011&&(c)!=012&&(c)!=013&&(c)!=015)?CC_control:CC_text)
123 #define ID_COPYBOOK 4
124 #define ID_CPLUSPLUS 5
137 #define ID_MAX ID_YACC
140 #define INFO_blocks 2
142 #define INFO_fstype 4
151 #define _MAGIC_PRIVATE_ \
152 Magicdisc_t* disc; /* discipline */ \
153 Vmalloc_t* vm; /* vmalloc region */ \
154 Entry_t* magic; /* parsed magic table */ \
155 Entry_t* magiclast; /* last entry in magic */ \
156 char* mime; /* MIME type */ \
157 unsigned char* x2n; /* CC_ALIEN=>CC_NATIVE */ \
158 char fbuf[SF_BUFSIZE + 1]; /* file data */ \
159 char xbuf[SF_BUFSIZE + 1]; /* indirect file data */ \
160 char nbuf[256]; /* !CC_NATIVE data */ \
161 char mbuf[64]; /* mime string */ \
162 char sbuf[64]; /* type suffix string */ \
163 char tbuf[2 * PATH_MAX]; /* type string */ \
164 Cctype_t cctype[UCHAR_MAX + 1]; /* char code types */ \
165 unsigned int count[UCHAR_MAX + 1]; /* char frequency count */ \
166 unsigned int multi[UCHAR_MAX + 1]; /* muti char count */ \
167 int keep[MAXNEST]; /* ckmagic nest stack */ \
168 char* cap[MAXNEST]; /* ckmagic mime stack */ \
169 char* msg[MAXNEST]; /* ckmagic text stack */ \
170 Entry_t* ret[MAXNEST]; /* ckmagic return stack */ \
171 int fbsz; /* fbuf size */ \
172 int fbmx; /* fbuf max size */ \
173 int xbsz; /* xbuf size */ \
174 int swap; /* swap() operation */ \
175 unsigned long flags; /* disc+open flags */ \
176 long xoff; /* xbuf offset */ \
177 int identifier[ID_MAX + 1]; /* Info_t identifier */ \
178 Sfio_t* fp; /* fbuf fp */ \
179 Sfio_t* tmp; /* tmp string */ \
180 regdisc_t redisc; /* regex discipline */ \
181 Dtdisc_t dtdisc; /* dict discipline */ \
182 Dt_t* idtab; /* identifier dict */ \
183 Dt_t* infotab; /* info keyword dict */
187 static Info_t dict
[] = /* keyword dictionary */
189 { "COMMON", ID_FORTRAN
},
190 { "COMPUTE", ID_COBOL
},
191 { "COMP", ID_COPYBOOK
},
192 { "COMPUTATIONAL",ID_COPYBOOK
},
194 { "DEFINED", ID_PL1
},
195 { "DIMENSION", ID_FORTRAN
},
196 { "DIVISION", ID_COBOL
},
197 { "FILLER", ID_COPYBOOK
},
199 { "FUNCTION", ID_FORTRAN
},
201 { "INTEGER", ID_FORTRAN
},
203 { "OPTIONS", ID_PL1
},
204 { "PERFORM", ID_COBOL
},
205 { "PIC", ID_COPYBOOK
},
206 { "REAL", ID_FORTRAN
},
207 { "REDEFINES", ID_COPYBOOK
},
208 { "S9", ID_COPYBOOK
},
209 { "SECTION", ID_COBOL
},
210 { "SELECT", ID_COBOL
},
211 { "SUBROUTINE", ID_FORTRAN
},
213 { "VALUE", ID_COPYBOOK
},
215 { "binary", ID_YACC
},
216 { "block", ID_FORTRAN
},
220 { "class", ID_CPLUSPLUS
},
221 { "clr", ID_NOTEXT
},
223 { "common", ID_FORTRAN
},
225 { "dimension", ID_FORTRAN
},
232 { "function", ID_FORTRAN
},
236 { "include", ID_INCL1
},
238 { "integer", ID_FORTRAN
},
239 { "jmp", ID_NOTEXT
},
241 { "libc", ID_INCL2
},
244 { "mov", ID_NOTEXT
},
245 { "private", ID_CPLUSPLUS
},
246 { "public", ID_CPLUSPLUS
},
247 { "real", ID_FORTRAN
},
248 { "register", ID_C
},
249 { "right", ID_YACC
},
250 { "sfio", ID_INCL2
},
252 { "stdio", ID_INCL2
},
254 { "subroutine", ID_FORTRAN
},
255 { "sys", ID_NOTEXT
},
258 { "tst", ID_NOTEXT
},
262 { "union", ID_YACC
},
266 static Info_t info
[] =
268 { "atime", INFO_atime
},
269 { "blocks", INFO_blocks
},
270 { "ctime", INFO_ctime
},
271 { "fstype", INFO_fstype
},
273 { "mode", INFO_mode
},
274 { "mtime", INFO_mtime
},
275 { "name", INFO_name
},
276 { "nlink", INFO_nlink
},
277 { "size", INFO_size
},
282 * return pointer to data at offset off and size siz
286 getdata(register Magic_t
* mp
, register long off
, register int siz
)
292 if (off
+ siz
<= mp
->fbsz
)
293 return mp
->fbuf
+ off
;
294 if (off
< mp
->xoff
|| off
+ siz
> mp
->xoff
+ mp
->xbsz
)
296 if (off
+ siz
> mp
->fbmx
)
298 n
= (off
/ (SF_BUFSIZE
/ 2)) * (SF_BUFSIZE
/ 2);
299 if (sfseek(mp
->fp
, n
, SEEK_SET
) != n
)
301 if ((mp
->xbsz
= sfread(mp
->fp
, mp
->xbuf
, sizeof(mp
->xbuf
) - 1)) < 0)
307 mp
->xbuf
[mp
->xbsz
] = 0;
309 if (off
+ siz
> mp
->xoff
+ mp
->xbsz
)
312 return mp
->xbuf
+ off
- mp
->xoff
;
316 * @... evaluator for strexpr()
320 indirect(const char* cs
, char** e
, void* handle
)
322 register char* s
= (char*)cs
;
323 register Magic_t
* mp
= (Magic_t
*)handle
;
331 n
= *++s
== '(' ? strexpr(s
, e
, indirect
, mp
) : strtol(s
, e
, 0);
337 if (p
= getdata(mp
, n
, 1))
338 n
= *(unsigned char*)p
;
345 if (p
= getdata(mp
, n
, 2))
346 n
= swapget(mp
->swap
, p
, 2);
353 if (p
= getdata(mp
, n
, 8))
354 n
= swapget(mp
->swap
, p
, 8);
361 if (p
= getdata(mp
, n
, 4))
362 n
= swapget(mp
->swap
, p
, 4);
370 else if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
371 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%s in indirect expression", *e
);
376 * emit regex error message
380 regmessage(Magic_t
* mp
, regex_t
* re
, int code
)
384 if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
386 regerror(code
, re
, buf
, sizeof(buf
));
387 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 3, "regex: %s", buf
);
392 * decompose vcodex(3) method composition
396 vcdecomp(char* b
, char* e
, unsigned char* m
, unsigned char* x
)
405 map
= CCMAP(CC_ASCII
, CC_NATIVE
);
414 if (m
< (x
- 1) && !*(m
+ 1))
424 while (b
< e
&& (c
= *o
++))
429 case 0: o
= "delta"; break;
430 case 1: o
= "huffman"; break;
431 case 2: o
= "huffgroup"; break;
432 case 3: o
= "arith"; break;
433 case 4: o
= "bwt"; break;
434 case 5: o
= "rle"; break;
435 case 6: o
= "mtf"; break;
436 case 7: o
= "transpose"; break;
437 case 8: o
= "table"; break;
438 case 9: o
= "huffpart"; break;
439 case 50: o
= "map"; break;
440 case 100: o
= "recfm"; break;
441 case 101: o
= "ss7"; break;
442 default: o
= "UNKNOWN"; break;
445 while (b
< e
&& (c
= *o
++))
449 while (b
< e
&& m
< x
&& (c
= *m
++))
460 n
= (n
<<7) | (*m
& 0x7f);
472 * check for magic table match in buf
476 ckmagic(register Magic_t
* mp
, const char* file
, char* buf
, struct stat
* st
, unsigned long off
)
478 register Entry_t
* ep
;
481 register int level
= 0;
489 regmatch_t matches
[10];
492 b
= mp
->msg
[0] = buf
;
493 mp
->mime
= mp
->cap
[0] = 0;
495 for (ep
= mp
->magic
; ep
; ep
= ep
->next
)
500 if (++level
>= MAXNEST
)
506 mp
->mime
= mp
->cap
[0];
509 mp
->keep
[level
] = mp
->keep
[level
- 1] != 0;
511 mp
->cap
[level
] = mp
->mime
;
516 if (mp
->keep
[level
] && b
> buf
)
522 b
= mp
->msg
[0] = buf
;
523 mp
->mime
= mp
->cap
[0] = 0;
528 if (mp
->keep
[level
] && call
< (MAXNEST
- 1))
530 mp
->ret
[++call
] = ep
;
536 ep
= mp
->ret
[call
--];
541 if (mp
->keep
[level
] > 1)
545 if (!mp
->keep
[level
])
548 mp
->mime
= mp
->cap
[level
];
556 num
= ep
->offset
+ off
;
561 num
= strexpr(ep
->expr
, NiL
, indirect
, mp
) + off
;
577 ep
->type
= toupper(ep
->type
);
580 if (ep
->type
== 'e' || ep
->type
== 'm' || ep
->type
== 's')
582 p
= fmtgid(st
->st_gid
);
583 ep
->type
= toupper(ep
->type
);
592 if (ep
->type
== 'e' || ep
->type
== 'm' || ep
->type
== 's')
594 p
= fmtmode(st
->st_mode
, 0);
595 ep
->type
= toupper(ep
->type
);
599 num
= modex(st
->st_mode
);
610 if (base
= strrchr(file
, '/'))
616 ep
->type
= toupper(ep
->type
);
627 if (ep
->type
== 'e' || ep
->type
== 'm' || ep
->type
== 's')
629 p
= fmtuid(st
->st_uid
);
630 ep
->type
= toupper(ep
->type
);
643 if (!(p
= getdata(mp
, num
, 1)))
645 num
= *(unsigned char*)p
;
649 if (!(p
= getdata(mp
, num
, 2)))
651 num
= swapget(ep
->swap
? (~ep
->swap
^ mp
->swap
) : mp
->swap
, p
, 2);
657 if (!(p
= getdata(mp
, num
, 4)))
659 num
= swapget(ep
->swap
? (~ep
->swap
^ mp
->swap
) : mp
->swap
, p
, 4);
663 if (!(p
= getdata(mp
, num
, 8)))
665 num
= swapget(ep
->swap
? (~ep
->swap
^ mp
->swap
) : mp
->swap
, p
, 8);
669 if (!(p
= getdata(mp
, num
, 0)))
675 if ((c
= regexec(ep
->value
.sub
, p
, elementsof(matches
), matches
, 0)) || (c
= regsubexec(ep
->value
.sub
, p
, elementsof(matches
), matches
)))
678 if (c
>= sizeof(mp
->nbuf
))
679 c
= sizeof(mp
->nbuf
) - 1;
680 p
= (char*)memcpy(mp
->nbuf
, p
, c
);
682 ccmapstr(mp
->x2n
, p
, c
);
683 if ((c
= regexec(ep
->value
.sub
, p
, elementsof(matches
), matches
, 0)) || (c
= regsubexec(ep
->value
.sub
, p
, elementsof(matches
), matches
)))
685 if (c
!= REG_NOMATCH
)
686 regmessage(mp
, ep
->value
.sub
, c
);
690 p
= ep
->value
.sub
->re_sub
->re_buf
;
693 if (mp
->keep
[level
]++ && b
> buf
&& *(b
- 1) != ' ' && *t
&& *t
!= ',' && *t
!= '.' && *t
!= '\b')
695 b
+= sfsprintf(b
, PATH_MAX
- (b
- buf
), *q
? q
: "%s", p
+ (*p
== '\b'));
701 if (!(p
= getdata(mp
, num
, ep
->mask
)))
705 if (!(p
= getdata(mp
, num
, 0)))
713 if (*ep
->value
.str
== '*' && !*(ep
->value
.str
+ 1) && isprint(*p
))
715 if ((ep
->type
== 'm' || ep
->type
== 'M') ? strmatch(p
, ep
->value
.str
) : !memcmp(p
, ep
->value
.str
, ep
->mask
))
717 if (p
== mp
->nbuf
|| ep
->mask
>= sizeof(mp
->nbuf
))
719 p
= (char*)memcpy(mp
->nbuf
, p
, ep
->mask
);
721 ccmapstr(mp
->x2n
, p
, ep
->mask
);
724 if (mp
->keep
[level
]++ && b
> buf
&& *(b
- 1) != ' ' && *q
&& *q
!= ',' && *q
!= '.' && *q
!= '\b')
726 for (t
= p
; (c
= *t
) >= 0 && c
<= 0177 && isprint(c
) && c
!= '\n'; t
++);
728 b
+= sfsprintf(b
, PATH_MAX
- (b
- buf
), q
+ (*q
== '\b'), p
);
742 if (num
== ep
->value
.num
)
750 if ((num
= swapget(mp
->swap
= 1, p
, 2) & mask
) == ep
->value
.num
)
752 if (!(mp
->swap
& (mp
->swap
+ 1)))
757 else if (ep
->type
== 'l')
759 for (c
= 1; c
< 4; c
++)
760 if ((num
= swapget(mp
->swap
= c
, p
, 4) & mask
) == ep
->value
.num
)
762 if (!(mp
->swap
& (mp
->swap
+ 1)))
767 else if (ep
->type
== 'q')
769 for (c
= 1; c
< 8; c
++)
770 if ((num
= swapget(mp
->swap
= c
, p
, 8) & mask
) == ep
->value
.num
)
776 if (num
!= ep
->value
.num
)
781 if (num
^ ep
->value
.num
)
786 if (num
> ep
->value
.num
)
791 if (num
< ep
->value
.num
)
796 if (num
> 0 && mp
->keep
[level
] && call
< (MAXNEST
- 1))
798 if (!ep
->value
.loop
->count
)
800 ep
->value
.loop
->count
= num
;
801 ep
->value
.loop
->offset
= off
;
802 off
= ep
->value
.loop
->start
;
804 else if (!--ep
->value
.loop
->count
)
806 off
= ep
->value
.loop
->offset
;
810 off
+= ep
->value
.loop
->size
;
811 mp
->ret
[++call
] = ep
;
812 ep
= ep
->value
.loop
->lab
;
819 t
= ckmagic(mp
, file
, b
+ (b
> buf
), st
, num
);
835 if (!(t
= strrchr(file
, '.')))
837 sfprintf(mp
->tmp
, "/reg/classes_root/%s", t
);
838 if (!(t
= sfstruse(mp
->tmp
)) || !(rp
= sfopen(NiL
, t
, "r")))
843 while (t
= sfgetr(rp
, '\n', 1))
845 if (strneq(t
, "Content Type=", 13))
847 ep
->mime
= vmnewof(mp
->vm
, ep
->mime
, char, sfvalue(rp
), 0);
848 strcpy(ep
->mime
, t
+ 13);
854 sfprintf(mp
->tmp
, "/reg/classes_root/%s", t
);
855 if ((e
= sfstruse(mp
->tmp
)) && (gp
= sfopen(NiL
, e
, "r")))
857 ep
->desc
= vmnewof(mp
->vm
, ep
->desc
, char, strlen(t
), 1);
870 if (!strncasecmp(t
, "microsoft", 9))
874 e
= "application/x-ms-";
875 ep
->mime
= vmnewof(mp
->vm
, ep
->mime
, char, strlen(t
), strlen(e
));
876 e
= strcopy(ep
->mime
, e
);
877 while ((c
= *t
++) && c
!= '.' && c
!= ' ')
878 *e
++ = isupper(c
) ? tolower(c
) : c
;
881 while (t
= sfgetr(gp
, '\n', 1))
882 if (*t
&& !streq(t
, "\"\""))
884 ep
->desc
= vmnewof(mp
->vm
, ep
->desc
, char, sfvalue(gp
), 0);
892 for (t
= T(ep
->desc
); *t
; t
++)
895 if (!mp
->keep
[level
])
901 if (ep
->cont
== '#' && !mp
->keep
[level
])
907 if (!(p
= getdata(mp
, num
, 4)))
913 c
= (c
<<7) | (*p
& 0x7f);
914 } while (*p
++ & 0x80);
915 if (!(p
= getdata(mp
, num
, c
)))
917 if (mp
->keep
[level
]++ && b
> buf
&& *(b
- 1) != ' ')
922 b
= vcdecomp(b
, buf
+ PATH_MAX
, (unsigned char*)p
, (unsigned char*)p
+ c
);
928 if (mp
->keep
[level
]++ && b
> buf
&& *(b
- 1) != ' ' && *q
&& *q
!= ',' && *q
!= '.' && *q
!= '\b')
930 if (ep
->type
== 'd' || ep
->type
== 'D')
931 b
+= sfsprintf(b
, PATH_MAX
- (b
- buf
), q
+ (*q
== '\b'), fmttime("%?%l", (time_t)num
));
932 else if (ep
->type
== 'v')
933 b
+= sfsprintf(b
, PATH_MAX
- (b
- buf
), q
+ (*q
== '\b'), fmtversion(num
));
935 b
+= sfsprintf(b
, PATH_MAX
- (b
- buf
), q
+ (*q
== '\b'), num
);
936 if (ep
->mime
&& *ep
->mime
)
941 if (!mp
->keep
[level
])
944 mp
->mime
= mp
->cap
[level
];
947 mp
->keep
[level
- 1] = mp
->keep
[level
];
960 if (mp
->keep
[level
] && b
> buf
)
969 * check english language stats
973 ckenglish(register Magic_t
* mp
, int pun
, int badpun
)
976 register int vowl
= 0;
977 register int freq
= 0;
978 register int rare
= 0;
980 if (5 * badpun
> pun
)
982 if (2 * mp
->count
[';'] > mp
->count
['E'] + mp
->count
['e'])
984 if ((mp
->count
['>'] + mp
->count
['<'] + mp
->count
['/']) > mp
->count
['E'] + mp
->count
['e'])
986 for (s
= "aeiou"; *s
; s
++)
987 vowl
+= mp
->count
[toupper(*s
)] + mp
->count
[*s
];
988 for (s
= "etaion"; *s
; s
++)
989 freq
+= mp
->count
[toupper(*s
)] + mp
->count
[*s
];
990 for (s
= "vjkqxz"; *s
; s
++)
991 rare
+= mp
->count
[toupper(*s
)] + mp
->count
[*s
];
992 return 5 * vowl
>= mp
->fbsz
- mp
->count
[' '] && freq
>= 10 * rare
;
996 * check programming language stats
1000 cklang(register Magic_t
* mp
, const char* file
, char* buf
, struct stat
* st
)
1003 register unsigned char* b
;
1004 register unsigned char* e
;
1020 b
= (unsigned char*)mp
->fbuf
;
1022 memzero(mp
->count
, sizeof(mp
->count
));
1023 memzero(mp
->multi
, sizeof(mp
->multi
));
1024 memzero(mp
->identifier
, sizeof(mp
->identifier
));
1027 * check character coding
1032 flags
|= mp
->cctype
[*b
++];
1033 b
= (unsigned char*)mp
->fbuf
;
1037 for (c
= 0; c
< CC_MAPS
; c
++)
1040 if ((flags
& CC_MASK
) < n
)
1042 n
= flags
& CC_MASK
;
1048 if (!(flags
& (CC_binary
|CC_notext
)))
1053 ccmaps(mp
->fbuf
, mp
->fbsz
, q
, CC_NATIVE
);
1055 if (b
[0] == '#' && b
[1] == '!')
1057 for (b
+= 2; b
< e
&& isspace(*b
); b
++);
1058 for (s
= (char*)b
; b
< e
&& isprint(*b
); b
++);
1061 if ((st
->st_mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)) || match(s
, "/*bin*/*") || !access(s
, F_OK
))
1063 if (t
= strrchr(s
, '/'))
1065 for (t
= s
; *t
; t
++)
1071 sfsprintf(mp
->mbuf
, sizeof(mp
->mbuf
), "application/x-%s", *s
? s
: "sh");
1072 mp
->mime
= mp
->mbuf
;
1073 if (match(s
, "*sh"))
1086 t1
= T("interpreter");
1090 sfsprintf(mp
->sbuf
, sizeof(mp
->sbuf
), T("%s%s script"), s
, t1
);
1095 b
= (unsigned char*)mp
->fbuf
;
1106 if (c
== q
&& (q
!= '*' || *b
== '/' && b
++))
1118 if (isalpha(c
) || c
== '_')
1123 else if (!isdigit(c
))
1135 if (((char*)b
- s
) == 3 && (s
== (mp
->fbuf
+ 1) || *(s
- 2) == '\n'))
1144 if (s
== t
&& *b
== '}')
1150 if (mp
->idtab
= dtnew(mp
->vm
, &mp
->dtdisc
, Dthash
))
1151 for (q
= 0; q
< elementsof(dict
); q
++)
1152 dtinsert(mp
->idtab
, &dict
[q
]);
1153 else if (mp
->disc
->errorf
)
1154 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 3, "out of space");
1160 if (ip
= (Info_t
*)dtmatch(mp
->idtab
, s
))
1161 mp
->identifier
[ip
->value
]++;
1169 if (b
== (unsigned char*)(mp
->fbuf
+ 1) || *(b
- 2) == '\n')
1183 if (*b
== '(' && *(b
+ 1) != ' ')
1197 if (*b
== ':' && isspace(*(b
+ 1)) && b
> (unsigned char*)(mp
->fbuf
+ 1) && isspace(*(b
- 2)))
1207 if (*b
!= ' ' && *b
!= '\n')
1218 base
= (t1
= strrchr(file
, '/')) ? t1
+ 1 : (char*)file
;
1219 suff
= (t1
= strrchr(base
, '.')) ? t1
+ 1 : "";
1222 if (match(suff
, "*sh|bat|cmd"))
1224 if (match(base
, "*@(mkfile)"))
1226 if (match(base
, "*@(makefile|.mk)"))
1228 if (match(base
, "*@(mamfile|.mam)"))
1230 if (match(suff
, "[cly]?(pp|xx|++)|cc|ll|yy"))
1232 if (match(suff
, "f"))
1234 if (match(suff
, "htm+(l)"))
1236 if (match(suff
, "cpy"))
1238 if (match(suff
, "cob|cbl|cb2"))
1240 if (match(suff
, "pl[1i]"))
1242 if (match(suff
, "tex"))
1244 if (match(suff
, "asm|s"))
1246 if ((st
->st_mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)) && (!suff
|| suff
!= strchr(suff
, '.')))
1249 s
= T("command script");
1250 mp
->mime
= "application/sh";
1253 if (strmatch(mp
->fbuf
, "From * [0-9][0-9]:[0-9][0-9]:[0-9][0-9] *"))
1255 s
= T("mail message");
1256 mp
->mime
= "message/rfc822";
1259 if (match(base
, "*@(mkfile)"))
1263 mp
->mime
= "application/mk";
1266 if (match(base
, "*@(makefile|.mk)") || mp
->multi
['\t'] >= mp
->count
[':'] && (mp
->multi
['$'] > 0 || mp
->multi
[':'] > 0))
1270 mp
->mime
= "application/make";
1273 if (mp
->multi
['.'] >= 3)
1275 s
= T("nroff input");
1276 mp
->mime
= "application/x-troff";
1279 if (mp
->multi
['X'] >= 3)
1282 mp
->mime
= "application/x-tex";
1285 if (mp
->fbsz
< SF_BUFSIZE
&&
1286 (mp
->multi
['('] == mp
->multi
[')'] &&
1287 mp
->multi
['{'] == mp
->multi
['}'] &&
1288 mp
->multi
['['] == mp
->multi
[']']) ||
1289 mp
->fbsz
>= SF_BUFSIZE
&&
1290 (mp
->multi
['('] >= mp
->multi
[')'] &&
1291 mp
->multi
['{'] >= mp
->multi
['}'] &&
1292 mp
->multi
['['] >= mp
->multi
[']']))
1294 c
= mp
->identifier
[ID_INCL1
];
1295 if (c
>= 2 && mp
->identifier
[ID_INCL2
] >= c
&& mp
->identifier
[ID_INCL3
] >= c
&& mp
->count
['.'] >= c
||
1296 mp
->identifier
[ID_C
] >= 5 && mp
->count
[';'] >= 5 ||
1297 mp
->count
['='] >= 20 && mp
->count
[';'] >= 20)
1307 mp
->mime
= "application/x-cc";
1312 mp
->mime
= "application/x-lex";
1316 if (mp
->identifier
[ID_YACC
] < 5 || mp
->count
['%'] < 5)
1318 mp
->mime
= "application/x-cc";
1325 mp
->mime
= "application/x-yacc";
1328 if (mp
->identifier
[ID_CPLUSPLUS
] >= 3)
1331 mp
->mime
= "application/x-c++";
1333 sfsprintf(mp
->sbuf
, sizeof(mp
->sbuf
), "%s%s%s", t1
, t2
, t3
);
1338 if (mp
->identifier
[ID_MAM1
] >= 2 && mp
->identifier
[ID_MAM3
] >= 2 &&
1339 (mp
->fbsz
< SF_BUFSIZE
&& mp
->identifier
[ID_MAM1
] == mp
->identifier
[ID_MAM2
] ||
1340 mp
->fbsz
>= SF_BUFSIZE
&& mp
->identifier
[ID_MAM1
] >= mp
->identifier
[ID_MAM2
]))
1343 s
= T("mam program");
1344 mp
->mime
= "application/x-mam";
1347 if (mp
->identifier
[ID_FORTRAN
] >= 8)
1350 s
= T("fortran program");
1351 mp
->mime
= "application/x-fortran";
1354 if (mp
->identifier
[ID_HTML
] > 0 && mp
->count
['<'] >= 8 && (c
= mp
->count
['<'] - mp
->count
['>']) >= -2 && c
<= 2)
1357 s
= T("html input");
1358 mp
->mime
= "text/html";
1361 if (mp
->identifier
[ID_COPYBOOK
] > 0 && mp
->identifier
[ID_COBOL
] == 0 && (c
= mp
->count
['('] - mp
->count
[')']) >= -2 && c
<= 2)
1364 s
= T("cobol copybook");
1365 mp
->mime
= "application/x-cobol";
1368 if (mp
->identifier
[ID_COBOL
] > 0 && mp
->identifier
[ID_COPYBOOK
] > 0 && (c
= mp
->count
['('] - mp
->count
[')']) >= -2 && c
<= 2)
1371 s
= T("cobol program");
1372 mp
->mime
= "application/x-cobol";
1375 if (mp
->identifier
[ID_PL1
] > 0 && (c
= mp
->count
['('] - mp
->count
[')']) >= -2 && c
<= 2)
1378 s
= T("pl1 program");
1379 mp
->mime
= "application/x-pl1";
1382 if (mp
->count
['{'] >= 6 && (c
= mp
->count
['{'] - mp
->count
['}']) >= -2 && c
<= 2 && mp
->count
['\\'] >= mp
->count
['{'])
1386 mp
->mime
= "text/tex";
1389 if (mp
->identifier
[ID_ASM
] >= 4)
1392 s
= T("as program");
1393 mp
->mime
= "application/x-as";
1396 if (ckenglish(mp
, pun
, badpun
))
1398 s
= T("english text");
1399 mp
->mime
= "text/plain";
1403 else if (streq(base
, "core"))
1405 mp
->mime
= "x-system/core";
1406 return T("core dump");
1408 if (flags
& (CC_binary
|CC_notext
))
1410 b
= (unsigned char*)mp
->fbuf
;
1425 if (b
< e
&& (*b
++ & 0xc0) != 0x80)
1428 if (b
< e
&& (*b
++ & 0xc0) != 0x80)
1431 if (b
< e
&& (*b
++ & 0xc0) != 0x80)
1439 flags
&= ~(CC_binary
|CC_notext
);
1449 if (flags
& (CC_binary
|CC_notext
))
1451 unsigned long d
= 0;
1453 if ((q
= mp
->fbsz
/ UCHAR_MAX
) >= 2)
1456 * compression/encryption via standard deviation
1460 for (c
= 0; c
< UCHAR_MAX
; c
++)
1462 pun
= mp
->count
[c
] - q
;
1474 s
= T("compressed");
1479 mp
->mime
= "application/octet-stream";
1482 mp
->mime
= "text/plain";
1483 if (flags
& CC_utf_8
)
1484 s
= (flags
& CC_control
) ? T("utf-8 text with control characters") : T("utf-8 text");
1485 else if (flags
& CC_latin
)
1486 s
= (flags
& CC_control
) ? T("latin text with control characters") : T("latin text");
1488 s
= (flags
& CC_control
) ? T("text with control characters") : T("text");
1490 if (!flags
&& mp
->count
['\n'] >= mp
->count
['\r'] && mp
->count
['\n'] <= (mp
->count
['\r'] + 1) && mp
->count
['\r'])
1493 mp
->mime
= "text/dos";
1499 if (code
== CC_ASCII
)
1500 sfsprintf(buf
, PATH_MAX
, "ascii %s%s", t
, s
);
1503 sfsprintf(buf
, PATH_MAX
, "ebcdic%d %s%s", code
- 1, t
, s
);
1504 mp
->mime
= "text/ebcdic";
1510 sfsprintf(buf
, PATH_MAX
, "%s%s", t
, s
);
1517 * return the basic magic string for file,st in buf,size
1521 type(register Magic_t
* mp
, const char* file
, struct stat
* st
, char* buf
, int size
)
1527 if (!S_ISREG(st
->st_mode
))
1529 if (S_ISDIR(st
->st_mode
))
1531 mp
->mime
= "x-system/dir";
1532 return T("directory");
1534 if (S_ISLNK(st
->st_mode
))
1536 mp
->mime
= "x-system/lnk";
1538 s
+= sfsprintf(s
, PATH_MAX
, T("symbolic link to "));
1539 if (pathgetlink(file
, s
, size
- (s
- buf
)) < 0)
1540 return T("cannot read symbolic link text");
1543 if (S_ISBLK(st
->st_mode
))
1545 mp
->mime
= "x-system/blk";
1546 sfsprintf(buf
, PATH_MAX
, T("block special (%s)"), fmtdev(st
));
1549 if (S_ISCHR(st
->st_mode
))
1551 mp
->mime
= "x-system/chr";
1552 sfsprintf(buf
, PATH_MAX
, T("character special (%s)"), fmtdev(st
));
1555 if (S_ISFIFO(st
->st_mode
))
1557 mp
->mime
= "x-system/fifo";
1561 if (S_ISSOCK(st
->st_mode
))
1563 mp
->mime
= "x-system/sock";
1568 if (!(mp
->fbmx
= st
->st_size
))
1571 s
= T("cannot read");
1574 mp
->fbsz
= sfread(mp
->fp
, mp
->fbuf
, sizeof(mp
->fbuf
) - 1);
1576 s
= fmterror(errno
);
1577 else if (mp
->fbsz
== 0)
1581 mp
->fbuf
[mp
->fbsz
] = 0;
1584 if (!(s
= ckmagic(mp
, file
, buf
, st
, 0)))
1585 s
= cklang(mp
, file
, buf
, st
);
1589 mp
->mime
= "application/unknown";
1590 else if ((t
= strchr(mp
->mime
, '%')) && *(t
+ 1) == 's' && !*(t
+ 2))
1598 me
= (m
= mp
->mime
= mp
->fbuf
) + sizeof(mp
->fbuf
) - 1;
1599 while (m
< me
&& b
< t
)
1604 if (!(be
= strchr(t
, ' ')))
1609 if (*(be
- 1) == ',' || strneq(be
+ 1, "data", 4) || strneq(be
+ 1, "file", 4))
1614 while (m
< me
&& b
< be
)
1615 if ((*m
++ = *b
++) == ' ')
1623 * low level for magicload()
1627 load(register Magic_t
* mp
, char* file
, register Sfio_t
* fp
)
1629 register Entry_t
* ep
;
1644 Entry_t
* fun
['z' - 'a' + 1];
1646 memzero(fun
, sizeof(fun
));
1652 error_info
.file
= file
;
1653 error_info
.line
= 0;
1654 first
= ep
= vmnewof(mp
->vm
, 0, Entry_t
, 1, 0);
1655 while (p
= sfgetr(fp
, '\n', 1))
1658 for (; isspace(*p
); p
++);
1671 if (++lev
< MAXNEST
)
1673 else if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
1674 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "{ ... } operator nesting too deep -- %d max", MAXNEST
);
1677 if (!last
|| lev
<= 0)
1679 if (mp
->disc
->errorf
)
1680 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "`%c': invalid nesting", *p
);
1682 else if (lev
-- == ent
)
1686 ep
->offset
= ret
->offset
;
1690 ep
->desc
= "[RETURN]";
1692 ep
= ret
->next
= vmnewof(mp
->vm
, 0, Entry_t
, 1, 0);
1699 if (*(p
+ 1) == '{' || *(p
+ 1) == '(' && *p
!= '+' && *p
!= '>' && *p
!= '&' && *p
!= '|')
1702 if (n
>= 'a' && n
<= 'z')
1706 if (mp
->disc
->errorf
)
1707 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: invalid function name", n
);
1710 if (ret
&& mp
->disc
->errorf
)
1711 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: function has no return", ret
->offset
+ 'a');
1716 ep
->desc
= "[FUNCTION]";
1720 if (*(p
+ 1) != ')' && mp
->disc
->errorf
)
1721 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: invalid function call argument list", n
+ 'a');
1722 ep
->desc
= "[CALL]";
1730 ep
= ep
->next
= vmnewof(mp
->vm
, 0, Entry_t
, 1, 0);
1732 fun
[n
] = last
->value
.lab
= ep
;
1733 else if (!(last
->value
.lab
= fun
[n
]) && mp
->disc
->errorf
)
1734 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: function not defined", n
+ 'a');
1738 ep
->nest
= (lev
> 0 && lev
!= ent
) ? ('0' + lev
- !!ent
) : ' ';
1754 * old style nesting push
1763 if (last
->cont
== '>')
1775 if ((mp
->flags
& MAGIC_VERBOSE
) && !isalpha(*p
) && mp
->disc
->errorf
)
1776 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "`%c': invalid line continuation operator", *p
);
1779 case '0': case '1': case '2': case '3': case '4':
1780 case '5': case '6': case '7': case '8': case '9':
1781 ep
->cont
= (lev
> 0) ? '&' : '#';
1791 * old style nesting pop
1798 if (ep
->cont
== '&')
1812 ep
->offset
= strton(p
, &next
, NiL
, 0);
1817 for (p2
= p
; *p2
&& !isspace(*p2
); p2
++);
1820 if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
1821 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "not enough fields: `%s'", p
);
1830 ep
->expr
= vmstrdup(mp
->vm
, p
);
1832 ep
->offset
= (ip
= (Info_t
*)dtmatch(mp
->infotab
, p
)) ? ip
->value
: 0;
1833 else if (*p
== '(' && ep
->cont
== '>')
1836 * convert old style indirection to @
1862 for (; isspace(*p2
); p2
++);
1863 for (p
= p2
; *p2
&& !isspace(*p2
); p2
++);
1866 if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
1867 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "not enough fields: `%s'", p
);
1876 if ((*p
== 'b' || *p
== 'l') && *(p
+ 1) == 'e')
1878 ep
->swap
= ~(*p
== 'l' ? 7 : 0);
1883 if (*(p
+ 1) == 'h')
1892 if (p
= strchr(p
, '&'))
1898 ep
->mask
= strton(++p
, NiL
, NiL
, 0);
1900 for (; isspace(*p2
); p2
++);
1905 * comparison operation
1909 if (p2
= strchr(p
, '\t'))
1917 * assume balanced {}[]()\\""'' field
1947 if (qe
== n
&& qn
> 0)
1962 if (!qe
&& isspace(n
))
1974 if (ep
->type
== 'e' || ep
->type
== 'm' || ep
->type
== 's')
1980 ep
->mask
= strton(++p
, &next
, NiL
, 0);
2017 ep
->value
.num
= ep
->mask
;
2021 if (ep
->op
!= '*' && !ep
->value
.num
)
2023 if (ep
->type
== 'e')
2025 if (ep
->value
.sub
= vmnewof(mp
->vm
, 0, regex_t
, 1, 0))
2027 ep
->value
.sub
->re_disc
= &mp
->redisc
;
2028 if (!(n
= regcomp(ep
->value
.sub
, p
, REG_DELIMITED
|REG_LENIENT
|REG_NULL
|REG_DISCIPLINE
)))
2030 p
+= ep
->value
.sub
->re_npat
;
2031 if (!(n
= regsubcomp(ep
->value
.sub
, p
, NiL
, 0, 0)))
2032 p
+= ep
->value
.sub
->re_npat
;
2036 regmessage(mp
, ep
->value
.sub
, n
);
2039 else if (*p
&& mp
->disc
->errorf
)
2040 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "invalid characters after substitution: %s", p
);
2043 else if (ep
->type
== 'm')
2045 ep
->mask
= stresc(p
) + 1;
2046 ep
->value
.str
= vmnewof(mp
->vm
, 0, char, ep
->mask
+ 1, 0);
2047 memcpy(ep
->value
.str
, p
, ep
->mask
);
2048 if ((!ep
->expr
|| !ep
->offset
) && !strmatch(ep
->value
.str
, "\\!\\(*\\)"))
2049 ep
->value
.str
[ep
->mask
- 1] = '*';
2051 else if (ep
->type
== 's')
2053 ep
->mask
= stresc(p
);
2054 ep
->value
.str
= vmnewof(mp
->vm
, 0, char, ep
->mask
, 0);
2055 memcpy(ep
->value
.str
, p
, ep
->mask
);
2057 else if (*p
== '\'')
2060 ep
->value
.num
= *(unsigned char*)(p
+ 1) + lge
;
2062 else if (strmatch(p
, "+([a-z])\\(*\\)"))
2069 while (*p
&& *p
++ != '(');
2074 if (n
< 'a' || n
> 'z')
2076 if (mp
->disc
->errorf
)
2077 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: invalid function name", n
);
2079 else if (!fun
[n
-= 'a'])
2081 if (mp
->disc
->errorf
)
2082 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: function not defined", n
+ 'a');
2086 ep
->value
.loop
= vmnewof(mp
->vm
, 0, Loop_t
, 1, 0);
2087 ep
->value
.loop
->lab
= fun
[n
];
2088 while (*p
&& *p
++ != ',');
2089 ep
->value
.loop
->start
= strton(p
, &t
, NiL
, 0);
2090 while (*t
&& *t
++ != ',');
2091 ep
->value
.loop
->size
= strton(t
, &t
, NiL
, 0);
2096 ep
->desc
= vmnewof(mp
->vm
, 0, char, 32, 0);
2097 ep
->mime
= vmnewof(mp
->vm
, 0, char, 32, 0);
2102 if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
2103 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "%-.*s: unknown function", p
- t
, t
);
2109 ep
->value
.num
= strton(p
, NiL
, NiL
, 0) + lge
;
2111 ep
->value
.num
= swapget(0, (char*)&ep
->value
.num
, sizeof(ep
->value
.num
));
2121 for (; isspace(*p2
); p2
++);
2122 if (p
= strchr(p2
, '\t'))
2125 * check for message catalog index
2131 for (p3
= p2
; isalnum(*p3
); p3
++);
2134 for (; isdigit(*p3
); p3
++);
2137 for (p2
= p
; isspace(*p2
); p2
++);
2138 if (p
= strchr(p2
, '\t'))
2145 ep
->desc
= vmstrdup(mp
->vm
, p2
);
2148 for (; isspace(*p
); p
++);
2150 ep
->mime
= vmstrdup(mp
->vm
, p
);
2161 ep
= ep
->next
= vmnewof(mp
->vm
, 0, Entry_t
, 1, 0);
2167 mp
->magiclast
->next
= first
;
2170 mp
->magiclast
= last
;
2173 if ((mp
->flags
& MAGIC_VERBOSE
) && mp
->disc
->errorf
)
2176 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "too many } operators");
2178 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 1, "not enough } operators");
2180 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "%c: function has no return", ret
->offset
+ 'a');
2182 error_info
.file
= 0;
2183 error_info
.line
= 0;
2188 * load a magic file into mp
2192 magicload(register Magic_t
* mp
, const char* file
, unsigned long flags
)
2202 mp
->flags
= mp
->disc
->flags
| flags
;
2204 if (list
= !(s
= (char*)file
) || !*s
|| (*s
== '-' || *s
== '.') && !*(s
+ 1))
2206 if (!(s
= getenv(MAGIC_FILE_ENV
)) || !*s
)
2213 else if (e
= strchr(s
, ':'))
2216 * ok, so ~ won't work for the last list element
2217 * we do it for MAGIC_FILES_ENV anyway
2220 if ((strneq(s
, "~/", n
= 2) || strneq(s
, "$HOME/", n
= 6) || strneq(s
, "${HOME}/", n
= 8)) && (t
= getenv("HOME")))
2222 sfputr(mp
->tmp
, t
, -1);
2225 sfwrite(mp
->tmp
, s
, e
- s
);
2226 if (!(s
= sfstruse(mp
->tmp
)))
2229 if (!*s
|| streq(s
, "-"))
2231 if (!(fp
= sfopen(NiL
, s
, "r")))
2235 if (!(t
= pathpath(mp
->fbuf
, s
, "", PATH_REGULAR
|PATH_READ
)) && !strchr(s
, '/'))
2237 strcpy(mp
->fbuf
, s
);
2238 sfprintf(mp
->tmp
, "%s/%s", MAGIC_DIR
, mp
->fbuf
);
2239 if (!(s
= sfstruse(mp
->tmp
)))
2241 if (!(t
= pathpath(mp
->fbuf
, s
, "", PATH_REGULAR
|PATH_READ
)))
2244 if (!(fp
= sfopen(NiL
, t
, "r")))
2249 if (mp
->disc
->errorf
)
2250 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 3, "%s: cannot open magic file", s
);
2255 n
= load(mp
, s
, fp
);
2266 if (mp
->flags
& MAGIC_VERBOSE
)
2268 if (mp
->disc
->errorf
)
2269 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 2, "cannot find magic file");
2275 if (mp
->disc
->errorf
)
2276 (*mp
->disc
->errorf
)(mp
, mp
->disc
, 3, "out of space");
2281 * open a magic session
2285 magicopen(Magicdisc_t
* disc
)
2287 register Magic_t
* mp
;
2292 register Vmalloc_t
* vm
;
2293 unsigned char* map
[CC_MAPS
+ 1];
2295 if (!(vm
= vmopen(Vmdcheap
, Vmbest
, 0)))
2297 if (!(mp
= vmnewof(vm
, 0, Magic_t
, 1, 0)))
2305 mp
->flags
= disc
->flags
;
2306 mp
->redisc
.re_version
= REG_VERSION
;
2307 mp
->redisc
.re_flags
= REG_NOFREE
;
2308 mp
->redisc
.re_errorf
= (regerror_t
)disc
->errorf
;
2309 mp
->redisc
.re_resizef
= (regresize_t
)vmgetmem
;
2310 mp
->redisc
.re_resizehandle
= (void*)mp
->vm
;
2311 mp
->dtdisc
.key
= offsetof(Info_t
, name
);
2312 mp
->dtdisc
.link
= offsetof(Info_t
, link
);
2313 if (!(mp
->tmp
= sfstropen()) || !(mp
->infotab
= dtnew(mp
->vm
, &mp
->dtdisc
, Dthash
)))
2315 for (n
= 0; n
< elementsof(info
); n
++)
2316 dtinsert(mp
->infotab
, &info
[n
]);
2317 for (i
= 0; i
< CC_MAPS
; i
++)
2318 map
[i
] = ccmap(i
, CC_ASCII
);
2319 mp
->x2n
= ccmap(CC_ALIEN
, CC_NATIVE
);
2320 for (n
= 0; n
<= UCHAR_MAX
; n
++)
2326 c
= ccmapchr(map
[i
], n
);
2327 f
= (f
<< CC_BIT
) | CCTYPE(c
);
2338 * close a magicopen() session
2342 magicclose(register Magic_t
* mp
)
2347 sfstrclose(mp
->tmp
);
2354 * return the magic string for file with optional stat info st
2358 magictype(register Magic_t
* mp
, Sfio_t
* fp
, const char* file
, register struct stat
* st
)
2363 mp
->flags
= mp
->disc
->flags
;
2366 s
= T("cannot stat");
2370 off
= sfseek(mp
->fp
, (off_t
)0, SEEK_CUR
);
2371 s
= type(mp
, file
, st
, mp
->tbuf
, sizeof(mp
->tbuf
));
2373 sfseek(mp
->fp
, off
, SEEK_SET
);
2374 if (!(mp
->flags
& MAGIC_MIME
))
2376 if (S_ISREG(st
->st_mode
) && (st
->st_size
> 0) && (st
->st_size
< 128))
2377 sfprintf(mp
->tmp
, "%s ", T("short"));
2378 sfprintf(mp
->tmp
, "%s", s
);
2379 if (!mp
->fp
&& (st
->st_mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)))
2380 sfprintf(mp
->tmp
, ", %s", S_ISDIR(st
->st_mode
) ? T("searchable") : T("executable"));
2381 if (st
->st_mode
& S_ISUID
)
2382 sfprintf(mp
->tmp
, ", setuid=%s", fmtuid(st
->st_uid
));
2383 if (st
->st_mode
& S_ISGID
)
2384 sfprintf(mp
->tmp
, ", setgid=%s", fmtgid(st
->st_gid
));
2385 if (st
->st_mode
& S_ISVTX
)
2386 sfprintf(mp
->tmp
, ", sticky");
2387 if (!(s
= sfstruse(mp
->tmp
)))
2388 s
= T("out of space");
2391 if (mp
->flags
& MAGIC_MIME
)
2399 * list the magic table in mp on sp
2403 magiclist(register Magic_t
* mp
, register Sfio_t
* sp
)
2405 register Entry_t
* ep
= mp
->magic
;
2406 register Entry_t
* rp
= 0;
2408 mp
->flags
= mp
->disc
->flags
;
2409 sfprintf(sp
, "cont\toffset\ttype\top\tmask\tvalue\tmime\tdesc\n");
2412 sfprintf(sp
, "%c %c\t", ep
->cont
, ep
->nest
);
2414 sfprintf(sp
, "%s", ep
->expr
);
2416 sfprintf(sp
, "%ld", ep
->offset
);
2417 sfprintf(sp
, "\t%s%c\t%c\t%lo\t", ep
->swap
== (char)~3 ? "L" : ep
->swap
== (char)~0 ? "B" : "", ep
->type
, ep
->op
, ep
->mask
);
2422 sfputr(sp
, fmtesc(ep
->value
.str
), -1);
2428 sfprintf(sp
, "loop(%d,%d,%d,%d)", ep
->value
.loop
->start
, ep
->value
.loop
->size
, ep
->value
.loop
->count
, ep
->value
.loop
->offset
);
2431 sfprintf(sp
, "vcodex()");
2434 sfprintf(sp
, "%p", ep
->value
.str
);
2439 sfprintf(sp
, "%lo", ep
->value
.num
);
2442 sfprintf(sp
, "\t%s\t%s\n", ep
->mime
? ep
->mime
: "", fmtesc(ep
->desc
));
2443 if (ep
->cont
== '$' && !ep
->value
.lab
->mask
)
2450 if (ep
->cont
== ':')
2453 ep
->value
.lab
->mask
= 1;