Avoid the creation of too many strings in 'package'

Both when setting a path and searching for a file ('searchpath'),
this commit reduces the number of intermediate strings created
in Lua.
(For setting a path the change is not relevant, because this is
done only twice when loading the module. Anyway, it is a nice example
of how to use auxlib buffers to manipulate strings in the C API.)
diff --git a/lgc.h b/lgc.h
index 9ba7ecb..b972472 100644
--- a/lgc.h
+++ b/lgc.h
@@ -127,7 +127,7 @@
 
 /*
 ** some gc parameters are stored divided by 4 to allow a maximum value
-** larger than 1000 in a 'lu_byte'.
+** up to 1023 in a 'lu_byte'.
 */
 #define getgcparam(p)	((p) * 4)
 #define setgcparam(p,v)	((p) = (v) / 4)
diff --git a/llimits.h b/llimits.h
index cc98397..febf755 100644
--- a/llimits.h
+++ b/llimits.h
@@ -39,7 +39,7 @@
 /* maximum value for size_t */
 #define MAX_SIZET	((size_t)(~(size_t)0))
 
-/* maximum size visible for Lua (must be representable in a lua_Integer */
+/* maximum size visible for Lua (must be representable in a lua_Integer) */
 #define MAX_SIZE	(sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
                           : (size_t)(LUA_MAXINTEGER))
 
diff --git a/loadlib.c b/loadlib.c
index 4cf9aec..ff73a45 100644
--- a/loadlib.c
+++ b/loadlib.c
@@ -290,22 +290,33 @@
 static void setpath (lua_State *L, const char *fieldname,
                                    const char *envname,
                                    const char *dft) {
+  const char *dftmark;
   const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX);
-  const char *path = getenv(nver);  /* use versioned name */
-  if (path == NULL)  /* no environment variable? */
+  const char *path = getenv(nver);  /* try versioned name */
+  if (path == NULL)  /* no versioned environment variable? */
     path = getenv(envname);  /* try unversioned name */
   if (path == NULL || noenv(L))  /* no environment variable? */
     lua_pushstring(L, dft);  /* use default */
-  else {
-    /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
-    path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP,
-                              LUA_PATH_SEP AUXMARK LUA_PATH_SEP);
-    luaL_gsub(L, path, AUXMARK, dft);
-    lua_remove(L, -2); /* remove result from 1st 'gsub' */
+  else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL)
+    lua_pushstring(L, path);  /* nothing to change */
+  else {  /* path contains a ";;": insert default path in its place */
+    size_t len = strlen(path);
+    luaL_Buffer b;
+    luaL_buffinit(L, &b);
+    if (path < dftmark) {  /* is there a prefix before ';;'? */
+      luaL_addlstring(&b, path, dftmark - path);  /* add it */
+      luaL_addchar(&b, *LUA_PATH_SEP);
+    }
+    luaL_addstring(&b, dft);  /* add default */
+    if (dftmark < path + len - 2) {  /* is there a sufix after ';;'? */
+      luaL_addchar(&b, *LUA_PATH_SEP);
+      luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
+    }
+    luaL_pushresult(&b);
   }
   setprogdir(L);
   lua_setfield(L, -3, fieldname);  /* package[fieldname] = path value */
-  lua_pop(L, 1);  /* pop versioned variable name */
+  lua_pop(L, 1);  /* pop versioned variable name ('nver') */
 }
 
 /* }================================================================== */
@@ -421,17 +432,26 @@
 }
 
 
