/* REXX */
/**********************************************************************/
/* EXEC:      HWTJSPRT                                                */
/*                                                                    */
/* This is a sample REXX program that illustrates some usage of the   */
/* REXX interface to the z/OS HTTP/HTTPS protocol enabler for JSON.   */
/* A JSON input string is formatted for viewing as output.            */
/*                                                                    */
/****PROPRIETARY_STATEMENT********************************************/
/*                                                                   */
/* LICENSED MATERIALS - PROPERTY OF IBM                              */
/* 5650-ZOS COPYRIGHT IBM CORP. 2016                                 */
/*                                                                   */
/* STATUS= HBB77B0                                                   */
/*                                                                   */
/****END_OF_PROPRIETARY_STATEMENT*************************************/
/*                                                                    */
/* Description:                                                       */
/* This example will format a JSON string for viewing.                */
/* The processing routines recursively descend through the objects    */
/*                                                                    */
/* Syntax:  hwtjsprt [-s] [filename]                                  */
/*          -s  strip quotes, commas, and unescape strings            */
/*          -p  -s plus strip { } [ ]                                 */
/*          [filename] is the file that contains the json             */
/*                     if omitted, stdin is used (UNIX only)          */
/*                                                                    */
/* This can run in a TSO, shell, or SYSREXX environment               */
/*                                                                    */
/* Example: format a json string provided in a here document          */
/* hwtjsprt /dev/fd0 <<///                                            */
/* {"name":"Bill","location":"Pok","years":30,"developer":true}       */
/* ///                                                                */
/*                                                                    */
/**********************************************************************/
/*                                                                    */
/* Change Activity:                                                   */
/*  $D0=Wxxxxxx  HBB77B0, 160208, PDND: initial                       */
/*                        180323, PDND: exit rc=4 if no input         */
/*                                                                    */
/**********************************************************************/

signal on novalue
/* initialize environment */
call initialize

/* initialize global variables */
g.=''
g.!debug=0
g.!namewidth=16   /* minimum name width */
g.!cols=2         /* number of columns to indent */
g.!indent=0
g.!q='"'
g.!c=','

/* parse args */
parse arg args
if pos('-d',args)>0 then    /* debug */
   do
   parse var args pre '-d' suf
   args=pre suf
   g.!debug=1
   end
if pos('-s',args)>0 then    /* strip escapes */
   do
   parse var args pre '-s' suf
   args=pre suf
   g.!q=''
   g.!c=''
   end
if pos('-p',args)>0 then    /* strip escapes, brackets, braces */
   do
   parse var args pre '-p' suf
   args=pre suf
   g.!q=''
   g.!c=''
   g.$print=1
   end

/* get json file name from the args */
jsonfile=strip(args)
if jsonfile='' then
   do
   parse source . . . . . . . rxenv .
   if rxenv<>'OMVS' then
      do
      call saye 'specify input file'
      exit 1
      end
   jsonfile='/dev/fd0'   /* default to stdin if omvs environment */
   end

/* read the file to get the json string */
json=getjson(jsonfile)
if length(json)<2 then exit 4

/* initialize a parse instance */
if jsonrqst("hwtjinit g.!jtok") then exit

/* parse the json string */
if jsonrqst('hwtjpars g.!jtok json') then exit

/* format the json string starting at the root */
call say processtype(0)

/* terminate the parse instance */
if jsonrqst("hwtjterm g.!jtok") then exit
return 0

/**********************************************************************/
/* initialize     basic environment initialization                    */
/**********************************************************************/
initialize:
   call syscalls 'ON'
   /* ensure region as large as we are permitted */
   address syscall 'getrlimit (rlimit_as) as.'
   if as.1<>as.2 then
      do
      as.1=as.2
      address syscall 'setrlimit (rlimit_as) as.'
      end
   /* define the host command environment */
   call hwtcalls "ON"
   return

/**********************************************************************/
/* getjson        read the file to get the json string                */
/**********************************************************************/
getjson: procedure expose g. (syscall_constants)
   parse arg jsonfile
   address syscall 'open (jsonfile)' o_rdonly
   fd=retval
   if fd=-1 then
      do
      say 'cannot read' jsonfile
      exit 0
      end
   json=''
   do until retval<1
      address syscall 'read (fd) buf 4096'
      json=json || buf
   end
   address syscall 'close (fd)'
   return json

/**********************************************************************/
/* processtype    call the specific processing routine for the        */
/*                json data type for the element represented by       */
/*                the passed in token and optional name               */
/**********************************************************************/
processtype: procedure expose g.
   parse arg tok,name
   /* get the json data type */
   if jsonrqst('hwtjgjst g.!jtok tok type') then exit
   /* call the processing routine for that data type */
   if type='HWTJ_OBJECT_TYPE' then
      return processobject(tok,name)
   else
   if type='HWTJ_ARRAY_TYPE' then
      return processarray(tok,name)
   else
   if type='HWTJ_STRING_TYPE' then
      return processstring(tok,name)
   else
   if type='HWTJ_NUMBER_TYPE' then
      return processnumber(tok,name)
   else
   if type='HWTJ_BOOLEAN_TYPE' then
      return processboolean(tok,name)
   else
   if type='HWTJ_NULL_TYPE' then
      if length(name)>0 then
         return dq(name)': null'
       else
         return 'null'

   call say 'json type error:' type
   exit 1

