Browse Source

os400: implement CLI tool

This is provided as a QADRT (ascii) program, a link to it in the IFS and
a minimal CL command.

Closes #11547
Patrick Monnerat 8 months ago
parent
commit
6f8d9cc4e9

+ 3 - 0
lib/setup-os400.h

@@ -34,6 +34,9 @@
 /* No OS/400 header file defines u_int32_t. */
 typedef unsigned long   u_int32_t;
 
+/* OS/400 has no idea of a tty! */
+#define isatty(fd)      0
+
 
 /* System API wrapper prototypes & definitions to support ASCII parameters. */
 

+ 4 - 1
packages/Makefile.am

@@ -28,6 +28,8 @@ EXTRA_DIST = README.md \
   OS400/rpg-examples \
   OS400/ccsidcurl.c \
   OS400/ccsidcurl.h \
+  OS400/curlcl.c \
+  OS400/curlmain.c \
   OS400/curl.inc.in \
   OS400/initscript.sh \
   OS400/make-include.sh \
@@ -36,7 +38,8 @@ EXTRA_DIST = README.md \
   OS400/make-tests.sh \
   OS400/makefile.sh \
   OS400/os400sys.c \
-  OS400/os400sys.h
+  OS400/os400sys.h \
+  OS400/curl.cmd
 
 CHECKSRC = $(CS_$(V))
 CS_0 = @echo "  RUN     " $@;

+ 13 - 0
packages/OS400/README.OS400

@@ -374,3 +374,16 @@ _ Since V7R4M0, procedure overloading is used to emulate limited "vararg-like"
 is provided for that purpose: this allows storing a long value in the
 curl_forms array. Please note the form API is deprecated and the MIME API
 should be used instead.
+
+
+CLI tool:
+
+  The build system provides it as a bound program, an IFS link to it and a
+simple CL command. The latter however is not able to provide a different
+parameter for each option since there are too many of those; instead,
+parameters are entered in a single field subject to quoting and escaping, in
+the same form as expected by the standard CLI program.
+  Care must be taken about the program output encoding: by default, it is sent
+to the standard output and is thus subject to transcoding. It is therefore
+recommended to use option "--output" to redirect output to a specific IFS file.
+Similar problems may occur about the standard input encoding.

+ 32 - 0
packages/OS400/curl.cmd

@@ -0,0 +1,32 @@
+/*****************************************************************************/
+/*                                  _   _ ____  _                            */
+/*  Project                     ___| | | |  _ \| |                           */
+/*                             / __| | | | |_) | |                           */
+/*                            | (__| |_| |  _ <| |___                        */
+/*                             \___|\___/|_| \_\_____|                       */
+/*                                                                           */
+/* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.                   */
+/*                                                                           */
+/* This software is licensed as described in the file COPYING, which         */
+/* you should have received as part of this distribution. The terms          */
+/* are also available at https://curl.se/docs/copyright.html.                */
+/*                                                                           */
+/* You may opt to use, copy, modify, merge, publish, distribute and/or sell  */
+/* copies of the Software, and permit persons to whom the Software is        */
+/* furnished to do so, under the terms of the COPYING file.                  */
+/*                                                                           */
+/* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY */
+/* KIND, either express or implied.                                          */
+/*                                                                           */
+/* SPDX-License-Identifier: curl                                             */
+/*                                                                           */
+/*                                                                           */
+/*****************************************************************************/
+
+/*      Use program CURLCL as interface to the curl command line tool        */
+
+             CMD        PROMPT('File transfer utility')
+
+             PARM       KWD(CMDARGS) TYPE(*CHAR) LEN(5000) VARY(*YES *INT2)   +
+                          CASE(*MIXED) EXPR(*YES) MIN(1)                      +
+                          PROMPT('Curl command arguments')

+ 176 - 0
packages/OS400/curlcl.c

