Description: Remove all use of MAXPATHLEN and its variants
Author: James Clarke <jrtc27@jrtc27.com>
Forwarded: https://github.com/polyml/polyml/pull/33
Applied-Upstream: https://github.com/polyml/polyml/commit/1fcce28158b37906ff130f6dd6d240efda934a82
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/libpolyml/basicio.cpp
+++ b/libpolyml/basicio.cpp
@@ -111,10 +111,6 @@
 #define _tcsdup strdup
 #endif
 
-#if(!defined(MAXPATHLEN) && defined(MAX_PATH))
-#define MAXPATHLEN MAX_PATH
-#endif
-
 #ifndef O_BINARY
 #define O_BINARY    0 /* Not relevant. */
 #endif
@@ -1651,13 +1647,21 @@
                 raise_syscall(taskData, "GetCurrentDirectory failed", -(int)GetLastError());
             return SAVE(C_string_to_Poly(taskData, buff));
 #else
-            // This is a bit messy in Unix.  getcwd will return an error result if there's
-            // not enough space be we have to iterate to find the size.
-            // Use the fixed size for the moment.
-            TCHAR string_buffer[MAXPATHLEN+1];
-            if (getcwd(string_buffer, MAXPATHLEN+1) == NULL)
+            size_t size = 4096;
+            TempString string_buffer((TCHAR *)malloc(size * sizeof(TCHAR)));
+            if (string_buffer == NULL) raise_syscall(taskData, "Insufficient memory", ENOMEM);
+            TCHAR *cwd;
+            while ((cwd = getcwd(string_buffer, size)) == NULL && errno == ERANGE) {
+                if (size > SIZE_MAX / 2) raise_fail(taskData, "getcwd needs too large a buffer");
+                size *= 2;
+                TCHAR *new_buf = (TCHAR *)realloc(string_buffer, size * sizeof(TCHAR));
+                if (new_buf == NULL) raise_syscall(taskData, "Insufficient memory", ENOMEM);
+                string_buffer = new_buf;
+            }
+
+            if (cwd == NULL)
                raise_syscall(taskData, "getcwd failed", errno);
-            return SAVE(C_string_to_Poly(taskData, string_buffer));
+            return SAVE(C_string_to_Poly(taskData, cwd));
 #endif
         }
 
@@ -1728,8 +1732,19 @@
             int nLen;
             TempString linkName(args->Word());
             if (linkName == 0) raise_syscall(taskData, "Insufficient memory", ENOMEM);
-            char resBuf[MAXPATHLEN];
-            nLen = readlink(linkName, resBuf, sizeof(resBuf));
+
+            size_t size = 4096;
+            TempString resBuf((TCHAR *)malloc(size * sizeof(TCHAR)));
+            if (resBuf == NULL) raise_syscall(taskData, "Insufficient memory", ENOMEM);
+            // nLen is signed, so cast size to ssize_t to perform signed
+            // comparison, avoiding an infinite loop when nLen is -1.
+            while ((nLen = readlink(linkName, resBuf, size)) >= (ssize_t) size) {
+                size *= 2;
+                if (size > SSIZE_MAX) raise_fail(taskData, "readlink needs too large a buffer");
+                TCHAR *newBuf = (TCHAR *)realloc(resBuf, size * sizeof(TCHAR));
+                if (newBuf == NULL) raise_syscall(taskData, "Insufficient memory", ENOMEM);
+                resBuf = newBuf;
+            }
             if (nLen < 0) raise_syscall(taskData, "readlink failed", errno);
             return(SAVE(C_string_to_Poly(taskData, resBuf, nLen)));
 #endif
@@ -1780,13 +1795,18 @@
                 raise_syscall(taskData, "GetTempPath failed", -(int)(GetLastError()));
             lstrcat(buff, _T("MLTEMPXXXXXX"));
 #else
-            TCHAR buff[MAXPATHLEN];
+            const char *template_subdir =  "/MLTEMPXXXXXX";
 #ifdef P_tmpdir
+            TempString buff((TCHAR *)malloc(strlen(P_tmpdir) + strlen(template_subdir) + 1));
+            if (buff == 0) raise_syscall(taskData, "Insufficient memory", ENOMEM);
             strcpy(buff, P_tmpdir);
 #else