/**********************************************************************/
/* processobject  handle processing for a json object type            */
/**********************************************************************/
processobject: procedure expose g.
   parse arg objtok,name
   /* get number of entries in the object */
   if jsonrqst('hwtjgnue g.!jtok objtok entries') then exit
   /* enclose output of an object in braces indenting the content */
   if length(name)>0 then
      call say dq(name,0)': {'
    else
      call say '{'
   g.!indent=g.!indent+g.!cols
   /* for each entry, process the json data type */
   do i=0 by 1 while i<entries
      if jsonrqst('hwtjgoen g.!jtok objtok i objname vtok') then exit
      if i<entries-1 then
         comma=g.!c
       else
         comma=''
      call say processtype(vtok,objname) || comma
   end
   g.!indent=g.!indent-g.!cols
   return '}'

/**********************************************************************/
/* processarray   handle processing for a json array type             */
/**********************************************************************/
processarray: procedure expose g.
   parse arg atok,name
   /* get number of entries in the array  */
   if jsonrqst('hwtjgnue g.!jtok atok entries') then exit
   /* enclose output of an array in brackets indenting the content */
   if length(name)>0 then
      call say dq(name,0)': ['
    else
      call say '['
   g.!indent=g.!indent+g.!cols
   /* for each entry, process the json data type */
   do aix=0 by 1 while aix<entries
      /* get next entry */
      if jsonrqst('hwtjgaen g.!jtok atok aix vtok') then exit
      /* process that entry type */
      if aix<entries-1 then
         comma=g.!c
       else
         comma=''
      call say processtype(vtok) || comma
   end
   g.!indent=g.!indent-g.!cols
   return ']'

/**********************************************************************/
/* processnumber  handle processing for a json number type            */
/**********************************************************************/
processnumber: procedure expose g.
   parse arg vtok,name
   /* get the numeric value as a string */
   if jsonrqst('hwtjgval g.!jtok vtok jvalue') then exit
   /* format the output line */
   if length(name)>0 then
      name=dq(name)': '
   return name || jvalue

/**********************************************************************/
/* processboolean handle processing for a json boolean type           */
/**********************************************************************/
processboolean: procedure expose g.
   parse arg vtok,name
   /* get the boolean value */
   if jsonrqst('hwtjgbov g.!jtok vtok jvalue') then exit
   /* format the output line */
   if length(name)>0 then
      name=dq(name)': '
   return name || jvalue

/**********************************************************************/
/* processstring  handle processing for a json string type            */
/**********************************************************************/
processstring: procedure expose g.
   parse arg vtok,name
   /* get the string value */
   if jsonrqst('hwtjgval g.!jtok vtok jvalue') then exit
   /* format the output line */
   if length(name)>0 then
      name=dq(name)': '
   return name || dq(jvalue)

/**********************************************************************/
/* jsonrqst     build and format the hwtjson call                     */
/*              and check for service call errors                     */
/*                first word is the service name                      */
/*                remainder are the argument variable names without   */
/*                the returncode and diag. as first and last          */
/*                returncode and diag get added and checked           */
/*                returns: 0 = ok  1 = error                          */
/**********************************************************************/
jsonrqst:
   parse arg jcmd jcmdstr
   if g.!debug then say jcmd jcmdstr
   @diag.=''
   jrc=0
   address hwtjson jcmd 'jrc' jcmdstr '@diag.'
   hcrc=rc

   if hcrc=0 & jrc=0 then return 0
   if hcrc<>0 then
      do
      call say '>>' jcmd "failed, rc="hcrc
      return 1
      end
   call say '>>' jcmd jcmdstr
   call say '>>' "retcode="jrc d2x(jrc)'x'
   call say '>>' "reason="@diag.HWTJ_REASONCODE
   call say '>>' "desc="@diag.HWTJ_REASONDESC
   return 1

/*****************************************/
/* quote the argument                    */
/*****************************************/
dq: procedure expose g.
   parse arg str,pad
   if pad='' then
      pad=g.!namewidth
   str=translate(str,'.',xrange('00'x,'3f'x))
   if g.!q='' then
      call jsonrqst 'hwtjesct hwtj_decode str str'
   if pad=0 | length(str)>pad+2 then
      return g.!q||str||g.!q
   return left(g.!q||str||g.!q,pad+3)

/*****************************************/
/* say utility                           */
/*****************************************/
say:
   call sayutil copies(' ',g.!indent)arg(1)
   return

/*****************************************/
/* say utility                           */
/*****************************************/
sayutil: procedure expose g.
   slwidth=126    /* max single line */
   mlwidth=64     /* multi-line width */
   parse source . . . . . . . rxenv .
   parse arg msg,fd
   if g.$print=1 then
      do
      msg=translate(msg,'-   ','{}[]')
      if msg='' then return
      if msg='-' then msg=''
      end
   /* if sysrexx use single line wto for short lines */
   /* and multiline wto for long lines               */
   if rxenv='AXR' then
      do
      cid='FIRSTLINE'
      if length(msg)<=slwidth then
         call axrwto msg
      else
         do forever
            if length(msg)<=mlwidth then
               do
               call axrmlwto msg,'cid','de'
               call axrmlwto '','cid','e'
               return
               end
            parse var msg p1 +(mlwidth) msg
            call axrmlwto strip(p1,'T'),'cid','d'
         end
      end
   else
   /* if a shell environment issue write as a single write to      */
   /* the file descriptor requested or stdout along with a NL      */
   if rxenv='OMVS' then
      do
      if fd='' then fd=1
      address syscall 'write (fd) msg' length(msg)
      nl='15'x
      address syscall 'write (fd) nl'
      end
   else
   /* for any other environment just use say  */
      say arg(1)
   return