@@ -0,0 +1,176 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ *
+ ***************************************************************************/
+
+/* CL interface program to curl cli tool. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <milib.h>
+#include <miptrnam.h>
+#include <mih/callpgmv.h>
+
+
+/* Variable-length string, with 16-bit length. */
+typedef struct {
+  short len;
+  char  string[5000];
+}  vary2;
+
+
+/* Arguments from CL command. */
+typedef struct {
+  char *    pgm;            /* Program name. */
+  vary2 *   cmdargs;        /* Command line arguments. */
+}  arguments;
+
+
+static int
+is_ifs(char c)
+{
+  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static int
+parse_command_line(const char *cmdargs, size_t len,
+                   size_t *argc, char **argv,
+                   size_t *argsize, char *argbuf)
+{
+  const char *endline = cmdargs + len;
+  char quote = '\0';
+  int inarg = 0;
+
+  *argc = 0;
+  *argsize = 0;
+
+  while(cmdargs < endline) {
+    char c = *cmdargs++;
+
+    if(!inarg) {
+      /* Skip argument separator. */
+      if(is_ifs(c))
+        continue;
+
+      /* Start a new argument. */
+      ++*argc;
+      if(argv)
+        *argv++ = argbuf;
+      inarg = 1;
+    }
+
+    /* Check for quoting end. */
+    if(quote && quote == c) {
+      quote = '\0';
+      continue;
+    }
+
+    /* Check for backslash-escaping. */
+    if(quote != '\'' && c == '\\') {
+      if(cmdargs >= endline) {
+        fputs("Trailing backslash in command\n", stderr);
+        return -1;
+      }
+      c = *cmdargs++;
+    }
+    else if(!quote && is_ifs(c)) {      /* Check for end of argument. */
+      inarg = 0;
+      c = '\0';         /* Will store a string terminator. */
+    }
+
+    /* Store argument character and count it. */
+    if(argbuf)
+      *argbuf++ = c;
+    ++*argsize;
+  }
+
+  if(quote) {
+    fprintf(stderr, "Unterminated quote: %c\n", quote);
+    return -1;
+  }
+
+  /* Terminate last argument. */
+  if(inarg) {
+    if(argbuf)
+      *argbuf = '\0';
+    ++*argsize;
+  }
+
+  /* Terminate argument list. */
+  if(argv)
+    *argv = NULL;
+
+  return 0;
+}
+
+
+int
+main(int argsc, arguments *args)
+{
+  size_t argc;
+  char **argv;
+  size_t argsize;
+  int i;
+  int exitcode;
+  char library[11];
+
+  /* Extract current program library name. */
+  for(i = 0; i < 10; i++) {
+    char c = args->pgm[i];
+
+    if(!c || c == '/')
+      break;
+
+    library[i] = c;
+  }
+  library[i] = '\0';
+
+  /* Measure arguments size. */
+  exitcode = parse_command_line(args->cmdargs->string, args->cmdargs->len,
+                   &argc, NULL, &argsize, NULL);
+
+  if(!exitcode) {
+    /* Allocate space for parsed arguments. */
+    argv = (char **) malloc((argc + 1) * sizeof(*argv) + argsize);
+    if(!argv) {
+      fputs("Memory allocation error\n", stderr);
+      exitcode = -2;
+    }
+    else {
+      _SYSPTR pgmptr = rslvsp(WLI_PGM, (char *) "CURL", library, _AUTH_NONE);
+      _LU_Work_Area_T *luwrka = (_LU_Work_Area_T *) _LUWRKA();
+
+      parse_command_line(args->cmdargs->string, args->cmdargs->len,
+                   &argc, argv, &argsize, (char *) (argv + argc + 1));
+
+      /* Call program. */
+      _CALLPGMV((void *) &pgmptr, argv, argc);
+      exitcode = luwrka->LU_RC;
+
+      free(argv);
+    }
+  }
+
+  return exitcode;
+}

+ 123 - 0
packages/OS400/curlmain.c

@@ -0,0 +1,123 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ *
+ ***************************************************************************/
+
+/*
+ * QADRT/QADRTMAIN2 substitution program.
+ * This is needed because the IBM-provided QADRTMAIN2 does not
+ * properly translate arguments by default or if no locale is provided.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <iconv.h>
+#include <errno.h>
+#include <locale.h>
+
+/* Do not use qadrt.h since it defines unneeded static procedures. */
+extern void     QadrtInit(void);
+extern int      QadrtFreeConversionTable(void);
+extern int      QadrtFreeEnviron(void);
+extern char *   setlocale_a(int, const char *);
+
+
+/* The ASCII main program. */
+extern int      main_a(int argc, char * * argv);
+
+/* Global values of original EBCDIC arguments. */
+int             ebcdic_argc;
+char **         ebcdic_argv;
+
+
+int
+main(int argc, char **argv)
+
+{
+  int i;
+  int j;
+  iconv_t cd;
+  size_t bytecount = 0;
+  char * inbuf;
+  char * outbuf;
+  size_t inbytesleft;
+  size_t outbytesleft;
+  char dummybuf[128];
+  char tocode[32];
+  char fromcode[32];
+
+  ebcdic_argc = argc;
+  ebcdic_argv = argv;
+
+  /* Build the encoding converter. */
+  strncpy(tocode, "IBMCCSID01208", sizeof tocode);      /* Use UTF-8. */
+  strncpy(fromcode, "IBMCCSID000000000010", sizeof fromcode);
+  cd = iconv_open(tocode, fromcode);
+
+  /* Measure the arguments. */
+  for(i = 0; i < argc; i++) {
+    inbuf = argv[i];
+    do {
+      inbytesleft = 0;
+      outbuf = dummybuf;
+      outbytesleft = sizeof dummybuf;
+      j = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
+      bytecount += outbuf - dummybuf;
+    } while(j == -1 && errno == E2BIG);
+
+    /* Reset the shift state. */
+    iconv(cd, NULL, &inbytesleft, &outbuf, &outbytesleft);
+   }
+
+  /* Allocate memory for the ASCII arguments and vector. */
+  argv = (char **) malloc((argc + 1) * sizeof *argv + bytecount);
+
+  /* Build the vector and convert argument encoding. */
+  outbuf = (char *) (argv + argc + 1);
+  outbytesleft = bytecount;
+
+  for(i = 0; i < argc; i++) {
+    argv[i] = outbuf;
+    inbuf = ebcdic_argv[i];
+    inbytesleft = 0;
+    iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
+    iconv(cd, NULL, &inbytesleft, &outbuf, &outbytesleft);
+  }
+
+  iconv_close(cd);
+  argv[argc] = NULL;
+
+  /* Try setting the locale regardless of QADRT_ENV_LOCALE. */
+  setlocale_a(LC_ALL, "");
+
+  /* Call the program. */
+  i = main_a(argc, argv);
+
+  /* Clean-up allocated items. */
+  free((char *) argv);
+  QadrtFreeConversionTable();
+  QadrtFreeEnviron();
+
+  /* Terminate. */
+  return i;
+}

+ 25 - 2
packages/OS400/initscript.sh

@@ -80,7 +80,6 @@ setenv OUTPUT           '*NONE'                 # Compilation output option.
 setenv TGTRLS           '*CURRENT'              # Target OS release.
 setenv IFSDIR           '/curl'                 # Installation IFS directory.
 setenv QADRTDIR         '/QIBM/ProdData/qadrt'  # QADRT IFS directory.
-setenv QADRTLIB         'QADRT'                 # QADRT object library.
 
 #       Define ZLIB availability and locations.
 
@@ -236,7 +235,7 @@ make_module()
         CMD="${CMD} OPTIMIZE(${OPTIMIZE})"
         CMD="${CMD} DBGVIEW(${DEBUG})"
 
-        DEFINES="${3} BUILDING_LIBCURL 'qadrt_use_inline'"
+        DEFINES="${3} 'qadrt_use_inline'"
 
         if [ "${WITH_ZLIB}" != "0" ]
         then    DEFINES="${DEFINES} HAVE_LIBZ"
@@ -270,6 +269,7 @@ db2_name()
                 tr 'a-z-' 'A-Z_'                                        |
                 sed -e 's/\..*//'                                       \
                     -e 's/^CURL_*/C/'                                   \
+                    -e 's/^TOOL_*/T/'                                   \
                     -e 's/^\(.\).*\(.........\)$/\1\2/'
         fi
 }
