Merge branch 'more_argcargv'

* more_argcargv:
  Document argc argv library
  argcargv.i cosmetic updates
  Typemaps for (int ARGC, char **ARGV) fixup
  Fix argcargv.i in Perl5, Tcl, PHP Add missing type map for type check. Add testing of argcargv.i for Perl5, Tcl, PHP and Ruby.
  Add Lua test for argcargv.i
  Add argcargv.i to more languages: Perl 5, Tcl, PHP
  Add argcargv.i to Lua
diff --git a/CHANGES.current b/CHANGES.current
index a8975a0..32ccc4f 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,12 @@
 Version 4.1.0 (in progress)
 ===========================
 
+2022-05-15: erezgeva, eiselekd
+            [Lua, Perl, PHP, Tcl] #2275 #2276 Add argcargv.i library containing
+            (int ARGC, char **ARGV) multi-argument typemaps.
+
+            Document this library in Typemaps.html.
+
 2022-05-07: KrisThielemans
             [Python] Fix "too many initializers for 'PyHeapTypeObject'" errors
             using PyPy 3.8 and later.
diff --git a/Doc/Manual/Contents.html b/Doc/Manual/Contents.html
index 57ef770..dfa9f61 100644
--- a/Doc/Manual/Contents.html
+++ b/Doc/Manual/Contents.html
@@ -419,6 +419,7 @@
 <li><a href="Library.html#Library_nn2">The %include directive and library search path</a>
 <li><a href="Library.html#Library_nn3">C arrays and pointers</a>
 <ul>
+<li><a href="Library.html#Library_argcargv">argcargv.i</a>
 <li><a href="Library.html#Library_nn4">cpointer.i</a>
 <li><a href="Library.html#Library_carrays">carrays.i</a>
 <li><a href="Library.html#Library_nn6">cmalloc.i</a>
diff --git a/Doc/Manual/Library.html b/Doc/Manual/Library.html
index 0efb8fb..3ecfc3a 100644
--- a/Doc/Manual/Library.html
+++ b/Doc/Manual/Library.html
@@ -14,6 +14,7 @@
 <li><a href="#Library_nn2">The %include directive and library search path</a>
 <li><a href="#Library_nn3">C arrays and pointers</a>
 <ul>
+<li><a href="#Library_argcargv">argcargv.i</a>
 <li><a href="#Library_nn4">cpointer.i</a>
 <li><a href="#Library_carrays">carrays.i</a>
 <li><a href="#Library_nn6">cmalloc.i</a>
@@ -115,7 +116,48 @@
 memory, their use is potentially unsafe and you should exercise caution.
 </p>
 
-<H3><a name="Library_nn4">12.2.1 cpointer.i</a></H3>
+<H3><a name="Library_argcargv">12.2.1 argcargv.i</a></H3>
+
+
+<p>
+The argcargv.i library is a simple library providing multi-argument typemaps for handling C
+argc argv command line argument C string arrays.
+The <tt>argc</tt> parameter contains the argument count and <tt>argv</tt> contains the argument vector array.
+</p>
+
+<p>
+This library provides the following multi-argument typemap:
+</p>
+
+<p>
+<b><tt>(int ARGC, char **ARGV)</tt></b>
+</p>
+
+<p>
+Apply this multi-argument typemap to your use case, for example:
+</p>
+
+<div class="code">
+<pre>
+%apply (int ARGC, char **ARGV) { (size_t argc, const char **argv) }
+
+int mainApp(size_t argc, const char **argv);
+</pre>
+</div>
+
+<p>
+then from Ruby:
+</p>
+
+<div class="targetlang">
+<pre>
+$args = ["myarg1", "myarg2"]
+mainApp(args);
+</pre>
+</div>
+
+
+<H3><a name="Library_nn4">12.2.2 cpointer.i</a></H3>
 
 
 <p>
@@ -331,7 +373,7 @@
 <b>Note:</b> When working with simple pointers, typemaps can often be used to provide more seamless operation.
 </p>
 
-<H3><a name="Library_carrays">12.2.2 carrays.i</a></H3>
+<H3><a name="Library_carrays">12.2.3 carrays.i</a></H3>
 
 
 <p>
@@ -510,7 +552,7 @@
 SWIG's default handling of these types is to handle them as character strings and the two macros do not do enough to change this.
 </p>
 
