upx/src/stub/scripts/gpp_inc.py

193 lines
5.8 KiB
Python
Raw Normal View History

2020-05-24 02:07:23 +08:00
#! /usr/bin/env python2
## vim:set ts=4 sw=4 et: -*- coding: utf-8 -*-
#
# gpp_inc.py -- Generic PreProcessor: include
#
# This file is part of the UPX executable compressor.
#
2024-01-04 00:47:25 +08:00
# Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer
# All Rights Reserved.
#
# UPX and the UCL library are free software; you can redistribute them
# and/or modify them under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.
# If not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Markus F.X.J. Oberhumer Laszlo Molnar
2016-09-28 18:25:01 +08:00
# <markus@oberhumer.com> <ezerotven+github@gmail.com>
#
import getopt, os, re, sys
class opts:
dry_run = 0
verbose = 0
fatal = 1
includes = []
mode = "c"
target_mf = None
target_mmd = None
files_md = []
files_mmd = []
files_st = {}
def add_dep(state, fn, mode):
if mode:
files = files_md
else:
files = files_mmd
fn = os.path.normpath(fn)
fn = os.path.normcase(fn)
if fn in files:
return
# FIXME: could use os.path.samestat() etc.
files.append(fn)
files_st[fn] = os.stat(fn)
def not_found(state, l, inc, fatal=None):
if fatal is None:
fatal = opts.fatal
if fatal:
2024-07-23 18:24:09 +08:00
raise Exception("%s:%d: include file %s not found" % (state[0], state[2], inc))
return l
def parse_comment(state, l, comment):
cf = {}
if not comment: return cf
m = re.search(r"gpp_inc:(.+?):", comment.strip())
if not m: return cf
flags = re.split(r"\s+", m.group(1).strip())
for f in flags:
assert f, (f, flags, m.groups(), comment)
if f == "ignore=0": cf["fatal"] = 1
elif f == "ignore=1": cf["fatal"] = 0
else:
2024-07-23 18:24:09 +08:00
raise Exception("%s:%d: bad flags %s %s" % (state[0], state[2], f, str(flags)))
return cf
def handle_inc_c(state, l, ofp):
m = re.search(r"^\s*\#\s*include\s+([\"\<])(.+?)([\"\>])(.*)$", l)
if not m:
return l
q1, inc, q2, comment = m.groups()
cf = parse_comment(state, l, comment)
if q1 == '<' and q2 == '>':
dirs = opts.includes
elif q1 == '"' and q2 == '"':
dirs = [state[1]] + opts.includes
else:
2024-07-23 18:24:09 +08:00
raise Exception("syntax error: include line " + l)
for dir in dirs:
fn = os.path.join(dir, inc)
if os.path.isfile(fn):
add_dep(state, fn, q1 == '<')
handle_file(fn, ofp, state)
return None
return not_found(state, l, inc, cf.get("fatal"))
def handle_inc_nasm(state, l, ofp):
m = re.search(r"^\s*\%\s*include\s+([\"\<])(.+?)([\"\>])(.*)$", l)
if not m:
return l
q1, inc, q2, comment = m.groups()
cf = parse_comment(state, l, comment)
if q1 == '<' and q2 == '>':
pass
elif q1 == '"' and q2 == '"':
pass
else:
2024-07-23 18:24:09 +08:00
raise Exception("syntax error: include line " + l)
# info: nasm simply does concat the includes
for prefix in opts.includes + [""]:
fn = prefix + inc
if os.path.isfile(fn):
add_dep(state, fn, False)
handle_file(fn, ofp, state)
return None
return not_found(state, l, inc, cf.get("fatal"))
def handle_file(ifn, ofp, parent_state=None):
state = [ifn, os.path.dirname(ifn) or ".", 0, parent_state]
ifp = open(ifn, "rb")
for l in ifp.readlines():
state[2] += 1 # line counter
l = l.rstrip("\n")
if opts.mode == "c":
l = handle_inc_c(state, l, ofp)
elif opts.mode == "nasm":
l = handle_inc_nasm(state, l, ofp)
if l is not None:
ofp.write(l + "\n")
def main(argv):
2016-09-22 20:27:57 +08:00
try: assert 0
except AssertionError: pass
else: raise Exception("fatal error - assertions not enabled")
ofile = None
shortopts, longopts = "qvI:o:", ["dry-run", "MF=", "MMD=", "mode=", "quiet", "verbose"]
xopts, args = getopt.gnu_getopt(argv[1:], shortopts, longopts)
for opt, optarg in xopts:
if 0: pass
elif opt in ["-q", "--quiet"]: opts.verbose = opts.verbose - 1
elif opt in ["-v", "--verbose"]: opts.verbose = opts.verbose + 1
elif opt in ["--dry-run"]: opts.dry_run = opts.dry_run + 1
elif opt in ["-I"]: opts.includes.append(optarg)
elif opt in ["-o"]: ofile = optarg
elif opt in ["--mode"]: opts.mode = optarg.lower()
elif opt in ["--MF"]: opts.target_mf = optarg
elif opt in ["--MMD"]: opts.target_mmd = optarg
else: assert 0, ("getopt problem:", opt, optarg, xopts, args)
if ofile is None:
assert len(args) == 2
ifile = args[0]
ofile = args[1]
else:
assert len(args) == 1
ifile = args[0]
assert os.path.isfile(ifile)
ofp = open(ofile, "wb")
handle_file(ifile, ofp)
ofp.close()
if opts.target_mmd:
fn = ofile + ".d"
if opts.target_mf:
fn = opts.target_mf
if os.path.isfile(fn):
os.unlink(fn)
if files_mmd:
fp = open(fn, "wb")
fp.write("%s : \\\n" % opts.target_mmd)
for i, f in enumerate(files_mmd):
if i < len(files_mmd) - 1:
fp.write(" %s \\\n" % f)
else:
fp.write(" %s\n" % f)
fp.close()
if __name__ == "__main__":
sys.exit(main(sys.argv))