-            strcpy(buff, "/tmp");
+            const char *tmpdir = "/tmp";
+            TempString buff((TCHAR *)malloc(strlen(tmpdir) + strlen(template_subdir) + 1));
+            if (buff == 0) raise_syscall(taskData, "Insufficient memory", ENOMEM);
+            strcpy(buff, tmpdir);
 #endif
-            strcat(buff, "/MLTEMPXXXXXX");
+            strcat(buff, template_subdir);
 #endif
 
 #if (defined(HAVE_MKSTEMP) && ! defined(UNICODE))
--- a/libpolyml/exporter.cpp
+++ b/libpolyml/exporter.cpp
@@ -82,10 +82,6 @@
 #include "machoexport.h"
 #endif
 
-#if(!defined(MAXPATHLEN) && defined(MAX_PATH))
-#define MAXPATHLEN MAX_PATH
-#endif
-
 /*
 To export the function and everything reachable from it we need to copy
 all the objects into a new area.  We leave tombstones in the original
@@ -416,14 +412,14 @@
 
 static void exporter(TaskData *taskData, Handle args, const TCHAR *extension, Exporter *exports)
 {
-    TCHAR fileNameBuff[MAXPATHLEN+MAX_EXTENSION];
-    POLYUNSIGNED length =
-        Poly_string_to_C(DEREFHANDLE(args)->Get(0), fileNameBuff, MAXPATHLEN);
-    if (length > MAXPATHLEN)
-        raise_syscall(taskData, "File name too long", ENAMETOOLONG);
+    size_t extLen = _tcslen(extension);
+    TempString fileNameBuff(Poly_string_to_C_alloc(DEREFHANDLE(args)->Get(0), extLen * sizeof(TCHAR)));
+    if (fileNameBuff == NULL)
+        raise_syscall(taskData, "Insufficient memory", ENOMEM);
+    size_t length = _tcslen(fileNameBuff);
 
     // Does it already have the extension?  If not add it on.
-    if (length < _tcslen(extension) || _tcscmp(fileNameBuff + length - _tcslen(extension), extension) != 0)
+    if (length < extLen || _tcscmp(fileNameBuff + length - extLen, extension) != 0)
         _tcscat(fileNameBuff, extension);
 #if (defined(_WIN32) && defined(UNICODE))
     exports->exportFile = _wfopen(fileNameBuff, L"wb");
--- a/libpolyml/polystring.cpp
+++ b/libpolyml/polystring.cpp
@@ -97,7 +97,7 @@
     return chars;
 } /* Poly_string_to_C */
 