-static const char *pushnextfilename (lua_State *L, const char *path) {
-  const char *l;
-  if (*path == *LUA_PATH_SEP)
-    path++;  /* skip separator */
-  if (*path == '\0')
+/*
+** Get the next name in '*path' = 'name1;name2;name3;...', changing
+** the ending ';' to '\0' to create a zero-terminated string. Return
+** NULL when list ends.
+*/
+static const char *getnextfilename (char **path, char *end) {
+  char *sep;
+  char *name = *path;
+  if (name == end)
     return NULL;  /* no more names */
-  l = strchr(path, *LUA_PATH_SEP);  /* find next separator */
-  if (l == NULL)  /* no more separators? */
-    l = path + strlen(path);  /* go until the end */
-  lua_pushlstring(L, path, l - path);  /* file name */
-  return l;  /* rest of the path */
+  else if (*name == '\0') {  /* from previous iteration? */
+    *name = *LUA_PATH_SEP;  /* restore separator */
+    name++;  /* skip it */
+  }
+  sep = strchr(name, *LUA_PATH_SEP);  /* find next separator */
+  if (sep == NULL)  /* separator not found? */
+    sep = end;  /* name goes until the end */
+  *sep = '\0';  /* finish file name */
+  *path = sep;  /* will start next search from here */
+  return name;
 }
 
 
@@ -442,12 +462,12 @@
 **	no file 'blublu.so'
 */
 static void pusherrornotfound (lua_State *L, const char *path) {
-  if (*path == *LUA_PATH_SEP)
-    path++;  /* skip separator */
-  lua_pushstring(L, "\n\tno file '");
-  luaL_gsub(L, path, LUA_PATH_SEP, "'\n\tno file '");
-  lua_pushstring(L, "'");
-  lua_concat(L, 3);
+  luaL_Buffer b;
+  luaL_buffinit(L, &b);
+  luaL_addstring(&b, "\n\tno file '");
+  luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
+  luaL_addstring(&b, "'");
+  luaL_pushresult(&b);
 }
 
 
@@ -455,17 +475,24 @@
                                              const char *path,
                                              const char *sep,
                                              const char *dirsep) {
+  luaL_Buffer buff;
+  char *pathname;  /* path with name inserted */
+  char *endpathname;  /* its end */
+  const char *filename;
   /* separator is non-empty and appears in 'name'? */
   if (*sep != '\0' && strchr(name, *sep) != NULL)
     name = luaL_gsub(L, name, sep, dirsep);  /* replace it by 'dirsep' */
-  /* replace marks ('?') in 'path' by the file name */
-  path = luaL_gsub(L, path, LUA_PATH_MARK, name);
-  while ((path = pushnextfilename(L, path)) != NULL) {
-    const char *filename = lua_tostring(L, -1);
+  luaL_buffinit(L, &buff);
+  /* add path to the buffer, replacing marks ('?') with the file name */
+  luaL_addgsub(&buff, path, LUA_PATH_MARK, name);
+  luaL_addchar(&buff, '\0');
+  pathname = luaL_buffaddr(&buff);  /* writable list of file names */
+  endpathname = pathname + luaL_bufflen(&buff) - 1;
+  while ((filename = getnextfilename(&pathname, endpathname)) != NULL) {
     if (readable(filename))  /* does file exist and is readable? */
-      return filename;  /* return that file name */
-    lua_pop(L, 1);  /* else remove file name */
+      return lua_pushstring(L, filename);  /* save and return name */
   }
+  luaL_pushresult(&buff);  /* push path to create error message */
   pusherrornotfound(L, lua_tostring(L, -1));  /* create error message */
   return NULL;  /* not found */
 }
diff --git a/testes/main.lua b/testes/main.lua
index b9dcab1..aab490c 100644
--- a/testes/main.lua
+++ b/testes/main.lua
@@ -142,12 +142,18 @@
   prepfile("print(package.path, package.cpath)")
   RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
        prog, out)
+  local output = getoutput()
+  defaultpath = string.match(output, "^(.-)\t")
+  defaultCpath = string.match(output, "\t(.-)$")
+
+  -- running with an empty environment
+  RUN('env -i lua %s > %s', prog, out)
   local out = getoutput()
-  defaultpath = string.match(out, "^(.-)\t")
-  defaultCpath = string.match(out, "\t(.-)$")
+  assert(defaultpath == string.match(output, "^(.-)\t"))
+  assert(defaultCpath == string.match(output, "\t(.-)$"))
 end
 
--- paths did not changed
+-- paths did not change
 assert(not string.find(defaultpath, "xxx") and
        string.find(defaultpath, "lua") and
        not string.find(defaultCpath, "xxx") and
@@ -160,15 +166,20 @@
   RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
   local expected = getoutput()
   expected = string.sub(expected, 1, -2)   -- cut final end of line
-  assert(string.gsub(p, ";;", ";"..defaultpath..";") == expected)
+  if string.find(p, ";;") then
+    p = string.gsub(p, ";;", ";"..defaultpath..";")
+    p = string.gsub(p, "^;", "")   -- remove ';' at the beginning
+    p = string.gsub(p, ";$", "")   -- remove ';' at the end
+  end
+  assert(p == expected)
 end
 
 convert(";")
 convert(";;")
-convert(";;;")
-convert(";;;;")
-convert(";;;;;")
-convert(";;a;;;bc")
+convert("a;;b")
+convert(";;b")
+convert("a;;")
+convert("a;b;;c")
 
 
 -- test -l over multiple libraries