Commit | Line | Data |
---|---|---|
a4a9692d | 1 | /* IS_EXEC.C |
95fe4534 GM |
2 | * |
3 | * Copyright (C) 1995 DJ Delorie | |
4 | * Copyright (C) 1994 Eli Zaretskii <eliz@is.elta.co.il> | |
5 | * | |
6 | * (See the README file in this directory for the copyright and license | |
7 | * history of this file.) | |
8 | * | |
ff0cdfb9 | 9 | * This file is free software: you can redistribute it and/or modify |
95fe4534 | 10 | * it under the terms of the GNU General Public License as published by |
ff0cdfb9 GM |
11 | * the Free Software Foundation, either version 3 of the License, or |
12 | * (at your option) any later version. | |
95fe4534 GM |
13 | * |
14 | * This file is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
ff0cdfb9 | 20 | * along with this file. If not, see <http://www.gnu.org/licenses/>. |
95fe4534 GM |
21 | * |
22 | * Commentary: | |
a4a9692d DL |
23 | * |
24 | * Given a filename or a file handle, and the extension of the file, | |
25 | * determine if the file is executable. | |
26 | * First, the file extension is checked in case it uniquely identifies | |
27 | * the file as either an executable or not. Failing this, the first | |
28 | * two bytes of the file are tested for known signatures of executable | |
29 | * files. | |
30 | * | |
a4a9692d DL |
31 | */ |
32 | ||
33 | #include <libc/stubs.h> | |
34 | #include <stdio.h> | |
35 | #include <string.h> | |
36 | #include <ctype.h> | |
37 | #include <errno.h> | |
38 | #include <dpmi.h> | |
39 | #include <go32.h> | |
40 | #include <io.h> | |
41 | #include <libc/farptrgs.h> | |
42 | #include <libc/dosio.h> | |
43 | ||
44 | extern unsigned short _djstat_flags; | |
45 | unsigned short _get_magic(const char *, int); | |
46 | int _is_executable(const char *, int, const char *); | |
47 | ||
48 | /* | |
49 | * Read a MAGIC NUMBER from a given file. These are the first | |
50 | * two bytes of the file, if we look at them as an unsigned short. */ | |
51 | ||
52 | #define _STAT_EXEC_EXT 2 /* get execute bits from file extension? */ | |
53 | #define _STAT_EXEC_MAGIC 4 /* get execute bits from magic signature? */ | |
54 | ||
55 | unsigned short | |
56 | _get_magic(const char *s, int fh) | |
57 | { | |
58 | __dpmi_regs regs; | |
59 | unsigned short retval; | |
60 | unsigned short fpos_high = 0, fpos_low = 0; | |
61 | int read_fail = 0; | |
62 | ||
63 | /* If given a pathname, open the file. */ | |
64 | if (s) | |
65 | { | |
66 | int handle; | |
67 | if((handle = _open(s,0)) == -1) | |
68 | return 0; | |
69 | regs.x.bx = handle; | |
70 | } | |
71 | /* Else file already open. Remember its current file position | |
72 | and move to beginning of file. */ | |
73 | else | |
74 | { | |
75 | regs.x.ax = 0x4201; /* set pointer from current position */ | |
76 | regs.x.bx = fh; | |
77 | regs.x.cx = regs.x.dx = 0; /* move 0 bytes (i.e., stay put) */ | |
78 | __dpmi_int(0x21, ®s); | |
79 | if (regs.x.flags & 1) | |
80 | { | |
81 | errno = __doserr_to_errno(regs.x.ax); | |
82 | return 0; | |
83 | } | |
84 | fpos_high = regs.x.dx; /* got current position */ | |
85 | fpos_low = regs.x.ax; | |
86 | ||
87 | regs.x.ax = 0x4200; /* set pointer from the beginning of file */ | |
88 | regs.x.cx = regs.x.dx = 0; /* move to beginning of file */ | |
89 | __dpmi_int(0x21, ®s); | |
90 | if (regs.x.flags & 1) | |
91 | { | |
92 | errno = __doserr_to_errno(regs.x.ax); | |
93 | return 0; | |
94 | } | |
95 | } | |
96 | regs.x.ds = __tb_segment; | |
97 | regs.x.dx = __tb_offset; | |
98 | ||
99 | /* Read 2 bytes from the file. */ | |
100 | regs.x.ax = 0x3f00; | |
101 | regs.x.cx = 2; | |
102 | __dpmi_int(0x21, ®s); | |
103 | ||
104 | /* We can either (1) succeed, (2) read less than 2 bytes, | |
105 | or (3) fail to read at all. */ | |
106 | if (regs.x.ax != 2) | |
107 | read_fail = (regs.x.flags & 1) ? regs.x.ax : -1; | |
108 | ||
109 | /* If called with filename, close the file. */ | |
110 | if (s) | |
111 | { | |
112 | regs.x.ax = 0x3e00; | |
113 | __dpmi_int(0x21, ®s); | |
114 | if (regs.x.flags & 1) | |
115 | errno = __doserr_to_errno(regs.x.ax); | |
116 | } | |
117 | /* Else leave file pointer where we found it. */ | |
118 | else | |
119 | { | |
120 | regs.x.ax = 0x4200; /* set pointer from the beginning of file */ | |
121 | regs.x.bx = fh; | |
122 | regs.x.cx = fpos_high; | |
123 | regs.x.dx = fpos_low; | |
124 | __dpmi_int(0x21, ®s); | |
125 | if (regs.x.flags & 1) | |
126 | { | |
127 | errno = __doserr_to_errno(regs.x.ax); | |
128 | return 0; | |
129 | } | |
130 | } | |
131 | ||
132 | if (read_fail == 0) | |
133 | retval = _farpeekw(_dos_ds, __tb); | |
134 | else | |
135 | { | |
136 | /* The file couldn't be read: assume non-executable. If the file | |
137 | *is* executable, but was passed as a file-handle, and the user | |
138 | opened it in write-only mode, they lose... */ | |
139 | retval = 0; | |
140 | if (read_fail != -1) | |
141 | errno = __doserr_to_errno(read_fail); | |
142 | } | |
143 | ||
144 | return retval; | |
145 | } | |
146 | ||
147 | /* A list of extensions which designate executable files. These | |
148 | are NOT tested for the magic number. */ | |
149 | static char executables[] = "|EXE|COM|BAT|BTM|DLL|VXD|"; | |
150 | ||
151 | /* A list of extensions which belong to files known to NEVER be | |
152 | executables. These exist to minimize read()'ing files while | |
153 | detecting executables by magic number. You are welcome to | |
154 | add to this list, but remember: only extensions which could | |
155 | NEVER be present in executables should go here. */ | |
156 | static char non_executables[] = "\ | |
157 | |A|A01|A02|A03|A04|A05|ADL|ARC|ARJ|ASC|ASM|AUX|AWK\ | |
158 | |BAS|BIB|BGI|BMP\ | |
159 | |C|CC|CFG|CGZ|CH3|CHR|CI|CLP|CMF|CPI|CPP|CXX\ | |
160 | |DAT|DBF|DIZ|DOC|DVI\ | |
161 | |E|EL|ELC\ | |
162 | |F77|FN3\ | |
163 | |GIF|GZ\ | |
164 | |H|HLP|HPP|HXX\ | |
165 | |ICO|IN|INC|INF|INI\ | |
166 | |JPG\ | |
167 | |L|LEX|LF|LIB|LOG|LST|LZH\ | |
168 | |M|MAK|MAP|MF|MID|MPG\ | |
169 | |O|OBJ\ | |
170 | |PAK|PAS|PBM|PCD|PCX|PDS|PIC|PIF|PN3|PRJ|PS\ | |
171 | |RAS|RGB|RLE\ | |
172 | |S|SND|SY3\ | |
173 | |TAR|TAZ|TEX|TGA|TGZ|TIF|TXH|TXI|TXT\ | |
174 | |VOC\ | |
175 | |WAV|WK1|WK3|WKB|WQ1|WQ3|WQ4|WQ5|WQ6|WQ!\ | |
176 | |XBM\ | |
177 | |Y\ | |
178 | |ZIP|ZOO|"; | |
179 | ||
180 | int | |
181 | _is_executable(const char *filename, int fhandle, const char *extension) | |
182 | { | |
183 | if (!extension && filename) | |
184 | { | |
185 | const char *cp, *ep=0; | |
186 | for (cp=filename; *cp; cp++) | |
187 | { | |
188 | if (*cp == '.') | |
189 | ep = cp; | |
190 | if (*cp == '/' || *cp == '\\' || *cp == ':') | |
191 | ep = 0; | |
192 | } | |
193 | extension = ep; | |
194 | } | |
195 | if ((_djstat_flags & _STAT_EXEC_EXT) == 0 | |
196 | && extension | |
197 | && *extension | |
198 | && strlen(extension) <= ((extension[0]=='.') ? 4 : 3)) | |
199 | { | |
200 | /* Search the list of extensions in executables[]. */ | |
201 | char tmp_buf[6], *tp = tmp_buf; | |
202 | ||
203 | *tp++ = '|'; | |
204 | if (*extension == '.') | |
205 | extension++; | |
206 | while (*extension) | |
207 | *tp++ = toupper (*extension++); | |
208 | *tp++ = '|'; | |
209 | *tp = '\0'; | |
210 | if (strstr(non_executables, tmp_buf)) | |
211 | return 0; | |
212 | else if (strstr(executables, tmp_buf)) | |
213 | return 1; | |
214 | } | |
215 | ||
216 | /* No extension, or extension doesn't define execute | |
217 | bits unambiguously. We are in for some dirty work. | |
218 | Read the first two bytes of the file and see if they | |
219 | are any of the known magic numbers which designate | |
220 | executable files. | |
221 | Unix-like shells, which have executable shell scripts | |
222 | without extensions and DON'T have "#!" as their FIRST | |
223 | TWO CHARACTERS, lose here. Sorry, folks. */ | |
224 | if ( (_djstat_flags & _STAT_EXEC_MAGIC) == 0 ) | |
225 | { | |
226 | switch (_get_magic(filename, fhandle)) | |
227 | { | |
228 | case 0x5a4d: /* "MZ" */ | |
229 | case 0x010b: | |
230 | case 0x014c: | |
231 | case 0x2123: /* "#!" */ | |
232 | return 1; | |
233 | } | |
234 | } | |
235 | ||
236 | return 0; | |
237 | } | |
ab5796a9 | 238 |