-char *Poly_string_to_C_alloc(PolyWord ps)
+char *Poly_string_to_C_alloc(PolyWord ps, size_t buffExtra)
 /* Similar to Poly_string_to_C except that the string is allocated using
    malloc and must be freed by the caller. */
 {
@@ -105,7 +105,7 @@
 
     if (IS_INT(ps))
     {
-        res = (char*)malloc(2);
+        res = (char*)malloc(2 + buffExtra);
         if (res == 0) return 0;
         res[0] = (char)(UNTAGGED(ps));
         res[1] = '\0';
@@ -114,7 +114,7 @@
     {
         PolyStringObject * str = (PolyStringObject *)ps.AsObjPtr();
         POLYUNSIGNED chars = str->length;
-        res = (char*)malloc(chars+1);
+        res = (char*)malloc(chars + buffExtra + 1);
         if (res == 0) return 0;
         if (chars != 0) strncpy(res, str->chars, chars);
         res[chars] = '\0';
--- a/libpolyml/polystring.h
+++ b/libpolyml/polystring.h
@@ -46,7 +46,7 @@
 /* PolyStringObject functions */
 extern PolyWord C_string_to_Poly(TaskData *mdTaskData, const char *buffer, size_t buffLen = -1);
 extern POLYUNSIGNED Poly_string_to_C(PolyWord ps, char *buff, POLYUNSIGNED bufflen);
-extern char *Poly_string_to_C_alloc(PolyWord ps);
+extern char *Poly_string_to_C_alloc(PolyWord ps, size_t buffExtra = 0);
 
 extern Handle convert_string_list(TaskData *mdTaskData, int count, char **strings);
 
--- a/libpolyml/savestate.cpp
+++ b/libpolyml/savestate.cpp
@@ -101,10 +101,6 @@
 #include "osmem.h"
 #include "gc.h" // For FullGC.
 
-#if(!defined(MAXPATHLEN) && defined(MAX_PATH))
-#define MAXPATHLEN MAX_PATH
-#endif
-
 #ifdef _MSC_VER
 // Don't tell me about ISO C++ changes.
 #pragma warning(disable:4996)
@@ -653,11 +649,7 @@
 
 Handle SaveState(TaskData *taskData, Handle args)
 {
-    TCHAR fileNameBuff[MAXPATHLEN];
-    POLYUNSIGNED length =
-        Poly_string_to_C(DEREFHANDLE(args)->Get(0), fileNameBuff, MAXPATHLEN);
-    if (length > MAXPATHLEN)
-        raise_syscall(taskData, "File name too long", ENAMETOOLONG);
+    TempString fileNameBuff(Poly_string_to_C_alloc(DEREFHANDLE(args)->Get(0)));
     // The value of depth is zero for top-level save so we need to add one for hierarchy.
     unsigned newHierarchy = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(1)) + 1;
 
@@ -694,7 +686,7 @@
     // The fileName here is the last file loaded.  As well as using it
     // to load the name can also be printed out at the end to identify the
     // particular file in the hierarchy that failed.
-    TCHAR fileName[MAXPATHLEN];
+    AutoFree<TCHAR*> fileName;
     int errNumber;
 };
 
@@ -710,23 +702,22 @@
             return;
         }
         ML_Cons_Cell *p = DEREFLISTHANDLE(fileNameList);
-        POLYUNSIGNED length = Poly_string_to_C(p->h, fileName, MAXPATHLEN);
-        if (length > MAXPATHLEN)
+        fileName = Poly_string_to_C_alloc(p->h);
+        if (fileName == NULL)
         {
-            errorResult = "File name too long";
-            errNumber = ENAMETOOLONG;
+            errorResult = "Insufficient memory";
+            errNumber = ENOMEM;
             return;
         }
         (void)LoadFile(true, 0, p->t);
     }
     else
     {
-        POLYUNSIGNED length =
-            Poly_string_to_C(DEREFHANDLE(fileNameList), fileName, MAXPATHLEN);
-        if (length > MAXPATHLEN)
+        fileName = Poly_string_to_C_alloc(DEREFHANDLE(fileNameList));
+        if (fileName == NULL)
         {
-            errorResult = "File name too long";
-            errNumber = ENAMETOOLONG;
+            errorResult = "Insufficient memory";
+            errNumber = ENOMEM;
             return;
         }
         (void)LoadFile(true, 0, TAGGED(0));
@@ -867,11 +858,11 @@
                 return false;
             }
             ML_Cons_Cell *p = (ML_Cons_Cell *)tail.AsObjPtr();
-            POLYUNSIGNED length = Poly_string_to_C(p->h, fileName, MAXPATHLEN);
-            if (length > MAXPATHLEN)
+            fileName = Poly_string_to_C_alloc(p->h);
+            if (fileName == NULL)
             {
-                errorResult = "File name too long";
-                errNumber = ENAMETOOLONG;
+                errorResult = "Insufficient memory";
+                errNumber = ENOMEM;
                 return false;
             }
             if (! LoadFile(false, header.parentTimeStamp, p->t))
