Added a warning system to Lua

The warning system is just a way for Lua to emit warnings, messages
to the programmer that do not interfere with the running program.
diff --git a/lapi.c b/lapi.c
index 2d10bb7..0f0166e 100644
--- a/lapi.c
+++ b/lapi.c
@@ -1267,6 +1267,24 @@
 }
 
 
+void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
+  lua_lock(L);
+  G(L)->ud_warn = ud;
+  G(L)->warnf = f;
+  lua_unlock(L);
+}
+
+
+void lua_warning (lua_State *L, const char *msg) {
+  lua_WarnFunction wf = G(L)->warnf;
+  lua_lock(L);
+  if (wf != NULL)
+    wf(&G(L)->ud_warn, msg);
+  lua_unlock(L);
+}
+
+
+
 LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
   Udata *u;
   lua_lock(L);
diff --git a/lauxlib.c b/lauxlib.c
index 769586b..abf923f 100644
--- a/lauxlib.c
+++ b/lauxlib.c
@@ -986,9 +986,35 @@
 }
 
 
+/*
+** checks whether 'message' ends with end-of-line
+** (and therefore is the last part of a warning)
+*/
+static int islast (const char *message) {
+  size_t len = strlen(message);
+  return (len > 0 && message[len - 1] == '\n');
+}
+
+
+/*
+** Emit a warning. If '*pud' is NULL, previous message was to be
+** continued by the current one.
+*/
+static void warnf (void **pud, const char *message) {
+  if (*pud == NULL)  /* previous message was not the last? */
+    lua_writestringerror("%s", message);
+  else  /* start a new warning */
+    lua_writestringerror("Lua warning: %s", message);
+  *pud = (islast(message)) ? pud : NULL;
+}
+
+
 LUALIB_API lua_State *luaL_newstate (void) {
   lua_State *L = lua_newstate(l_alloc, NULL);
-  if (L) lua_atpanic(L, &panic);
+  if (L) {
+    lua_atpanic(L, &panic);
+    lua_setwarnf(L, warnf, L);
+  }
   return L;
 }
 
diff --git a/lbaselib.c b/lbaselib.c
index 7c0ebfe..26683a1 100644
--- a/lbaselib.c
+++ b/lbaselib.c
@@ -43,6 +43,13 @@
 }
 
 
+static int luaB_warn (lua_State *L) {
+  const char *msg = luaL_checkstring(L, 1);
+  lua_warning(L, msg);
+  return 0;
+}
+
+
 #define SPACECHARS	" \f\n\r\t\v"
 
 static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
@@ -482,6 +489,7 @@
   {"pairs", luaB_pairs},
   {"pcall", luaB_pcall},
   {"print", luaB_print},
+  {"warn", luaB_warn},
   {"rawequal", luaB_rawequal},
   {"rawlen", luaB_rawlen},
   {"rawget", luaB_rawget},
diff --git a/lstate.c b/lstate.c
index 04a9e06..b3e9ec6 100644
--- a/lstate.c
+++ b/lstate.c
@@ -365,6 +365,8 @@
   L->next = NULL;
   g->frealloc = f;
   g->ud = ud;
+  g->warnf = NULL;
+  g->ud_warn = NULL;
   g->mainthread = L;
   g->seed = luai_makeseed(L);
   g->gcrunning = 0;  /* no GC while building state */
diff --git a/lstate.h b/lstate.h
index b069b39..f379325 100644
--- a/lstate.h
+++ b/lstate.h
@@ -231,6 +231,8 @@
   TString *tmname[TM_N];  /* array with tag-method names */
   struct Table *mt[LUA_NUMTAGS];  /* metatables for basic types */
   TString *strcache[STRCACHE_N][STRCACHE_M];  /* cache for strings in API */
+  lua_WarnFunction warnf;  /* warning function */
+  void *ud_warn;         /* auxiliary data to 'warnf' */
 } global_State;
 
 
diff --git a/ltests.c b/ltests.c
index 0b5ed90..5ea8b08 100644
--- a/ltests.c
+++ b/ltests.c
@@ -63,10 +63,36 @@
 }
 
 
+static void badexit (void) {
+  /* avoid assertion failures when exiting */
+  l_memcontrol.numblocks = l_memcontrol.total = 0;
+  exit(EXIT_FAILURE);
+}
+
+
 static int tpanic (lua_State *L) {
   fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
                    lua_tostring(L, -1));
-  return (exit(EXIT_FAILURE), 0);  /* do not return to Lua */
+  return (badexit(), 0);  /* do not return to Lua */
+}
+
+
+static int islast (const char *message) {
+  size_t len = strlen(message);
+  return (len > 0 && message[len - 1] == '\n');
+}
+
+
+static void warnf (void **pud, const char *msg) {
+  if (*pud == NULL)  /* continuation line? */
+    printf("%s", msg);  /* print it */
+  else if (msg[0] == '*')  /* expected warning? */
+    printf("Expected Lua warning: %s", msg + 1);  /* print without the star */
+  else {  /* a real warning; should not happen during tests */
+    fprintf(stderr, "Warning in test mode (%s), aborting...\n", msg);
+    badexit();
+  }
+  *pud = islast(msg) ? pud : NULL;
 }
 
 
@@ -1405,6 +1431,10 @@
       const char *msg = getstring;
       printf("%s\n", msg);
     }
+    else if EQ("warning") {
+      const char *msg = getstring;
+      lua_warning(L1, msg);
+    }
     else if EQ("pushbool") {
       lua_pushboolean(L1, getnum);
     }
