[Zlib-devel] Windows CE: errno -> GetLastError; use win32/Makefile.gcc on arm-mingw32ce

Pedro Alves alves.ped at gmail.com
Fri Jan 1 15:25:25 EST 2010


Hello!

I said on the "zlib 1.2.3.4 Winter Solstice Edition" thread:

> On Tuesday 29 December 2009 16:01:40, Vincent Torri wrote:
> > 
> > On Tue, 29 Dec 2009, Pedro Alves wrote:
> > > Vincent, I can help with writing patches, if you're unavailable.
> > 
> > actually, writing a patch to use GetLastError() instead of the dummy errno 
> > would be nice :-)
> 
> I'll give it a try.  grep tells me that errno isn't much used other
> than being cleared before a library call, and for Z_ERRNO/zstrerror.
> 
> How about this for a plan:
> 
> 1) Stop treating errno as an lvalue.  In zutil.h, probably:
> 
>  #if !<WinCE>
>  # define seterrno(ERR) do { errno = (ERR); } while (0)
>  # if NO_ERRNO_H
>     ...
>  # else
>     ...
>  # endif
>  #else
>  # define seterrno(ERR) SetLastError(ERR)
>  # define errno GetLastError()
>  #endif
> 
> 2) Replace all `errno = 0' by (z)seterrno(0).
> 
> 3) Write a zstrerror (and perror) for WinCE that
>    uses FormatMessage.
> 
> Would something along these lines be acceptable?

Here's the patch that implements that.  I've chosen to
make the predicate 

 #if defined UNDER_CE && defined NO_ERRNO_H

so that Windows CE compilers/runtimes that do provide errno.h and
errno/strerror continue to use the runtime's errno and friends.
That is the case of at least arm-cegcc (our _other_ compiler
over at the cegcc project.  It is newlib based, kind of like Cygwin's
little brother; contrast with arm-mingw32ce, which is akin
of i686-pc-mingw32), and people using Microsoft's compiler with
runtime augmentation/replacement libraries like celib <http://www.rainer-keuchel.de/wince/celib.html>.  I've tested
compiling zlib with arm-cegcc, but didn't with celib.  I don't
use the latter, I'm just keeping it in mind.


As discussed on the other thread, this patch makes it so
that stddef.h is also included on mingw32ce (gcc based), by:

  #ifdef STDC
 -#  ifndef _WIN32_WCE
 +#  if !(defined _WIN32_WCE && defined _MSV_VER)
  #    include <stddef.h>

That is, restrict the non-inclusion of stddef.h to
MS's compiler.


The final change the patch makes, is tweak win32/Makefile.gcc
to be able to use it with arm-mingw32ce-gcc.

  - Define STRIP so that one can override it on the command line, just
    like CC, AS, etc. are already defined as make variables.

  - The makefile was assuming that executables get the .exe suffix
    appended automatically (`gcc foo.c -o foo' -> foo.exe), but
    that's only true for native compilers.  Since
    this makefile is for Windows targets only, the fix is to simply
    append the .exe to the make rules that produce executables.
    The main zlib/Makefile.in uses $(EXE), presumaly fr  --- let
    me know if you'd prefer that.

  - The makefile is using dllwrap.  This tool is deprecated for
    years and years, and as such doesn't exist for arm-mingw32ce.
    The patch switches to building the zlib1.dll by linking with
    `gcc -shared', as Gawd intended.  I've built (and manually
    tested with minigzip_d) the dll on x86/MinGW (actually Cygwin's 
    gcc -mno-cygwin), and looked at its exports comparing to the
    zlib1.dll downloadable from the zlib homepage, and everything
    looked fine to me.

 To make sure I haven't broken anything (too badly :-) ) I've also
compiled and tested zlib on x86_64-linux, with:

  ./configure --shared
  make
  make test

I got all okays.


I've manually tested zlib on a Windows Mobile 5.0 device,
by compressing/decompressing a file using both the minigzip
that links to static zlib, and the minigzip that links to
the zlib1.dll.

Finally, here's the command line I'm using:

 make -f win32/Makefile.gcc \
   CC=arm-mingw32ce-gcc \
   AR="arm-mingw32ce-ar" \
   RANLIB="arm-mingw32ce-ranlib" \
   RC="arm-mingw32ce-windres" \
   STRIP="arm-mingw32ce-strip" \
   CFLAGS="-DNO_ERRNO_H -DNO_FSEEKO -O3 -Wall"

The patch applies against zlib 1.2.3.4.  You may get a
conflict due to a "fd < 0" -> "fd == -1" change that
I think is already in your development tree.

I tried to follow the coding style of zlib.  Let me know
if you'd like me to split the patch into smaller bits,
and if I should write a changelog entry of some form.

-- 
Pedro Alves

---
 gzio.c             |   73 ++++++++++++++++++++++++++++++++++++++++++++++-------
 minigzip.c         |   63 +++++++++++++++++++++++++++++++++++++++++++++
 win32/Makefile.gcc |   22 ++++++++-------
 zutil.h            |   24 ++++++++---------
 4 files changed, 151 insertions(+), 31 deletions(-)

Index: zlib-1.2.3.4/gzio.c
===================================================================
--- zlib-1.2.3.4.orig/gzio.c	2009-12-22 06:43:16.000000000 +0000
+++ zlib-1.2.3.4/gzio.c	2010-01-01 18:55:14.000000000 +0000
@@ -38,6 +38,59 @@ struct internal_state {int dummy;}; /* f
 #  define Z_PRINTF_BUFSIZE 4096
 #endif
 
+#if defined UNDER_CE && defined NO_ERRNO_H
+#  include <windows.h>
+
+/* Map the Windows error number in ERROR to a locale-dependent error
+   message string and return a pointer to it.  Typically, the values
+   for ERROR come from GetLastError.
+
+   The string pointed to shall not be modified by the application,
+   but may be overwritten by a subsequent call to strwinerror
+
+   The strwinerror function does not change the current setting
+   of GetLastError.  */
+
+local char *strwinerror (error)
+     DWORD error;
+{
+    static char buf[1024];
+
+    wchar_t *msgbuf;
+    DWORD lasterr = GetLastError();
+    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+	| FORMAT_MESSAGE_ALLOCATE_BUFFER,
+	NULL,
+	error,
+	0, /* Default language */
+	(LPVOID)&msgbuf,
+	0,
+	NULL);
+    if (chars != 0) {
+	/* If there is an \r\n appended, zap it.  */
+	if (chars >= 2
+	    && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
+	    chars -= 2;
+	    msgbuf[chars] = 0;
+	}
+
+	if (chars > sizeof (buf) - 1) {
+	    chars = sizeof (buf) - 1;
+	    msgbuf[chars] = 0;
+	}
+
+	wcstombs(buf, msgbuf, chars + 1);
+	LocalFree(msgbuf);
+    } else {
+	sprintf(buf, "unknown win32 error (%ld)", error);
+    }
+
+    SetLastError(lasterr);
+    return buf;
+}
+
+#endif /* UNDER_CE && NO_ERRNO_H */
+
 #ifdef __MVS__
 #  pragma map (fdopen , "\174\174FDOPEN")
    FILE *fdopen(int, const char *);
@@ -191,8 +244,8 @@ local gzFile gz_open (path, mode, fd, us
     }
     s->stream.avail_out = Z_BUFSIZE;
 
-    errno = 0;
-    s->file = fd < 0 ? (use64 ? F_OPEN64(path, fmode) : F_OPEN(path, fmode)) :
+    zseterrno(0);
+    s->file = fd == -1 ? (use64 ? F_OPEN64(path, fmode) : F_OPEN(path, fmode)) :
               (FILE*)fdopen(fd, fmode);
 
     if (s->file == NULL) {
@@ -250,7 +303,7 @@ gzFile ZEXPORT gzdopen (fd, mode)
 {
     char name[46];      /* allow for up to 128-bit integers */
 
-    if (fd < 0) return (gzFile)Z_NULL;
+    if (fd == -1) return (gzFile)Z_NULL;
     sprintf(name, "<fd:%d>", fd); /* for debugging */
 
     return gz_open (name, mode, fd, 0);
@@ -291,7 +344,7 @@ local int get_byte(s)
 {
     if (s->z_eof) return EOF;
     if (s->stream.avail_in == 0) {
-        errno = 0;
+        zseterrno(0);
         s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file);
         if (s->stream.avail_in == 0) {
             s->z_eof = 1;
@@ -327,7 +380,7 @@ local void check_header(s)
     len = s->stream.avail_in;
     if (len < 2) {
         if (len) s->inbuf[0] = s->stream.next_in[0];
-        errno = 0;
+        zseterrno(0);
         len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
         if (len == 0) s->z_eof = 1;
         if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
@@ -403,7 +456,7 @@ local int destroy (s)
     }
     if (s->file != NULL && fclose(s->file)) {
 #ifdef ESPIPE
-        if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+        if (zerrno() != ESPIPE) /* fclose is broken for pipes in HP/UX */
 #endif
             err = Z_ERRNO;
     }
@@ -477,7 +530,7 @@ int ZEXPORT gzread (file, buf, len)
         }
         if (s->stream.avail_in == 0 && !s->z_eof) {
 
-            errno = 0;
+            zseterrno(0);
             s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file);
             if (s->stream.avail_in == 0) {
                 s->z_eof = 1;
@@ -1039,7 +1092,9 @@ int ZEXPORT gzclose (file)
     return destroy((gz_stream*)file);
 }
 
-#if defined(STDC) && !defined(_WIN32_WCE)
+#if defined UNDER_CE && defined NO_ERRNO_H
+#  define zstrerror(errnum) strwinerror((DWORD)errnum)
+#elif defined (STDC)
 #  define zstrerror(errnum) strerror(errnum)
 #else
 #  define zstrerror(errnum) ""
@@ -1066,7 +1121,7 @@ const char * ZEXPORT gzerror (file, errn
     *errnum = s->z_err;
     if (*errnum == Z_OK) return (const char*)"";
 
-    m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+    m = (char*)(*errnum == Z_ERRNO ? zstrerror(zerrno()) : s->stream.msg);
 
     if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
 
Index: zlib-1.2.3.4/minigzip.c
===================================================================
--- zlib-1.2.3.4.orig/minigzip.c	2006-08-16 16:21:07.000000000 +0100
+++ zlib-1.2.3.4/minigzip.c	2010-01-01 19:03:15.000000000 +0000
@@ -54,6 +54,69 @@
   extern int unlink OF((const char *));
 #endif
 
+#if defined UNDER_CE && defined NO_ERRNO_H
+#  include <windows.h>
+#  define perror(s) pwinerror(s)
+
+/* Map the Windows error number in ERROR to a locale-dependent error
+   message string and return a pointer to it.  Typically, the values
+   for ERROR come from GetLastError.
+
+   The string pointed to shall not be modified by the application,
+   but may be overwritten by a subsequent call to strwinerror
+
+   The strwinerror function does not change the current setting
+   of GetLastError.  */
+
+static char *strwinerror (error)
+     DWORD error;
+{
+    static char buf[1024];
+
+    wchar_t *msgbuf;
+    DWORD lasterr = GetLastError();
+    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+	| FORMAT_MESSAGE_ALLOCATE_BUFFER,
+	NULL,
+	error,
+	0, /* Default language */
+	(LPVOID)&msgbuf,
+	0,
+	NULL);
+    if (chars != 0) {
+	/* If there is an \r\n appended, zap it.  */
+	if (chars >= 2
+	    && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
+	    chars -= 2;
+	    msgbuf[chars] = 0;
+	}
+
+	if (chars > sizeof (buf) - 1) {
+	    chars = sizeof (buf) - 1;
+	    msgbuf[chars] = 0;
+	}
+
+	wcstombs(buf, msgbuf, chars + 1);
+	LocalFree(msgbuf);
+    } else {
+	sprintf(buf, "unknown win32 error (%ld)", error);
+    }
+
+    SetLastError(lasterr);
+    return buf;
+}
+
+static void pwinerror (s)
+    const char *s;
+{
+    if (s && *s)
+	fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
+    else
+	fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
+}
+
+#endif /* UNDER_CE && NO_ERRNO_H */
+
 #ifndef GZ_SUFFIX
 #  define GZ_SUFFIX ".gz"
 #endif
Index: zlib-1.2.3.4/zutil.h
===================================================================
--- zlib-1.2.3.4.orig/zutil.h	2006-10-14 18:03:00.000000000 +0100
+++ zlib-1.2.3.4/zutil.h	2010-01-01 17:33:01.000000000 +0000
@@ -17,24 +17,24 @@
 #include "zlib.h"
 
 #ifdef STDC
-#  ifndef _WIN32_WCE
+#  if !(defined _WIN32_WCE && defined _MSV_VER)
 #    include <stddef.h>
 #  endif
 #  include <string.h>
 #  include <stdlib.h>
 #endif
-#if defined(NO_ERRNO_H) || defined(_WIN32_WCE)
-#   ifdef _WIN32_WCE
-      /* The Microsoft C Run-Time Library for Windows CE doesn't have
-       * errno.  We define it as a global variable to simplify porting.
-       * Its value is always 0 and should not be used.  We rename it to
-       * avoid conflict with other libraries that use the same workaround.
-       */
-#     define errno z_errno
-#   endif
-    extern int errno;
+
+#if defined UNDER_CE && defined NO_ERRNO_H
+#  define zseterrno(ERR) SetLastError((DWORD)(ERR))
+#  define zerrno() ((int)GetLastError())
 #else
-#   include <errno.h>
+#  ifdef NO_ERRNO_H
+     extern int errno;
+#  else
+#    include <errno.h>
+#  endif
+#  define zseterrno(ERR) do { errno = (ERR); } while (0)
+#  define zerrno() errno
 #endif
 
 #ifndef local
Index: zlib-1.2.3.4/win32/Makefile.gcc
===================================================================
--- zlib-1.2.3.4.orig/win32/Makefile.gcc	2010-01-01 17:38:24.000000000 +0000
+++ zlib-1.2.3.4/win32/Makefile.gcc	2010-01-01 18:19:09.000000000 +0000
@@ -45,6 +45,8 @@ ARFLAGS = rcs
 RC = windres
 RCFLAGS = --define GCC_WINDRES
 
+STRIP = strip
+
 CP = cp -fp
 # If GNU install is available, replace $(CP) with install.
 INSTALL = $(CP)
@@ -57,13 +59,13 @@ OBJS = adler32.o compress.o crc32.o defl
        inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o
 OBJA =
 
-all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) example minigzip example_d minigzip_d
+all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) example.exe minigzip.exe example_d.exe minigzip_d.exe
 
-test: example minigzip
+test: example.exe minigzip.exe
 	./example
 	echo hello world | ./minigzip | ./minigzip -d
 
-testdll: example_d minigzip_d
+testdll: example_d.exe minigzip_d.exe
 	./example_d
 	echo hello world | ./minigzip_d | ./minigzip_d -d
 
@@ -79,20 +81,20 @@ $(STATICLIB): $(OBJS) $(OBJA)
 $(IMPLIB): $(SHAREDLIB)
 
 $(SHAREDLIB): win32/zlib.def $(OBJS) $(OBJA) zlibrc.o
-	dllwrap --driver-name $(CC) --def win32/zlib.def \
-	  --implib $(IMPLIB) -o $@ $(OBJS) $(OBJA) zlibrc.o
-	strip $@
+	$(CC) -shared -Wl,--out-implib,$(IMPLIB) \
+	-o $@ win32/zlib.def $(OBJS) $(OBJA) zlibrc.o
+	$(STRIP) $@
 
-example: example.o $(STATICLIB)
+example.exe: example.o $(STATICLIB)
 	$(LD) $(LDFLAGS) -o $@ example.o $(STATICLIB)
 
-minigzip: minigzip.o $(STATICLIB)
+minigzip.exe: minigzip.o $(STATICLIB)
 	$(LD) $(LDFLAGS) -o $@ minigzip.o $(STATICLIB)
 
-example_d: example.o $(IMPLIB)
+example_d.exe: example.o $(IMPLIB)
 	$(LD) $(LDFLAGS) -o $@ example.o $(IMPLIB)
 
-minigzip_d: minigzip.o $(IMPLIB)
+minigzip_d.exe: minigzip.o $(IMPLIB)
 	$(LD) $(LDFLAGS) -o $@ minigzip.o $(IMPLIB)
 
 zlibrc.o: win32/zlib1.rc




More information about the Zlib-devel mailing list