-<H3><a name="Library_nn6">12.2.3 cmalloc.i</a></H3>
+<H3><a name="Library_nn6">12.2.4 cmalloc.i</a></H3>
 
 
 <p>
@@ -671,7 +713,7 @@
 </pre>
 </div>
 
-<H3><a name="Library_nn7">12.2.4 cdata.i</a></H3>
+<H3><a name="Library_nn7">12.2.5 cdata.i</a></H3>
 
 
 <p>
diff --git a/Examples/test-suite/argcargvtest.i b/Examples/test-suite/argcargvtest.i
index ed5aa09..d87ce68 100644
--- a/Examples/test-suite/argcargvtest.i
+++ b/Examples/test-suite/argcargvtest.i
@@ -1,8 +1,10 @@
 %module argcargvtest
 
+#if !defined(SWIGCSHARP) && !defined(SWIGD) && !defined(SWIGGO) && !defined(SWIGGUILE) && !defined(SWIGJAVA) && !defined(SWIGJAVASCRIPT) && !defined(SWIGMZSCHEME) && !defined(SWIGOCTAVE) && !defined(SWIGR) && !defined(SWIGSCILAB)
 %include <argcargv.i>
 
 %apply (int ARGC, char **ARGV) { (size_t argc, const char **argv) }
+#endif
 
 %inline %{
 
diff --git a/Examples/test-suite/lua/argcargvtest_runme.lua b/Examples/test-suite/lua/argcargvtest_runme.lua
new file mode 100644
index 0000000..7f21366
--- /dev/null
+++ b/Examples/test-suite/lua/argcargvtest_runme.lua
@@ -0,0 +1,26 @@
+require("import")	-- the import fn
+import("argcargvtest")	-- import lib
+v = argcargvtest
+
+-- catch "undefined" global variables
+local env = _ENV -- Lua 5.2
+if not env then env = getfenv () end -- Lua 5.1
+setmetatable(env, {__index=function (t,i) error("undefined global variable `"..i.."'",2) end})
+
+largs = {"hi", "hola", "hello"}
+assert(v.mainc(largs) == 3, "bad main typemap")
+
+targs = {"hi", "hola"}
+assert(v.mainv(targs, 1) == "hola", "bad main typemap")
+
+targs = {"hi", "hola"}
+assert(v.mainv(targs, 1) == "hola", "bad main typemap")
+
+errorVal = 0
+function try()
+    mainv("hello", 1)
+    errorVal = 1
+end
+assert(not pcall(try) and errorVal == 0, "bad main typemap")
+
+v.initializeApp(largs)
diff --git a/Examples/test-suite/perl5/argcargvtest_runme.pl b/Examples/test-suite/perl5/argcargvtest_runme.pl
new file mode 100644
index 0000000..c4157eb
--- /dev/null
+++ b/Examples/test-suite/perl5/argcargvtest_runme.pl
@@ -0,0 +1,20 @@
+use strict;
+use warnings;
+use Test::More tests => 8;
+BEGIN { use_ok('argcargvtest') }
+require_ok('argcargvtest');
+
+my $largs = ["hi", "hola", "hello"];
+is(argcargvtest::mainc($largs), 3, "test main typemap 1");
+
+my $targs = ["hi", "hola"];
+is(argcargvtest::mainv($targs, 1), "hola", "test main typemap 2");
+
+my $errorVal = 0;
+my $ret = eval qq(argcargvtest::mainv("hello", 1); \$errorVal = 1;);
+is($ret, undef, "test main typemap 3");
+is($errorVal, 0, "test main typemap 4");
+
+is(argcargvtest::initializeApp($largs), undef, "test main typemap 5");
+
+ok(1, "done");
diff --git a/Examples/test-suite/php/argcargvtest_runme.php b/Examples/test-suite/php/argcargvtest_runme.php
new file mode 100644
index 0000000..4a675d0
--- /dev/null
+++ b/Examples/test-suite/php/argcargvtest_runme.php
@@ -0,0 +1,29 @@
+<?php
+
+require "tests.php";
+
+// New functions
+check::functions(array('mainc', 'mainv', 'initializeapp'));
+// New classes
+check::classes(array('argcargvtest'));
+// No new vars
+check::globals(array());
+
+$largs = array('hi', 'hola', 'hello');
+check::equal(mainc($largs), 3, 'Test main typemap 1');
+
+$targs = array('hi', 'hola');
+check::equal(mainv($targs, 1), 'hola', 'Test main typemap 2');
+
+$error = 0;
+try {
+    mainv('hello', 1);
+    $error = 1;
+}
+catch (exception $e) {
+}
+check::equal($error, 0, 'Test main typemap 3');
+
+initializeApp($largs);
+
+check::done();
diff --git a/Examples/test-suite/ruby/argcargvtest_runme.rb b/Examples/test-suite/ruby/argcargvtest_runme.rb
new file mode 100644
index 0000000..8f4cd7d
--- /dev/null
+++ b/Examples/test-suite/ruby/argcargvtest_runme.rb
@@ -0,0 +1,32 @@
+#!/usr/bin/env ruby
+
+require 'swig_assert'
+require 'argcargvtest'
+
+include Argcargvtest
+
+
+$largs = ["hi", "hola", "hello"]
+if mainc($largs) != 3
+    raise RuntimeError, "bad main typemap"
+end
+
+$targs = ["hi", "hola"]
+if mainv($targs, 1) != "hola"
+    raise RuntimeError, "bad main typemap"
+end
+
+$error = 0
+$ret = 0
+begin
+    mainv("hello", 1)
+    $error = 1
+rescue => e
+    $ret = 1
+end
+
+if $error == 1 or $ret != 1
+    raise RuntimeError, "bad main typemap"
+end
+
+initializeApp($largs)
diff --git a/Examples/test-suite/tcl/argcargvtest_runme.tcl b/Examples/test-suite/tcl/argcargvtest_runme.tcl
new file mode 100644
index 0000000..14c0e92
--- /dev/null
+++ b/Examples/test-suite/tcl/argcargvtest_runme.tcl
@@ -0,0 +1,28 @@
+if [ catch { load ./argcargvtest[info sharedlibextension] argcargvtest} err_msg ] {
+	puts stderr "Could not load shared object:\n$err_msg"
+}
+
+set largs {hi hola hello}
+if {[mainc $largs] != 3} {
+     puts stderr "bad main typemap"
+     exit 1
+}
+
+set targs {hi hola}
+if {[mainv $targs 1] != "hola"} {
+     puts stderr "bad main typemap"
+     exit 1
+}
+
+set targs " hi hola "
+if {[mainv $targs 1] != "hola"} {
+     puts stderr "bad main typemap"
+     exit 1
+}
+
+if { ! [ catch { mainv("hello", 1) } ] } {
+    puts stderr "bad main typemap"
+    exit 1
+}
+
+initializeApp $largs
diff --git a/Lib/lua/argcargv.i b/Lib/lua/argcargv.i
new file mode 100644
index 0000000..94cc8ed
--- /dev/null
+++ b/Lib/lua/argcargv.i
@@ -0,0 +1,57 @@
+/* ------------------------------------------------------------
+ * SWIG library containing argc and argv multi-argument typemaps
+
+   Use it as follows:
+
+     %apply (int ARGC, char **ARGV) { (size_t argc, const char **argv) }
+     extern int mainApp(size_t argc, const char **argv);
+
+   then from lua:
+
+     args = { "arg0", "arg1" }
+     mainApp(args)
+
+ * ------------------------------------------------------------ */
+
+%{
+SWIGINTERN int SWIG_argv_size(lua_State* L, int index) {
+  int n=0;
+  while(1){
+    lua_rawgeti(L,index,n+1);
+    if (lua_isnil(L,-1))
+      break;
+    ++n;
+    lua_pop(L,1);
+  }
+  lua_pop(L,1);
+  return n;
+}
+%}
+
+%typemap(in) (int ARGC, char **ARGV) {
+  if (lua_istable(L,$input)) {
+    int i, size = SWIG_argv_size(L,$input);
+    $1 = ($1_ltype) size;
+    $2 = (char **) malloc((size+1)*sizeof(char *));
+    for (i = 0; i < size; i++) {
+      lua_rawgeti(L,$input,i+1);
+      if (lua_isnil(L,-1))
+   break;
+      $2[i] = (char *)lua_tostring(L, -1);
+      lua_pop(L,1);
+    }
+    $2[i]=NULL;
+  } else {
+    $1 = 0; $2 = 0;
+    lua_pushstring(L,"Expecting argv array");
+    lua_error(L);
+  }
+}
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_STRING_ARRAY) (int ARGC, char **ARGV) {
+  $1 = lua_istable(L,$input);
+}
+
+%typemap(freearg) (int ARGC, char **ARGV) {
+  free((char *) $2);
+}
diff --git a/Lib/perl5/argcargv.i b/Lib/perl5/argcargv.i
new file mode 100644
index 0000000..48a6047
--- /dev/null
+++ b/Lib/perl5/argcargv.i
@@ -0,0 +1,32 @@
+/* ------------------------------------------------------------
+ * SWIG library containing argc and argv multi-argument typemaps
+ * ------------------------------------------------------------ */
+
+%typemap(in) (int ARGC, char **ARGV) {
+  int i;
+  I32 len;
+  AV *av = (AV *)SvRV($input);
+  if (SvTYPE(av) != SVt_PVAV) {
+    SWIG_croak("in method '$symname', Expecting reference to argv array");
+    goto fail;
+  }
+  len = av_len(av) + 1;
+  $1 = ($1_ltype) len;
+  $2 = (char **) malloc((len+1)*sizeof(char *));
+  for (i = 0; i < len; i++) {
+    SV **tv = av_fetch(av, i, 0);
+    $2[i] = SvPV_nolen(*tv);
+  }
+  $2[i] = NULL;
+}
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_STRING_ARRAY) (int ARGC, char **ARGV) {
+  AV *av = (AV *)SvRV($input);
+  $1 = SvTYPE(av) == SVt_PVAV;
+}
+
+%typemap(freearg) (int ARGC, char **ARGV) {
+  if ($2 != NULL) {
+    free((void *)$2);
+  }
+}
diff --git a/Lib/php/argcargv.i b/Lib/php/argcargv.i
new file mode 100644
index 0000000..e0093c2
--- /dev/null
+++ b/Lib/php/argcargv.i
@@ -0,0 +1,40 @@
+/* ------------------------------------------------------------
+ * SWIG library containing argc and argv multi-argument typemaps
+ * ------------------------------------------------------------ */
+
+%typemap(in) (int ARGC, char **ARGV) {
+  int len, i;
+  zval *val;
+  zend_array *ar;
+  if (Z_TYPE($input) != IS_ARRAY) {
+    SWIG_PHP_Error(E_ERROR, "Type error in '$symname'. Expected array");
+    goto fail;
+  }
+  ar = Z_ARR($input);
+  len = zend_array_count(ar);
+  $1 = ($1_ltype) len;
+  $2 = (char **) malloc((len+1)*sizeof(char *));
+  i = 0;
+  ZEND_HASH_FOREACH_VAL(ar, val) {
+    if (Z_TYPE(*val) != IS_STRING) {
+      SWIG_PHP_Error(E_ERROR, "Array must use strings only, in '$symname'.");
+      goto fail;
+    }
+    if (i == len) {
+      SWIG_PHP_Error(E_ERROR, "Array is bigger than zend report in '$symname'.");
+      goto fail;
+    }
+    $2[i++] = Z_STRVAL(*val);
+  } ZEND_HASH_FOREACH_END();
+  $2[i] = NULL;
+}
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_STRING_ARRAY) (int ARGC, char **ARGV) {
+  $1 = Z_TYPE($input) == IS_ARRAY;
+}
+
+%typemap(freearg) (int ARGC, char **ARGV) {
+  if ($2 != NULL) {
+    free((void *)$2);
+  }
+}
diff --git a/Lib/python/argcargv.i b/Lib/python/argcargv.i
index 717fe73..419803a 100644
--- a/Lib/python/argcargv.i
+++ b/Lib/python/argcargv.i
@@ -1,13 +1,10 @@
 /* ------------------------------------------------------------
- * --- Argc & Argv ---
+ * SWIG library containing argc and argv multi-argument typemaps
  * ------------------------------------------------------------ */
 
 %fragment("SWIG_AsArgcArgv","header",fragment="SWIG_AsCharPtrAndSize") {
 SWIGINTERN int
-SWIG_AsArgcArgv(PyObject *input,
-		swig_type_info *ppchar_info,
-		size_t *argc, char ***argv, int *owner)
-{  
+SWIG_AsArgcArgv(PyObject *input, swig_type_info *ppchar_info, size_t *argc, char ***argv, int *owner) {
   void *vptr;
   int res = SWIG_ConvertPtr(input, &vptr, ppchar_info, 0);
   if (!SWIG_IsOK(res)) {
@@ -51,7 +48,7 @@
   } else {
     /* seems dangerous, but the user asked for it... */
     size_t i = 0;
-    if (argv) { while (*argv[i] != 0) ++i;}    
+    if (argv) { while (*argv[i] != 0) ++i;}
     if (argc) *argc = i;
     if (owner) *owner = 0;
     return SWIG_OK;
@@ -66,10 +63,10 @@
 
 %typemap(in,noblock=0,fragment="SWIG_AsArgcArgv") (int ARGC, char **ARGV) (int res,char **argv = 0, size_t argc = 0, int owner= 0) {
   res = SWIG_AsArgcArgv($input, $descriptor(char**), &argc, &argv, &owner);
-  if (!SWIG_IsOK(res)) { 
+  if (!SWIG_IsOK(res)) {
     $1 = 0; $2 = 0;
     %argument_fail(SWIG_TypeError, "int ARGC, char **ARGV", $symname, $argnum);
-  } else {  
+  } else {
     $1 = %static_cast(argc,$1_ltype);
     $2 = %static_cast(argv, $2_ltype);
   }
diff --git a/Lib/ruby/argcargv.i b/Lib/ruby/argcargv.i
index fc0bc40..24df9c9 100644
--- a/Lib/ruby/argcargv.i
+++ b/Lib/ruby/argcargv.i
@@ -1,24 +1,20 @@
 /* ------------------------------------------------------------
- * --- Argc & Argv ---
- * ------------------------------------------------------------ */
- 
-/* ------------------------------------------------------------
+ * SWIG library containing argc and argv multi-argument typemaps
 
-   Use it as follow:
+   Use it as follows:
 
      %apply (int ARGC, char **ARGV) { (size_t argc, const char **argv) }
 
      %inline %{
 
-     int mainApp(size_t argc, const char **argv) 
-     {
+     int mainApp(size_t argc, const char **argv) {
        return argc;
      }
 
-   then in the ruby side:
+   then from ruby:
 
-     args = ["asdf", "asdf2"]
-     mainApp(args);
+     $args = ["asdf", "asdf2"]
+     mainApp(args)
 
  * ------------------------------------------------------------ */
 
@@ -31,7 +27,7 @@
     VALUE *ptr = RARRAY_PTR($input);
     for (i=0; i < size; i++, ptr++) {
       $2[i]= StringValuePtr(*ptr);
-    }    
+    }
     $2[i]=NULL;
   } else {
     $1 = 0; $2 = 0;
diff --git a/Lib/tcl/argcargv.i b/Lib/tcl/argcargv.i
new file mode 100644
index 0000000..bbe149e
--- /dev/null
+++ b/Lib/tcl/argcargv.i
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------
+ * SWIG library containing argc and argv multi-argument typemaps
+ * ------------------------------------------------------------ */
+
+%typemap(in) (int ARGC, char **ARGV) {
+  int i, nitems;
+  Tcl_Obj **listobjv;
+  if (Tcl_ListObjGetElements(interp, $input, &nitems, &listobjv) == TCL_ERROR) {
+     SWIG_exception_fail(SWIG_ValueError, "in method '$symname', Expecting list of argv");
+     goto fail;
+  }
+  $1 = ($1_ltype) nitems;
+  $2 = (char **) malloc((nitems+1)*sizeof(char *));
+  for (i = 0; i < nitems; i++) {
+    $2[i] = Tcl_GetStringFromObj(listobjv[i], NULL);
+  }
+  $2[i] = NULL;
+}
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_STRING_ARRAY) (int ARGC, char **ARGV) {
+  int len;
+  $1 = Tcl_ListObjLength(interp, $input, &len) == TCL_OK;
+}
+
+%typemap(freearg) (int ARGC, char **ARGV) {
+  if ($2 != NULL) {
+    free((void *)$2);
+  }
+}