@@ -1743,6 +1773,7 @@
 int luaB_opentests (lua_State *L) {
   void *ud;
   lua_atpanic(L, &tpanic);
+  lua_setwarnf(L, &warnf, L);
   atexit(checkfinalmem);
   lua_assert(lua_getallocf(L, &ud) == debug_realloc);
   lua_assert(ud == cast_voidp(&l_memcontrol));
diff --git a/lua.h b/lua.h
index 95ce5a2..a6f8268 100644
--- a/lua.h
+++ b/lua.h
@@ -126,6 +126,13 @@
 typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
 
 
+/*
+** Type for warning functions
+*/
+typedef void (*lua_WarnFunction) (void **pud, const char *msg);
+
+
+
 
 /*
 ** generic extra include file
@@ -300,6 +307,13 @@
 
 
 /*
+** Warning-related functions
+*/
+LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
+LUA_API void (lua_warning)  (lua_State *L, const char *msg);
+
+
+/*
 ** garbage-collection function and options
 */
 
diff --git a/manual/manual.of b/manual/manual.of
index 044bd09..196ea1e 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -1795,7 +1795,7 @@
 (different behavior, different definition) are always different.
 Functions created at different times but with no detectable differences
 may be classified as equal or not
-(depending on internal cashing details).
+(depending on internal caching details).
 
 You can change the way that Lua compares tables and userdata
 by using the @idx{__eq} metamethod @see{metatable}.
@@ -4033,6 +4033,16 @@
 
 }
 
+@APIEntry{int lua_setiuservalue (lua_State *L, int index, int n);|
+@apii{1,0,-}
+
+Pops a value from the stack and sets it as
+the new @id{n}-th user value associated to the
+full userdata at the given index.
+Returns 0 if the userdata does not have that value.
+
+}
+
 @APIEntry{void lua_setmetatable (lua_State *L, int index);|
 @apii{1,0,-}
 
@@ -4066,13 +4076,13 @@
 
 }
 
-@APIEntry{int lua_setiuservalue (lua_State *L, int index, int n);|
-@apii{1,0,-}
+@APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);|
+@apii{0,0,-}
 
-Pops a value from the stack and sets it as
-the new @id{n}-th user value associated to the
-full userdata at the given index.
-Returns 0 if the userdata does not have that value.
+Sets the @x{warning function} to be used by Lua to emit warnings
+@see{lua_WarnFunction}.
+The @id{ud} parameter initializes the slot @id{pud} passed to
+the warning function.
 
 }
 
@@ -4335,6 +4345,30 @@
 }
 
 @APIEntry{
+typedef void (*lua_WarnFunction) (void **pud, const char *msg);|
+
+The type of @x{warning function}s, called by Lua to emit warnings.
+The first parameter is the address of a writable slot,
+constant for a given Lua state and
+initialized by @Lid{lua_setwarnf}.
+The second parameter is the warning message.
+This function should assume that
+a message not ending with an end-of-line will be
+continued by the message in the next call.
+
+}
+
+@APIEntry{
+void lua_warning (lua_State *L, const char *msg);|
+@apii{0,0,-}
+
+Emits a warning with the given message.
+A message not ending with an end-of-line should be
+continued in another call to this function.
+
+}
+
+@APIEntry{
 typedef int (*lua_Writer) (lua_State *L,
                            const void* p,
                            size_t sz,
@@ -4345,7 +4379,7 @@
 @Lid{lua_dump} calls the writer,
 passing along the buffer to be written (@id{p}),
 its size (@id{sz}),
-and the @id{data} parameter supplied to @Lid{lua_dump}.
+and the @id{ud} parameter supplied to @Lid{lua_dump}.
 
 The writer returns an error code:
 @N{0 means} no errors;
@@ -6261,6 +6295,12 @@
 
 }
 
+@LibEntry{warn (message)|
+
+Emits a warning with the given message.
+
+}
+
 @LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
 
 This function is similar to @Lid{pcall},
diff --git a/testes/all.lua b/testes/all.lua
index 84ba80a..bde4195 100644
--- a/testes/all.lua
+++ b/testes/all.lua
@@ -5,8 +5,8 @@
 
 local version = "Lua 5.4"
 if _VERSION ~= version then
-  io.stderr:write("\nThis test suite is for ", version, ", not for ", _VERSION,
-    "\nExiting tests\n")
+  warn(string.format(
+   "This test suite is for %s, not for %s\nExiting tests\n", version, _VERSION))
   return
 end
 
@@ -190,11 +190,10 @@
 dofile('files.lua')
 
 if #msgs > 0 then
-  print("\ntests not performed:")
+  warn("*tests not performed:\n  ")
   for i=1,#msgs do
-    print(msgs[i])
+    warn(msgs[i]); warn("\n  ")
   end
-  print()
 end
 
 -- no test module should define 'debug'
@@ -220,6 +219,10 @@
 local fname = T and "time-debug.txt" or "time.txt"
 local lasttime
 
+
+warn("*This is "); warn("an expected"); warn(" warning\n")
+warn("*This is"); warn(" another one\n")
+
 if not usertests then
   -- open file with time of last performed test
   local f = io.open(fname)
diff --git a/testes/api.lua b/testes/api.lua
index 6f35e13..b4d6386 100644
--- a/testes/api.lua
+++ b/testes/api.lua
@@ -111,6 +111,20 @@
   tcheck(t, {10, 20, 30, 40})
 end
 
+
+-- testing warnings
+T.testC([[
+  warning "*This "
+  warning "warning "
+  warning "should be in a"
+  warning " single line
+"
+  warning "*This should be "
+  warning "another warning
+"
+]])
+
+
 -- testing message handlers
 do
   local f = T.makeCfunc[[