@@ -288,3 +288,26 @@ versioned_copy()
             -e "s/@LIBCURL_TIMESTAMP@/${LIBCURL_TIMESTAMP}/g"           \
                 < "${1}" > "${2}"
 }
+
+
+#       Get definitions from a make file.
+#       The `sed' statement works as follows:
+#       - Join \nl-separated lines.
+#       - Retain only lines that begins with "identifier =".
+#       - Turn these lines into shell variable assignments.
+
+get_make_vars()
+
+{
+        eval "`sed -e ': begin'                                         \
+                -e '/\\\\$/{'                                           \
+                -e 'N'                                                  \
+                -e 's/\\\\\\n/ /'                                       \
+                -e 'b begin'                                            \
+                -e '}'                                                  \
+                -e '/^[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/!d'           \
+                -e 's/[[:space:]]*=[[:space:]]*/=/'                     \
+                -e 's/=\\(.*[^[:space:]]\\)[[:space:]]*$/=\\"\\1\\"/'   \
+                -e 's/\\\$(\\([^)]*\\))/\${\\1}/g'                      \
+                < \"${1}\"`"
+}

+ 6 - 17
packages/OS400/make-lib.sh

@@ -44,37 +44,26 @@ echo '#pragma comment(user, "libcurl version '"${LIBCURL_VERSION}"'")' > os400.c
 echo '#pragma comment(user, __DATE__)' >> os400.c
 echo '#pragma comment(user, __TIME__)' >> os400.c
 echo '#pragma comment(copyright, "Copyright (C) Daniel Stenberg et al. OS/400 version by P. Monnerat")' >> os400.c
