浏览代码

Merge branch 'release/1.10.x'

rdb 7 年之前
父节点
当前提交
fc90ca7fdf

+ 2 - 1
direct/src/dist/commands.py

@@ -1347,7 +1347,8 @@ class bdist_apps(setuptools.Command):
 
 
         platforms = build_cmd.platforms
         platforms = build_cmd.platforms
         build_base = os.path.abspath(build_cmd.build_base)
         build_base = os.path.abspath(build_cmd.build_base)
-        os.makedirs(self.dist_dir, exist_ok=True)
+        if not os.path.exists(self.dist_dir):
+            os.makedirs(self.dist_dir)
         os.chdir(self.dist_dir)
         os.chdir(self.dist_dir)
 
 
         for platform in platforms:
         for platform in platforms:

+ 3 - 0
dtool/src/dtoolutil/load_dso.cxx

@@ -132,6 +132,9 @@ void *
 load_dso(const DSearchPath &path, const Filename &filename) {
 load_dso(const DSearchPath &path, const Filename &filename) {
   Filename abspath = resolve_dso(path, filename);
   Filename abspath = resolve_dso(path, filename);
   if (!abspath.is_regular_file()) {
   if (!abspath.is_regular_file()) {
+    // Make sure the error flag is cleared, to prevent a subsequent call to
+    // load_dso_error() from returning a previously stored error.
+    dlerror();
     return nullptr;
     return nullptr;
   }
   }
   string os_specific = abspath.to_os_specific();
   string os_specific = abspath.to_os_specific();

+ 6 - 1
dtool/src/prc/configPageManager.cxx

@@ -129,7 +129,12 @@ reload_implicit_pages() {
 #else
 #else
   const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo");
   const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo");
 #endif
 #endif
-  if (blobinfo != nullptr && (blobinfo->version == 0 || blobinfo->num_pointers < 10)) {
+  if (blobinfo == nullptr) {
+#ifndef _MSC_VER
+    // Clear the error flag.
+    dlerror();
+#endif
+  } else if (blobinfo->version == 0 || blobinfo->num_pointers < 10) {
     blobinfo = nullptr;
     blobinfo = nullptr;
   }
   }
 
 

+ 33 - 15
makepanda/makepackage.py

@@ -5,6 +5,9 @@ from installpanda import *
 import sys
 import sys
 import os
 import os
 import shutil
 import shutil
+import glob
+import re
+import subprocess
 
 
 
 
 INSTALLER_DEB_FILE = """
 INSTALLER_DEB_FILE = """
@@ -835,7 +838,8 @@ def MakeInstallerAndroid(version, **kwargs):
     # off any suffix (eg. libfile.so.1.0), as Android does not support them.
     # off any suffix (eg. libfile.so.1.0), as Android does not support them.
     source_dir = os.path.join(outputdir, "lib")
     source_dir = os.path.join(outputdir, "lib")
     target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
     target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
-    oscmd("mkdir -p %s" % (target_dir))
+    if not os.path.exists(target_dir):
+        os.makedirs(target_dir, mode=0o755)
 
 
     # Determine the library directories we should look in.
     # Determine the library directories we should look in.
     libpath = [source_dir]
     libpath = [source_dir]
@@ -857,19 +861,26 @@ def MakeInstallerAndroid(version, **kwargs):
             # Already processed.
             # Already processed.
             return
             return
 
 
-        oscmd("cp %s %s" % (source, target))
+        shutil.copy(source, target)
 
 
         # Walk through the library dependencies.
         # Walk through the library dependencies.
-        oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, outputdir), True)
-        for line in open(outputdir + "/tmp/otool-libs.txt", "r"):
-            line = line.strip()
-            if not line:
+        handle = subprocess.Popen(['readelf', '--dynamic', target], stdout=subprocess.PIPE)
+        for line in handle.communicate()[0].splitlines():
+            # The line will look something like:
+            # 0x0000000000000001 (NEEDED)             Shared library: [libpanda.so]
+            line = line.decode('utf-8', 'replace').strip()
+            if not line or '(NEEDED)' not in line or '[' not in line or ']' not in line:
                 continue
                 continue
-            if '.so.' in line:
-                dep = line.rpartition('.so.')[0] + '.so'
-                oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True)
-            else:
-                dep = line
+
+            # Extract the part between square brackets.
+            idx = line.index('[')
+            dep = line[idx + 1 : line.index(']', idx)]
+
+            # Change .so.1.2 suffix to .so, as needed for loading in .apk
+            if '.so.' in dep:
+                orig_dep = dep
+                dep = dep.rpartition('.so.')[0] + '.so'
+                oscmd("patchelf --replace-needed %s %s %s" % (orig_dep, dep, target), True)
 
 
             # Find it on the LD_LIBRARY_PATH.
             # Find it on the LD_LIBRARY_PATH.
             for dir in libpath:
             for dir in libpath:
@@ -958,21 +969,28 @@ def MakeInstallerAndroid(version, **kwargs):
     aapt_cmd += " -F %s" % (apk_unaligned)
     aapt_cmd += " -F %s" % (apk_unaligned)
     aapt_cmd += " -M apkroot/AndroidManifest.xml"
     aapt_cmd += " -M apkroot/AndroidManifest.xml"
     aapt_cmd += " -A apkroot/assets -S apkroot/res"
     aapt_cmd += " -A apkroot/assets -S apkroot/res"
-    aapt_cmd += " -I $PREFIX/share/aapt/android.jar"
+    aapt_cmd += " -I %s" % (SDK["ANDROID_JAR"])
     oscmd(aapt_cmd)
     oscmd(aapt_cmd)
 
 
     # And add all the libraries to it.
     # And add all the libraries to it.
-    oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned))
+    oscmd("aapt add %s classes.dex" % (os.path.join('..', apk_unaligned)), cwd="apkroot")
     for path, dirs, files in os.walk('apkroot/lib'):
     for path, dirs, files in os.walk('apkroot/lib'):
         if files:
         if files:
             rel = os.path.relpath(path, 'apkroot')
             rel = os.path.relpath(path, 'apkroot')
-            oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel))
+            rel_files = [os.path.join(rel, file).replace('\\', '/') for file in files]
+            oscmd("aapt add %s %s" % (os.path.join('..', apk_unaligned), ' '.join(rel_files)), cwd="apkroot")
 
 
     # Now align the .apk, which is necessary for Android to load it.
     # Now align the .apk, which is necessary for Android to load it.
     oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
     oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
 
 
     # Finally, sign it using a debug key.  This is generated if it doesn't exist.
     # Finally, sign it using a debug key.  This is generated if it doesn't exist.
-    oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
+    if GetHost() == 'android':
+        # Termux version of apksigner automatically generates a debug key.
+        oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
+    else:
+        if not os.path.isfile('debug.ks'):
+            oscmd("keytool -genkey -noprompt -dname CN=Panda3D,O=Panda3D,C=US -keystore debug.ks -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 1000")
+        oscmd("apksigner sign --ks debug.ks --ks-pass pass:android --min-sdk-version %s --out panda3d.apk %s" % (SDK["ANDROID_API"], apk_unsigned))
 
 
     # Clean up.
     # Clean up.
     oscmd("rm -rf apkroot")
     oscmd("rm -rf apkroot")

+ 4 - 1
makepanda/makepanda.py

@@ -2004,7 +2004,10 @@ def CompileRsrc(target, src, opts):
 
 
 def CompileJava(target, src, opts):
 def CompileJava(target, src, opts):
     """Compiles a .java file into a .class file."""
     """Compiles a .java file into a .class file."""
-    cmd = "ecj "
+    if GetHost() == 'android':
+        cmd = "ecj "
+    else:
+        cmd = "javac -bootclasspath " + BracketNameWithQuotes(SDK["ANDROID_JAR"]) + " "
 
 
     optlevel = GetOptimizeOption(opts)
     optlevel = GetOptimizeOption(opts)
     if optlevel >= 4:
     if optlevel >= 4:

+ 116 - 17
makepanda/makepandacore.py

@@ -564,11 +564,12 @@ def LocateBinary(binary):
         p = os.environ["PATH"]
         p = os.environ["PATH"]
 
 
     pathList = p.split(os.pathsep)
     pathList = p.split(os.pathsep)
+    suffixes = ['']
 
 
     if GetHost() == 'windows':
     if GetHost() == 'windows':
-        if not binary.endswith('.exe'):
+        if not binary.lower().endswith('.exe') and not binary.lower().endswith('.bat'):
             # Append .exe if necessary
             # Append .exe if necessary
-            binary += '.exe'
+            suffixes = ['.exe', '.bat']
 
 
         # On Windows the current directory is always implicitly
         # On Windows the current directory is always implicitly
         # searched before anything else on PATH.
         # searched before anything else on PATH.
@@ -576,8 +577,9 @@ def LocateBinary(binary):
 
 
     for path in pathList:
     for path in pathList:
         binpath = os.path.join(os.path.expanduser(path), binary)
         binpath = os.path.join(os.path.expanduser(path), binary)
