https://sourceforge.net/p/infozip/patches/25/

From 706b9fdc032582a258f0f8e5444fde4ee5bab220 Mon Sep 17 00:00:00 2001
From: Mingye Wang <arthur200126@gmail.com>
Date: Sat, 25 Jan 2020 23:07:03 +0800
Subject: [PATCH 2/2] unix: reproducible directory order (scandir)

This commit replaces the readdir() loop with scandir. This means a well-
defined, reproducible sort order, with the downside that we will need to
store ALL directory entries in memory to sort them.

I am too lazy to add a switch to change how it is supposed to work. This
is only a proof-of-concept.

Co-Authored-By: Bernhard M. Wiedemann <bwiedemann suse.de>
---
 unix/unix.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

Index: zip30/unix/unix.c
===================================================================
--- zip30.orig/unix/unix.c
+++ zip30/unix/unix.c
@@ -92,6 +92,10 @@ DIR *dirp;
 #define closedir(dirp) fclose(dirp)
 #endif /* NO_DIR */
 
+#ifdef NO_SCANDIR
+/* TODO Port the FreeBSD libc version */
+#error "We need scandir now."
+#endif
 
 local char *readd(d)
 DIR *d;                 /* directory stream to read from */
@@ -111,12 +115,14 @@ int caseflag;           /* true to force
    an error code in the ZE_ class. */
 {
   char *a;              /* path and name for recursion */
-  DIR *d;               /* directory stream from opendir() */
-  char *e;              /* pointer to name from readd() */
+  char *e;              /* pointer to name from scandir() */
+  int c;                /* number of entries from scandir() */
+  int i;                /* entry index */
   int m;                /* matched flag */
   char *p;              /* path for recursion */
   z_stat s;             /* result of stat() */
   struct zlist far *z;  /* steps through zfiles list */
+  struct dirent **namelist;
 
   if (strcmp(n, "-") == 0)   /* if compressing stdin */
     return newname(n, 0, caseflag);
@@ -176,14 +182,16 @@ int caseflag;           /* true to force
       }
     }
     /* recurse into directory */
-    if (recurse && (d = opendir(n)) != NULL)
+    if (recurse && (c = scandir(n, &namelist, NULL, alphasort)) >= 0)
     {
-      while ((e = readd(d)) != NULL) {
+      for (i = 0; i < c; i++) {
+        e = namelist[i]->d_name;
         if (strcmp(e, ".") && strcmp(e, ".."))
         {
           if ((a = malloc(strlen(p) + strlen(e) + 1)) == NULL)
           {
-            closedir(d);
+            for (; i < c; i++) free(namelist[i]);
+            free(namelist);
             free((zvoid *)p);
             return ZE_MEM;
           }
@@ -197,8 +205,9 @@ int caseflag;           /* true to force
           }
           free((zvoid *)a);
         }
+        free(namelist[i]);
       }
-      closedir(d);
+      free(namelist);
     }
     free((zvoid *)p);
   } /* (s.st_mode & S_IFDIR) */
Index: zip30/test.sh
===================================================================
--- /dev/null
+++ zip30/test.sh
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+mkdir -p test/{a,b,c}
+echo x > test/a/x
+echo y > test/a/y
+echo y > test/b/y
+export SOURCE_DATE_EPOCH=1
+./zip -X -r test.zip test
+md5sum test.zip
+echo "89057b9c9501ce122973d24b68a0522a  test.zip" | md5sum -c
+