-make_module     OS400           os400.c
+make_module     OS400           os400.c         BUILDING_LIBCURL
 LINK=                           # No need to rebuild service program yet.
 MODULES=
 
 
-#       Get source list.
+#       Get source list (CSOURCES variable).
 
-sed -e ':begin'                                                         \
-    -e '/\\$/{'                                                         \
-    -e 's/\\$/ /'                                                       \
-    -e 'N'                                                              \
-    -e 'bbegin'                                                         \
-    -e '}'                                                              \
-    -e 's/\n//g'                                                        \
-    -e 's/[[:space:]]*$//'                                              \
-    -e 's/^\([A-Za-z][A-Za-z0-9_]*\)[[:space:]]*=[[:space:]]*\(.*\)/\1="\2"/' \
-    -e 's/\$(\([A-Za-z][A-Za-z0-9_]*\))/${\1}/g'                        \
-        < Makefile.inc > tmpscript.sh
-. ./tmpscript.sh
+get_make_vars Makefile.inc
 
 
 #       Compile the sources into modules.
 
 INCLUDES="'`pwd`'"
 
-make_module     OS400SYS        "${SCRIPTDIR}/os400sys.c"
-make_module     CCSIDCURL       "${SCRIPTDIR}/ccsidcurl.c"
+make_module     OS400SYS        "${SCRIPTDIR}/os400sys.c"       BUILDING_LIBCURL
+make_module     CCSIDCURL       "${SCRIPTDIR}/ccsidcurl.c"      BUILDING_LIBCURL
 
 for SRC in ${CSOURCES}
 do      MODULE=`db2_name "${SRC}"`
-        make_module "${MODULE}" "${SRC}"
+        make_module "${MODULE}" "${SRC}" BUILDING_LIBCURL
 done
 
 

+ 73 - 2
packages/OS400/make-src.sh

@@ -23,5 +23,76 @@
 #
 ###########################################################################
 #
-#
-#       Not implemented yet on OS/400.
+#       Command line interface tool compilation script for the OS/400.
+
+SCRIPTDIR=`dirname "${0}"`
+. "${SCRIPTDIR}/initscript.sh"
+cd "${TOPDIR}/src"
+
+
+#       Get source lists.
+#       CURL_CFILES are in the current directory.
+#       CURLX_CFILES are in the lib directory and need to be recompiled because
+#               some function names change using macros.
+
+get_make_vars Makefile.inc
+
+
+#       Compile the sources into modules.
+
+LINK=
+MODULES=
+INCLUDES="'${TOPDIR}/lib'"
+
+for SRC in ${CURLX_CFILES}
+do      MODULE=`db2_name "${SRC}"`
+        MODULE=`db2_name "X${MODULE}"`
+        make_module "${MODULE}" "${SRC}"
+done
+
+for SRC in ${CURL_CFILES}
+do      MODULE=`db2_name "${SRC}"`
+        make_module "${MODULE}" "${SRC}"
+done
+
+
+#       Link modules into program.
+
+MODULES="`echo \"${MODULES}\" | sed \"s/[^ ][^ ]*/${TARGETLIB}\/&/g\"`"
+CMD="CRTPGM PGM(${TARGETLIB}/CURL)"
+CMD="${CMD} ENTMOD(${TARGETLIB}/CURLMAIN)"
+CMD="${CMD} MODULE(${MODULES})"
+CMD="${CMD} BNDSRVPGM(${TARGETLIB}/${SRVPGM} QADRTTS)"
+CMD="${CMD} TGTRLS(${TGTRLS})"
+CLcommand "${CMD}"
+
+
+#       Create the IFS command.
+
+IFSBIN="${IFSDIR}/bin"
+
+if action_needed "${IFSBIN}"
+then    mkdir -p "${IFSBIN}"
+fi
+
+rm -f "${IFSBIN}/curl"
+ln -s "/QSYS.LIB/${TARGETLIB}.LIB/CURL.PGM" "${IFSBIN}/curl"
+
+
+#       Create the CL interface program.
+
+if action_needed "${LIBIFSNAME}/CURLCL.PGM" "${SCRIPTDIR}/curlcl.c"
+then    CMD="CRTBNDC PGM(${TARGETLIB}/CURLCL)"
+        CMD="${CMD} SRCSTMF('${SCRIPTDIR}/curlcl.c')"
+        CMD="${CMD} TGTCCSID(${TGTCCSID})"
+        CLcommand "${CMD}"
+fi
+
+
+#       Create the CL command.
+
+if action_needed "${LIBIFSNAME}/CURL.CMD" "${SCRIPTDIR}/curl.cmd"
+then    CMD="CRTCMD CMD(${TARGETLIB}/CURL) PGM(${TARGETLIB}/CURLCL)"
+        CMD="${CMD} SRCSTMF('${SCRIPTDIR}/curl.cmd')"
+        CLcommand "${CMD}"
+fi

