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