-        if os.access(binpath, os.X_OK):
-            return os.path.abspath(os.path.realpath(binpath))
+        for suffix in suffixes:
+            if os.access(binpath + suffix, os.X_OK):
+                return os.path.abspath(os.path.realpath(binpath + suffix))
     return None
     return None
 
 
 ########################################################################
 ########################################################################
@@ -586,20 +588,31 @@ def LocateBinary(binary):
 ##
 ##
 ########################################################################
 ########################################################################
 
 
-def oscmd(cmd, ignoreError = False):
+def oscmd(cmd, ignoreError = False, cwd=None):
     if VERBOSE:
     if VERBOSE:
         print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
         print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
     sys.stdout.flush()
     sys.stdout.flush()
 
 
     if sys.platform == "win32":
     if sys.platform == "win32":
-        exe = cmd.split()[0]
+        if cmd[0] == '"':
+            exe = cmd[1 : cmd.index('"', 1)]
+        else:
+            exe = cmd.split()[0]
         exe_path = LocateBinary(exe)
         exe_path = LocateBinary(exe)
         if exe_path is None:
         if exe_path is None:
             exit("Cannot find "+exe+" on search path")
             exit("Cannot find "+exe+" on search path")
+
+        if cwd is not None:
+            pwd = os.getcwd()
+            os.chdir(cwd)
+
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
+
+        if cwd is not None:
+            os.chdir(pwd)
     else:
     else:
         cmd = cmd.replace(';', '\\;')
         cmd = cmd.replace(';', '\\;')
-        res = subprocess.call(cmd, shell=True)
+        res = subprocess.call(cmd, cwd=cwd, shell=True)
         sig = res & 0x7F
         sig = res & 0x7F
         if (GetVerbose() and res != 0):
         if (GetVerbose() and res != 0):
             print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
             print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
@@ -2463,22 +2476,46 @@ def SdkLocateAndroid():
     SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE
     SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE
 
 
     if GetHost() == 'android':
     if GetHost() == 'android':
+        # Assume we're compiling from termux.
+        prefix = os.environ.get("PREFIX", "/data/data/com.termux/files/usr")
+        SDK["ANDROID_JAR"] = prefix + "/share/aapt/android.jar"
         return
         return
 
 
+    # Find the location of the Android SDK.
+    sdk_root = os.environ.get('ANDROID_HOME')
+    if not sdk_root or not os.path.isdir(sdk_root):
+        sdk_root = os.environ.get('ANDROID_SDK_ROOT')
+
+        # Try the default installation location on Windows.
+        if not sdk_root and GetHost() == 'windows':
+            sdk_root = os.path.expanduser(os.path.join('~', 'AppData', 'Local', 'Android', 'Sdk'))
+
+        if not sdk_root:
+            exit('ANDROID_SDK_ROOT must be set when compiling for Android!')
+        elif not os.path.isdir(sdk_root):
+            exit('Cannot find %s.  Please install Android SDK and set ANDROID_SDK_ROOT or ANDROID_HOME.' % (sdk_root))
+
     # Determine the NDK installation directory.
     # Determine the NDK installation directory.
-    if 'NDK_ROOT' not in os.environ:
-        exit('NDK_ROOT must be set when compiling for Android!')
+    if os.environ.get('NDK_ROOT') or os.environ.get('ANDROID_NDK_ROOT'):
+        # We have an explicit setting from an environment variable.
+        ndk_root = os.environ.get('ANDROID_NDK_ROOT')
+        if not ndk_root or not os.path.isdir(ndk_root):
+            ndk_root = os.environ.get('NDK_ROOT')
+            if not ndk_root or not os.path.isdir(ndk_root):
+                exit("Cannot find %s.  Please install Android NDK and set ANDROID_NDK_ROOT." % (ndk_root))
+    else:
+        # Often, it's installed in the ndk-bundle subdirectory of the SDK.
+        ndk_root = os.path.join(sdk_root, 'ndk-bundle')
 
 
-    ndk_root = os.environ["NDK_ROOT"]
-    if not os.path.isdir(ndk_root):
-        exit("Cannot find %s.  Please install Android NDK and set NDK_ROOT." % (ndk_root))
+        if not os.path.isdir(os.path.join(ndk_root, 'toolchains')):
+            exit('Cannot find the Android NDK.  Install it via the SDK manager or set the ANDROID_NDK_ROOT variable if you have installed it in a different location.')
 
 
     SDK["ANDROID_NDK"] = ndk_root
     SDK["ANDROID_NDK"] = ndk_root
 
 
     # Determine the toolchain location.
     # Determine the toolchain location.
     prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt')
     prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt')
     if not os.path.isdir(prebuilt_dir):
     if not os.path.isdir(prebuilt_dir):