+ 3 - 19
packages/OS400/make-tests.sh

@@ -40,24 +40,8 @@ cd "${TOPDIR}/tests"
 cd libtest
 
 #       Get definitions from the Makefile.inc file.
-#       The `sed' statement works as follows:
-#       _ Join \nl-separated lines.
-#       _ Retain only lines that begins with "identifier =".
-#       _ Turn these lines into shell variable assignments.
-
-eval "`sed -e ': begin'                                                 \
-        -e '/\\\\$/{'                                                   \
-        -e 'N'                                                          \
-        -e 's/\\\\\\n/ /'                                               \
-        -e 'b begin'                                                    \
-        -e '}'                                                          \
-        -e '/^[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[=]/b keep'             \
-        -e 'd'                                                          \
-        -e ': keep'                                                     \
-        -e 's/[[:space:]]*=[[:space:]]*/=/'                             \
-        -e 's/=\\(.*[^[:space:]]\\)[[:space:]]*$/=\\"\\1\\"/'           \
-        -e 's/\\$(\\([^)]*\\))/${\\1}/g'                                \
-        < Makefile.inc`"
+
+get_make_vars Makefile.inc
 
 #       Special case: redefine chkhostname compilation parameters.
 
@@ -128,7 +112,7 @@ do      DB2PGM=`db2_name "${PGM}"`
                 MODULES="`echo \"${MODULES}\" |
                     sed \"s/[^ ][^ ]*/${TARGETLIB}\/&/g\"`"
                 CMD="CRTPGM PGM(${TARGETLIB}/${DB2PGM})"
-                CMD="${CMD} ENTMOD(${QADRTLIB}/QADRTMAIN2)"
+                CMD="${CMD} ENTMOD(${TARGETLIB}/CURLMAIN)"
                 CMD="${CMD} MODULE(${MODULES})"
                 CMD="${CMD} BNDSRVPGM(${TARGETLIB}/${SRVPGM} QADRTTS)"
                 CMD="${CMD} TGTRLS(${TGTRLS})"

+ 14 - 0
packages/OS400/makefile.sh

@@ -101,6 +101,20 @@ do      MEMBER="`basename \"${EXAMPLE}\"`"
 done
 
 
+#       Compile the QADRTMAIN2 replacement module.
+
+if action_needed "${LIBIFSNAME}/CURLMAIN.MODULE" "${SCRIPTDIR}/curlmain.c"
+then    CMD="CRTCMOD MODULE(${TARGETLIB}/CURLMAIN)"
+        CMD="${CMD} SRCSTMF('${SCRIPTDIR}/curlmain.c')"
+        CMD="${CMD} SYSIFCOPT(*IFS64IO) LOCALETYPE(*LOCALE) FLAG(10)"
+        CMD="${CMD} TGTCCSID(${TGTCCSID}) TGTRLS(${TGTRLS})"
+        CMD="${CMD} OUTPUT(${OUTPUT})"
+        CMD="${CMD} OPTIMIZE(${OPTIMIZE})"
+        CMD="${CMD} DBGVIEW(${DEBUG})"
+        CLcommand "${CMD}"
+fi
+
+
 #       Build in each directory.
 
 # for SUBDIR in include lib src tests