@@ -880,7 +871,17 @@
         else
         {
             size_t toRead = header.stringTableSize-header.parentNameEntry;
-            if (MAXPATHLEN < toRead) toRead = MAXPATHLEN;
+            size_t elems = ((toRead + sizeof(TCHAR) - 1) / sizeof(TCHAR));
+            // Always allow space for null terminator
+            size_t roundedBytes = (elems + 1) * sizeof(TCHAR);
+            TCHAR *newFileName = (TCHAR *)realloc(fileName, roundedBytes);
+            if (newFileName == NULL)
+            {
+                errorResult = "Insufficient memory";
+                errNumber = ENOMEM;
+                return false;
+            }
+            fileName = newFileName;
 
             if (header.parentNameEntry >= header.stringTableSize /* Bad entry */ ||
                 fseek(loadFile, header.stringTable + header.parentNameEntry, SEEK_SET) != 0 ||
@@ -889,7 +890,7 @@
                 errorResult = "Unable to read parent file name";
                 return false;
             }
-            fileName[toRead] = 0; // Should already be null-terminated, but just in case.
+            fileName[elems] = 0; // Should already be null-terminated, but just in case.
 
             if (! LoadFile(false, header.parentTimeStamp, TAGGED(0)))
                 return false;
@@ -1097,11 +1098,11 @@
             raise_fail(taskData, loader.errorResult);
         else
         {
-            char buff[MAXPATHLEN+100];
+            AutoFree<char*> buff((char *)malloc(strlen(loader.errorResult) + 2 + _tcslen(loader.fileName) * sizeof(TCHAR) + 1));
 #if (defined(_WIN32) && defined(UNICODE))
-            sprintf(buff, "%s: %S", loader.errorResult, loader.fileName);
+            sprintf(buff, "%s: %S", loader.errorResult, (TCHAR *)loader.fileName);
 #else
-            sprintf(buff, "%s: %s", loader.errorResult, loader.fileName);
+            sprintf(buff, "%s: %s", loader.errorResult, (TCHAR *)loader.fileName);
 #endif
             raise_syscall(taskData, buff, loader.errNumber);
         }
@@ -1139,26 +1140,23 @@
 Handle RenameParent(TaskData *taskData, Handle args)
 // Change the name of the immediate parent stored in a child
 {
-    TCHAR fileNameBuff[MAXPATHLEN], parentNameBuff[MAXPATHLEN];
     // The name of the file to modify.
-    POLYUNSIGNED fileLength =
-        Poly_string_to_C(DEREFHANDLE(args)->Get(0), fileNameBuff, MAXPATHLEN);
-    if (fileLength > MAXPATHLEN)
-        raise_syscall(taskData, "File name too long", ENAMETOOLONG);
+    AutoFree<TCHAR*> fileNameBuff(Poly_string_to_C_alloc(DEREFHANDLE(args)->Get(0)));
+    if (fileNameBuff == NULL)
+        raise_syscall(taskData, "Insufficient memory", ENOMEM);
     // The new parent name to insert.
-    POLYUNSIGNED parentLength =
-        Poly_string_to_C(DEREFHANDLE(args)->Get(1), parentNameBuff, MAXPATHLEN);
-    if (parentLength > MAXPATHLEN)
-        raise_syscall(taskData, "Parent name too long", ENAMETOOLONG);
+    AutoFree<TCHAR*> parentNameBuff(Poly_string_to_C_alloc(DEREFHANDLE(args)->Get(1)));
+    if (parentNameBuff == NULL)
+        raise_syscall(taskData, "Insufficient memory", ENOMEM);
 
     AutoClose loadFile(_tfopen(fileNameBuff, _T("r+b"))); // Open for reading and writing
     if ((FILE*)loadFile == NULL)
     {
-        char buff[MAXPATHLEN+1+23];
+        AutoFree<char*> buff((char *)malloc(23 + _tcslen(fileNameBuff) * sizeof(TCHAR) + 1));
 #if (defined(_WIN32) && defined(UNICODE))
-        sprintf(buff, "Cannot open load file: %S", fileNameBuff);
+        sprintf(buff, "Cannot open load file: %S", (TCHAR *)fileNameBuff);
 #else
-        sprintf(buff, "Cannot open load file: %s", fileNameBuff);
+        sprintf(buff, "Cannot open load file: %s", (TCHAR *)fileNameBuff);
 #endif
         raise_syscall(taskData, buff, errno);
     }
@@ -1203,20 +1201,18 @@
 Handle ShowParent(TaskData *taskData, Handle hFileName)
 // Return the name of the immediate parent stored in a child
 {
-    TCHAR fileNameBuff[MAXPATHLEN+1];
-    POLYUNSIGNED length =
-        Poly_string_to_C(DEREFHANDLE(hFileName), fileNameBuff, MAXPATHLEN);
-    if (length > MAXPATHLEN)
-        raise_syscall(taskData, "File name too long", ENAMETOOLONG);
+    AutoFree<TCHAR*> fileNameBuff(Poly_string_to_C_alloc(DEREFHANDLE(hFileName)));
+    if (fileNameBuff == NULL)
+        raise_syscall(taskData, "Insufficient memory", ENOMEM);
 
     AutoClose loadFile(_tfopen(fileNameBuff, _T("rb")));
     if ((FILE*)loadFile == NULL)
     {
-        char buff[MAXPATHLEN+1+23];
+        AutoFree<char*> buff((char *)malloc(23 + _tcslen(fileNameBuff) * sizeof(TCHAR) + 1));
 #if (defined(_WIN32) && defined(UNICODE))
-        sprintf(buff, "Cannot open load file: %S", fileNameBuff);
+        sprintf(buff, "Cannot open load file: %S", (TCHAR *)fileNameBuff);
 #else
-        sprintf(buff, "Cannot open load file: %s", fileNameBuff);
+        sprintf(buff, "Cannot open load file: %s", (TCHAR *)fileNameBuff);
 #endif
         raise_syscall(taskData, buff, errno);
     }
@@ -1239,9 +1235,13 @@
     // Does this have a parent?
     if (header.parentNameEntry != 0)
     {
-        TCHAR parentFileName[MAXPATHLEN+1];
         size_t toRead = header.stringTableSize-header.parentNameEntry;
-        if (MAXPATHLEN < toRead) toRead = MAXPATHLEN;
+        size_t elems = ((toRead + sizeof(TCHAR) - 1) / sizeof(TCHAR));
+        // Always allow space for null terminator
+        size_t roundedBytes = (elems + 1) * sizeof(TCHAR);
+        AutoFree<TCHAR*> parentFileName((TCHAR *)malloc(roundedBytes));
+        if (parentFileName == NULL)
+            raise_syscall(taskData, "Insufficient memory", ENOMEM);
 
         if (header.parentNameEntry >= header.stringTableSize /* Bad entry */ ||
             fseek(loadFile, header.stringTable + header.parentNameEntry, SEEK_SET) != 0 ||
@@ -1249,7 +1249,7 @@
         {
             raise_fail(taskData, "Unable to read parent file name");
         }
-        parentFileName[toRead] = 0; // Should already be null-terminated, but just in case.
+        parentFileName[elems] = 0; // Should already be null-terminated, but just in case.
         // Convert the name into a Poly string and then build a "Some" value.
         // It's possible, although silly, to have the empty string as a parent name.
         Handle resVal = SAVE(C_string_to_Poly(taskData, parentFileName));
@@ -1599,7 +1599,7 @@
             raise_fail(taskData, loader.errorResult);
         else
         {
-            char buff[MAXPATHLEN+100];
+            AutoFree<char*> buff((char *)malloc(strlen(loader.errorResult) + 2 + _tcslen(loader.fileName) * sizeof(TCHAR) + 1));
 #if (defined(_WIN32) && defined(UNICODE))
             sprintf(buff, "%s: %S", loader.errorResult, loader.fileName);
 #else
--- a/libpolyml/statistics.cpp
+++ b/libpolyml/statistics.cpp
@@ -669,13 +669,26 @@
     return result;
 #elif HAVE_MMAP
     // Find the shared memory in the user's home directory
-    int remMapFd = -1;
-    char remMapFileName[MAXPATHLEN];
-    remMapFileName[0] = 0;
     char *homeDir = getenv("HOME");
     if (homeDir == NULL)
         raise_exception_string(taskData, EXC_Fail, "No statistics available");
-    sprintf(remMapFileName, "%s/.polyml/" POLY_STATS_NAME "%" POLYUFMT, homeDir, pid);
+
+    int remMapFd = -1;
+    size_t remMapSize = 4096;
+    TempCString remMapFileName((char *)malloc(remMapSize));
+    if (remMapFileName == NULL)
+        raise_exception_string(taskData, EXC_Fail, "No statistics available");
+
+    while ((snprintf(remMapFileName, remMapSize, "%s/.polyml/" POLY_STATS_NAME "%" POLYUFMT, homeDir, pid), strlen(remMapFileName) >= remMapSize - 1)) {
+        if (remMapSize > SIZE_MAX / 2)
+            raise_exception_string(taskData, EXC_Fail, "No statistics available");
+        remMapSize *= 2;
+        char *newFileName = (char *)realloc(remMapFileName, remMapSize);
+        if (newFileName == NULL)
+            raise_exception_string(taskData, EXC_Fail, "No statistics available");
+        remMapFileName = newFileName;
+    }
+
     remMapFd = open(remMapFileName, O_RDONLY);
     if (remMapFd == -1)
         raise_exception_string(taskData, EXC_Fail, "No statistics available");