-        exit('Not found: %s' % (prebuilt_dir))
+        exit('Not found: %s (is the Android NDK installed?)' % (prebuilt_dir))
 
 
     host_tag = GetHost() + '-x86'
     host_tag = GetHost() + '-x86'
     if host_64:
     if host_64:
@@ -2527,7 +2564,44 @@ def SdkLocateAndroid():
     # STL that ships with Android.
     # STL that ships with Android.
     support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
     support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
     IncDirectory("ALWAYS", support.replace('\\', '/'))
     IncDirectory("ALWAYS", support.replace('\\', '/'))
-    LibName("ALWAYS", "-landroid_support")
+    if api < 21:
+        LibName("ALWAYS", "-landroid_support")
+
+    # Determine the location of android.jar.
+    SDK["ANDROID_JAR"] = os.path.join(sdk_root, 'platforms', 'android-%s' % (api), 'android.jar')
+    if not os.path.isfile(SDK["ANDROID_JAR"]):
+        exit("Cannot find %s.  Install platform API level %s via the SDK manager or change the targeted API level with --target=android-#" % (SDK["ANDROID_JAR"], api))
+
+    # Which build tools versions do we have?  Pick the latest.
+    versions = []
+    for version in os.listdir(os.path.join(sdk_root, "build-tools")):
+        match = re.match('([0-9]+)\\.([0-9]+)\\.([0-9]+)', version)
+        if match:
+            version_tuple = int(match.group(1)), int(match.group(2)), int(match.group(3))
+            versions.append(version_tuple)
+
+    versions.sort()
+    if versions:
+        version = versions[-1]
+        SDK["ANDROID_BUILD_TOOLS"] = os.path.join(sdk_root, "build-tools", "{0}.{1}.{2}".format(*version))
+
+    # And find the location of the Java compiler.
+    if GetHost() == "windows":
+        jdk_home = os.environ.get("JDK_HOME") or os.environ.get("JAVA_HOME")
+        if not jdk_home:
+            # Try to use the Java shipped with Android Studio.
+            studio_path = GetRegistryKey("SOFTWARE\\Android Studio", "Path", override64=False)
+            if studio_path and os.path.isdir(studio_path):
+                jdk_home = os.path.join(studio_path, "jre")
+
+        if not jdk_home or not os.path.isdir(jdk_home):
+            exit("Cannot find JDK.  Please set JDK_HOME or JAVA_HOME.")
+
+        javac = os.path.join(jdk_home, "bin", "javac.exe")
+        if not os.path.isfile(javac):
+            exit("Cannot find %s.  Install the JDK and set JDK_HOME or JAVA_HOME." % (javac))
+
+        SDK["JDK"] = jdk_home
 
 
 ########################################################################
 ########################################################################
 ##
 ##
@@ -2793,6 +2867,13 @@ def SetupBuildEnvironment(compiler):
     if GetTarget() == 'android' and GetHost() != 'android':
     if GetTarget() == 'android' and GetHost() != 'android':
         AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin"))
         AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin"))
 
 
+        if "ANDROID_BUILD_TOOLS" in SDK:
+            AddToPathEnv("PATH", SDK["ANDROID_BUILD_TOOLS"])
+
+        if "JDK" in SDK:
+            AddToPathEnv("PATH", os.path.join(SDK["JDK"], "bin"))
+            os.environ["JAVA_HOME"] = SDK["JDK"]
+
     if compiler == "MSVC":
     if compiler == "MSVC":
         # Add the visual studio tools to PATH et al.
         # Add the visual studio tools to PATH et al.
         SetupVisualStudioEnviron()
         SetupVisualStudioEnviron()
@@ -2844,8 +2925,22 @@ def SetupBuildEnvironment(compiler):
             Warn("%s failed" % (cmd))
             Warn("%s failed" % (cmd))
             SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
             SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
 
 
+        # The Android toolchain on Windows doesn't actually add this one.
+        if target == 'android' and GetHost() == 'windows':
+            libdir = SDK.get("SYSROOT", "") + "/usr/lib"
+            if GetTargetArch() == 'x86_64':
+                libdir += '64'
+            SYS_LIB_DIRS += [libdir]
+
         # Now extract the preprocessor's include directories.
         # Now extract the preprocessor's include directories.
-        cmd = GetCXX() + sysroot_flag + " -x c++ -v -E /dev/null"
+        cmd = GetCXX() + " -x c++ -v -E " + os.devnull
+        if "ANDROID_NDK" in SDK:
+            ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/')
+            cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir)
+            cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"])
+        else:
+            cmd += sysroot_flag
+
         null = open(os.devnull, 'w')
         null = open(os.devnull, 'w')
         handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True)
         handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True)
         scanning = False
         scanning = False
@@ -2858,8 +2953,12 @@ def SetupBuildEnvironment(compiler):
                     scanning = True
                     scanning = True
                 continue
                 continue
 
 
-            if not line.startswith(' /'):
-                continue
+            if sys.platform == "win32":
+                if not line.startswith(' '):
+                    continue
+            else:
+                if not line.startswith(' /'):
+                    continue
 
 
             line = line.strip()
             line = line.strip()
             if line.endswith(" (framework directory)"):
             if line.endswith(" (framework directory)"):

+ 1 - 0
panda/src/android/android_main.cxx

@@ -24,6 +24,7 @@
 #include "config_display.h"
 #include "config_display.h"
 // #define OPENGLES_1 #include "config_androiddisplay.h"
 // #define OPENGLES_1 #include "config_androiddisplay.h"
 
 
+#include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 
 

+ 0 - 1
panda/src/display/graphicsEngine.cxx

@@ -593,7 +593,6 @@ remove_all_windows() {
   // appeared to be happening, and this worked around it.
   // appeared to be happening, and this worked around it.
   Windows old_windows;
   Windows old_windows;
   old_windows.swap(_windows);
   old_windows.swap(_windows);
-  Windows::iterator wi;
   for (GraphicsOutput *win : old_windows) {
   for (GraphicsOutput *win : old_windows) {
     nassertv(win != nullptr);
     nassertv(win != nullptr);
     do_remove_window(win, current_thread);
     do_remove_window(win, current_thread);

+ 2 - 2
panda/src/downloader/documentSpec.cxx

@@ -66,7 +66,7 @@ input(std::istream &in) {
     // Scan the tag, up to but not including the closing paren.
     // Scan the tag, up to but not including the closing paren.
     std::string tag;
     std::string tag;
     in >> ch;
     in >> ch;
-    while (!in.fail() && ch != EOF && ch != ')') {
+    while (!in.fail() && ch != ')') {
       tag += ch;
       tag += ch;
       // We want to include embedded whitespace, so we use get().
       // We want to include embedded whitespace, so we use get().
       ch = in.get();
       ch = in.get();
@@ -81,7 +81,7 @@ input(std::istream &in) {
   // Scan the date, up to but not including the closing bracket.
   // Scan the date, up to but not including the closing bracket.
   if (ch != ']') {
   if (ch != ']') {
     std::string date;
     std::string date;
-    while (!in.fail() && ch != EOF && ch != ']') {
+    while (!in.fail() && ch != ']') {
       date += ch;
       date += ch;
       ch = in.get();
       ch = in.get();
     }
     }

+ 1 - 1
panda/src/downloader/httpDate.cxx

@@ -279,7 +279,7 @@ input(std::istream &in) {
 
 
   string date;
   string date;
   ch = in.get();
   ch = in.get();
-  while (!in.fail() && ch != EOF && ch != '"') {
+  while (!in.fail() && ch != '"') {
     date += ch;
     date += ch;
     ch = in.get();
     ch = in.get();
   }
   }

+ 2 - 0
panda/src/pgraph/cullTraverserData.h

@@ -67,6 +67,8 @@ PUBLISHED:
   void apply_transform_and_state(CullTraverser *trav);
   void apply_transform_and_state(CullTraverser *trav);
   void apply_transform(const TransformState *node_transform);
   void apply_transform(const TransformState *node_transform);
 
 
+  MAKE_PROPERTY(node_path, get_node_path);
+
 private:
 private:
   // We store a chain leading all the way to the root, so that we can compose
   // We store a chain leading all the way to the root, so that we can compose
   // a NodePath.  We may be able to eliminate this requirement in the future.
   // a NodePath.  We may be able to eliminate this requirement in the future.

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypePNM.cxx

@@ -193,7 +193,7 @@ pm_getuint(istream * const ifP) {
     'unsigned int'), issue an error message to stderr and abort the
     'unsigned int'), issue an error message to stderr and abort the
     program.
     program.
     -----------------------------------------------------------------------------*/
     -----------------------------------------------------------------------------*/
-  char ch;
+  int ch;
   unsigned int i;
   unsigned int i;
 
 
   // skip whitespace
   // skip whitespace