Writing Custom Builtins for KSH93


 
Thread Tools Search this Thread
Top Forums UNIX for Advanced & Expert Users Writing Custom Builtins for KSH93
# 1  
Old 11-12-2010
Writing Custom Builtins for KSH93

I am looking to create some ksh93 extensions using the custom builtin feature.

I can successfully create a builtin function, load it using the builtin -f command and get an output. However, I want to get/set values of KSH variables from within my built-in.

For example, lets say I am creating builtins to interact with a database. I might create the following builtins:
Code:
sqlopen  - open a db connection/cursor
sqlclose - close a db connection/cursor
sqlfetch - fetch results from a cursor

These can be used in the following fashion:
Code:
sqlopen connection "mydatabase"

sqlopen connection cursor "select first_name, last_name, email from guest"

while sqlfetch cursor first_name last_name email
do
  ## Process the record
done

sqlclose cursor
sqlclose connection

For the above code to work properly, my builtins should be able to read/write the values of the connection, cursor, first_name, last_name and email shell variables.

I have googled around and the documentation on this subject is minimal.
I know we need to use the nval library that is part of the AST package, but am not able to find any sample codes.

Has anyone done anything like this before? A sample code would be awesome! Smilie Any pointers would be appreciated!
# 2  
Old 11-12-2010
Some years ago I wrote a few ksh93 builtins. One of them can manipulate the PATH variable and similar ones (like MANPATH, etc). I haven't used it for some time now, so I am not sure, if it still works with a current ksh93 binary. But maybe you get an idea, how variables can be manipulated.

Code:
/*****************************************************************************
 *
 * $Id$
 *
 * Kornshell 93 builtin function 'setpath'.
 *
 *****************************************************************************/

#include <shell.h>

/*-------------*/
/* definitions */
/*-------------*/

#define DELIMITER     ':'
#define DEFAULTVAR    "PATH"

/*-----------------------------------------*/
/* embedded manpage and options definition */
/*-----------------------------------------*/

static char optget_opts[] =
"[+NAME?setpath - manipulate PATH type variables]"
"[-AUTHOR?Paul Herger <ph@herger.at>]"
"[+DESCRIPTION?\bsetpath\b manipulates $PATH or other PATH type shell "
    "variables.]"
"[+?The names of directories or files can be "
    "appended to the end (option \b-a\b), "
    "prepended to the beginning (option \b-p\b) or "
    "excluded (option  \b-x\b) from the variable. "
    "These options are mutually exclusive.]"
"[+?After these manipulations, duplicate entries can be removed from the "
    "variable if the \b-u\b option is specified. Each first occurence of "
    "duplicate entries is kept while all others are deleted.]"
"[+?If the \b-e\b option is specified, all directories or files that do not "
    "exists are deleted.]"

"[a?Append to the end of the variable]"
"[p?Prepend to the beginning of the variable]"
"[x?Exclude from the variable]"
"[e?Delete from or do not include into the path variable non existing"
    "directories or files]"
"[u?Make elements unique by deleting all but the first occurence of"
    "every directory or file]"
"[v?Use \avname\a instead of $PATH]:[vname]"
"\n"
"\n[path ...]\n"
"\n"
"[+EXIT STATUS?]{"
    "[+0?Completed successfully.]"
    "[+>0?An error occurred.]}"
"[+EXAMPLE?\b$ setpath -aeu /opt/bin /usr/local/bin\b]"
"[+?Both /opt/bin and /usr/local/bin are appended at the end of the shell "
    "variable $PATH, if they are not yet elements of it. If they do not "
    "exist they are not appended to or are deleted from $PATH.]"
"[+?\b$ setpath -xv MANPATH /opt/man\b]"
"[+?/opt/man is deleted from $MANPATH.]"
"[+?\b$ setpath -ue\b]"
"[+?Duplicate entries and entries that do not refer to an existing "
    "filesystem object are deleted from $PATH.]";

/*----------------------*/
/* other error messages */
/*----------------------*/

static char usage[]        = "[{-a|-p|-x}] [-eu] [-v vname] [path ...]";
static char err_internal[] = "internal error";
static char err_mutual[]   = "options -%c and -%c are mutually exclusive";

/*===========================================================================*/
/* Add a copy of a string to a container (set or queue). If the string       */
/* contains delimiters, split the string at the delimiters and add each part */
/* of the string as independent string to the container, thus splitting PATH */
/* type strings in their components.                                         */
/*===========================================================================*/

static void add_to_container(Dt_t *container, char *string)
{
    char *copy, *ptr;

    if (!(copy = stkcopy(stkstd, string))) return;

    dtinsert(container, copy);

    for (ptr = copy; *ptr; ptr++)
        if (*ptr == DELIMITER)
        {
            *ptr = '\0';
            dtinsert(container, ptr+1);
        }
}

/*===========================================================================*/
/* This is the main routine of the 'setpath' builtin. It processes the       */
/* arguments, sets up containers and builts the result on the stack.         */
/*===========================================================================*/

int b_setpath(int argc, char *argv[], void *context)
{
    Shell_t  *shp = (Shell_t*)context;
    Namval_t *np;
    Dtdisc_t disc = {0, 0, -1};
    Dt_t     *dt_old, *dt_del;
    char     ch, action = '\0', *obj, *vname = NULL;
    int      flag_e = 0, flag_u = 0, output = 0, stktop;

    /*-----------------------*/
    /* process the arguments */
    /*-----------------------*/

    while (ch = optget(argv, optget_opts))
        switch (ch)
        {
            /*-------------------*/
            /* action to perform */
            /*-------------------*/

            case 'a':
            case 'p':
            case 'x':
                if (action)
                    error(ERROR_ERROR, err_mutual, action, ch);
                else
                    action = ch;
                break;

            /*--------------------------*/
            /* what variable to process */
            /*--------------------------*/

            case 'v':
                if (vname)
                    error(ERROR_WARNING, "option -v can only be used "
                          "once, ignoring -v %s", opt_info.arg);
                else
                    vname = opt_info.arg;
                break;

            /*-------------*/
            /* other flags */
            /*-------------*/

            case 'e':
                flag_e = 1;
                break;

            case 'u':
                flag_u = 1;
                break;

            /*----------------------*/
            /* error and usage info */
            /*----------------------*/

            case ':':
                error(2, "%s", opt_info.arg);
                break;

            case '?':
                error(ERROR_usage(2), "%s", opt_info.arg);
                break;
        }

    /*-----------------------------*/
    /* finish processing arguments */
    /*-----------------------------*/

    argv += opt_info.index;
    argc -= opt_info.index;

    if (error_info.errors) error(ERROR_usage(2), "%s", usage);
    if (argc == 0 && flag_e == 0 && flag_u == 0) return 0;
    if (!vname) vname = DEFAULTVAR;

    /*---------------------------------------------------------------------*/
    /* create containers and get current content of the processed variable */
    /*---------------------------------------------------------------------*/

    dt_old = dtopen (&disc, Dtqueue);
    dt_del = dtopen (&disc, Dtset);

    np = nv_open(vname, shp->var_tree, NV_VARNAME);

    if (!dt_old || !dt_del || !np) error(ERROR_exit(1), err_internal);

    /*-------------------------------------------------------------*/
    /* fill containers with arguments and current variable content */
    /*-------------------------------------------------------------*/

    if (action == 'p')
        while (argc-- > 0) add_to_container(dt_old, *argv++);

    add_to_container (dt_old, (char *) nv_getval(np));

    if (action == 'a')
        while (argc-- > 0) add_to_container(dt_old, *argv++);

    if (action == 'x')
        while (argc-- > 0) add_to_container(dt_del, *argv++);

    /*---------------------------------------------------------------*/
    /* now copy entries from 'dt_old' to the stack for output        */
    /* - skip elements in 'dt_del' (options '-x' and '-u')           */
    /* - check for existence if option '-e' is set                   */
    /* - insert into 'dt_del' if option '-u' is set                  */
    /* - if all tests passed copy to the stack to build the new path */
    /*---------------------------------------------------------------*/

    stktop = stktell(stkstd);

    for (obj = dtfirst(dt_old); obj; obj = dtnext(dt_old, obj))
    {
        if (dtmatch(dt_del, obj)) continue;
        if (flag_e && *obj && *obj != '.' && access(obj, F_OK) < 0) continue;
        if (flag_u) dtinsert(dt_del, obj);
        if (output) sfputc(stkstd, DELIMITER);
        sfprintf(stkstd, "%s", obj);
        output = 1;
    }

    /*--------------------------------------------------*/
    /* copy the result string to the processed variable */
    /*--------------------------------------------------*/

    sfputc(stkstd, '\0');
    nv_putval(np, stkptr(stkstd, stktop), 0);
    nv_close(np);
    dtclose(dt_old);
    dtclose(dt_del);
    return 0;
}

This User Gave Thanks to hergp For This Post:
# 3  
Old 11-12-2010
This might help you ....

Extracted from a blog post KSH93 Custom Builtins II I wrote a couple of years ago:

Example 5 Using name-value routines to access ksh93 internals

The purpose of this custom builtin is to set the value of a specified variable (argument 1) to the size of the specified file (argument 2).
Code:
$ statsize myfilesize /usr/bin/ksh
$ print ${myfilesize}
1157295
$

This example uses the libast nval(3) name-value routines to access the internals of ksh93 and set up a variable with the specifed name (i.e. myfilesize) and value of 1157295 as returned by stat(2) for the file /usr/bin/ksh. See the nval(3) documentation for more detailed information.

Code:
#include <shell.h>
#include <nval.h>

#define SH_DICT "statsize"

static const char usage_statsize[] =
   [-?\n@(#)$Id: stat 2008-05-03 $\n]"
   [-author?Finnbarr P. Murphy <fpmATiisc.com>]"
   [-licence?http://www.opensource.org/licenses/cpl1.0.txt]"
   [+NAME?statsize - assign size of file to variable]"
   [+DESCRIPTION?\bstat\b assigns the size of the specified file"
      "(in bytes) to the specified variable.]"
   "[+OPTIONS?none.]"
   "\n"
   "[+EXIT STATUS?] {"
      "[+0?Success.]"
      "[+>0?An error occurred.]"
   "[+SEE ALSO?\bstat\b(2)]"
;

int
b_statsize(int argc, char *argv[], void *extra)
{
   Namval_t *nvp = (Namval_t *)NULL;
   Shell_t *shp = (Shell_t *)NULL;
   struct stat st;
   long d;
   register int n;

   while (n = optget(argv, usage_statsize)) switch(n) {
      case ':':
        error(2, "%s", opt_info.arg);
        break;
      case '?':
        errormsg(SH_DICT, ERROR_usage(2), "%s",
           opt_info.arg);
        break;
   }
   argc -= opt_info.index;
   argv += opt_info.index;

   if (argc != 2)
      errormsg(SH_DICT, ERROR_usage(2), "%s",
         optusage((char*)0));

   /* get current shell context */
   shp = sh_getinterp();

   /* retrieve information about file */
   stat(argv[1], &st);
   /* assign size of file to long */
   d = (long) st.st_size;

   /* access the variables tree and add specified variable */
   nvp = nv_open(argv[0], shp->var_tree,
          NV_NOARRAY|NV_VARNAME|NV_NOASSIGN);
   if (!nv_isnull(nvp))
      nv_unset(nvp);
   nv_putval(nvp, (char *)&d, NV_INTEGER|NV_RDONLY);
   nv_close(nvp);

   return(0);
}

This custom builtin can easily be extended to provide much more information about a file using command line options. I will leave it as an exercise for you the reader.

Example 6 Print out detailed information about a specified shell variable.

Code:
$ showvar HOME
Value: /home/fpm, Flags: 12288 NV_EXPORT NV_IMPORT
$ integer i=123
$ showvar i
Value: 123, Flags: 10 NV_UINT64 NV_UTOL

See the libast header <nval.h> for detailed information about the different flags which can be associated with each variable. Note that some flags are overloaded so that they mean different things according to how they are OR’ed with other flags.

Code:
#include <shell.h>
#include <nval.h>

#define SH_DICT "showvar"

static const char usage_showvar[] =
   "[-?\n@(#)$Id: showvar 2008-05-04 $\n]"
   "[-author?Finnbarr P. Murphy <fpmATiisc.com>]"
   "[-licence?http://www.opensource.org/licenses/cpl1.0.txt]"
   "[+NAME?showvar - display variable details]"
   "[+DESCRIPTION?\bshowvar\b displays details about the
                   specified variable.]"
   "[+OPTIONS?none.]"
   "\n"
   "\nvariable_name\n"
   "\n"
   "[+EXIT STATUS?] {"
        "[+0?Success.]"
        "[+>0?An error occurred.]"
   "}"
   "[+SEE ALSO?\bstat\b(2)]"
;

struct Flag {
   int flag;
   char *name;
};

/* Note: not a complete list of all possible flags */
struct Flag Flags[] = {
   NV_ARRAY,   "NV_ARRAY",
   NV_BINARY,  "NV_BINARY",
   NV_EXPORT,  "NV_EXPORT",
   NV_HOST,    "NV_HOST",
   NV_IMPORT,  "NV_IMPORT",
   NV_LJUST,   "NV_LJUST",
   NV_LTOU,    "NV_LTOU",
   NV_RAW,     "NV_RAW",
   NV_RDONLY,  "NV_RDONLY",
   NV_REF,     "NV_REF",
   NV_RJUST,   "NV_RJUST",
   NV_TABLE,   "NV_TABLE",
   NV_TAGGED,  "NV_TAGGED",
   NV_UTOL,    "NV_UTOL",
   NV_ZFILL,   "NV_ZFILL",
   0,(char *)NULL
};

struct Flag IntFlags[] = {
   NV_LTOU|NV_UTOL|NV_INTEGER,   "NV_UINT64",
   NV_LTOU|NV_RJUST|NV_INTEGER,  "NV_UINT16",
   NV_RJUST|NV_ZFILL|NV_INTEGER, "NV_FLOAT",
   NV_UTOL|NV_ZFILL|NV_INTEGER,  "NV_LDOUBLE",
   NV_RJUST|NV_INTEGER,          "NV_INT16",
   NV_LTOU|NV_INTEGER,           "NV_UINT32",
   NV_UTOL|NV_INTEGER,           "NV_INT64",
   NV_RJUST,                     "NV_SHORT",
   NV_UTOL,                      "NV_LONG",
   NV_LTOU,                      "NV_UNSIGN",
   NV_ZFILL,                     "NV_DOUBLE",
   NV_LJUST,                     "NV_EXPNOTE",
   NV_INTEGER,                   "NV_INT32(NV_INTEGER)",
   0,(char *)NULL
};

int
b_showvar(int argc, char *argv[], void *extra)
{
    Shell_t *shp = (Shell_t *)NULL;
    Namval_t *nvp = (Namval_t *)NULL;
    char *ptr = (char *)NULL;
    int i;

    while (i = optget(argv, usage_showvar)) switch(i) {
         case ':':
           error(2, "%s", opt_info.arg);
           break;
         case '?':
           errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
           break;
    }
    argc -= opt_info.index;
    argv += opt_info.index;

    if (argc != 1)
        errormsg(SH_DICT, ERROR_usage(2), "%s", optusage((char*)0));

    /* get current shell context */
    shp = sh_getinterp();

    if ((nvp = nv_search(*argv, shp->var_tree, 0)) == NULL) {
           errormsg(SH_DICT, ERROR_exit(1),
              "%s: variable not found", *argv);
        return(1);
    }

    if ((ptr = nv_getval(nvp)) == NULL) {
        errormsg(SH_DICT, ERROR_exit(3),
                "%s: variable is NULL", *argv);
        return(1);
    }

    sfprintf(sfstdout,
             "Value: %s, Flags: %d", ptr, (int)nvp->nvflag);
    if ((int)nvp->nvflag & NV_INTEGER) {
       for (i=0; IntFlags[i].name != NULL; i++) {
          if ((int)nvp->nvflag & IntFlags[i].flag) {
              sfprintf(sfstdout, " %s", IntFlags[i].name);
              break;
          }
       }
    }
    for (i=0; Flags[i].name != NULL; i++) {
        if ((int)nvp->nvflag & Flags[i].flag)
            sfprintf(sfstdout, " %s", Flags[i].name);
    }

    sfprintf(sfstdout,"\n");
    nv_close(nvp);

    return(0);
}

This User Gave Thanks to fpmurphy For This Post:
Login or Register to Ask a Question

Previous Thread | Next Thread

10 More Discussions You Might Find Interesting

1. AIX

Ksh93/AIX compatibility

Hi everyone ! Im trying to know from wich version of AIX KSH93 is available ? Internet tell me 6.x and 7.x AIX are available, bue what about 5.x ? Is KSH93 available on AIX 5.x ? Is it the same way to manipulate variables as KSH93 on 7.x ? Thanks for your support and have a nice day ! (2 Replies)
Discussion started by: majinfrede
2 Replies

2. Programming

Help w/ writing a custom Linux shell using x86 assembly (NASM)

Hello world, I thought this might be a great start to help me with an assignment I have that requires me to write an assembly program that mimics a 32 bit Linux command shell: When launched, your program will perform the following steps in a loop: 1. Print a prompt, specifically “$ “... (1 Reply)
Discussion started by: turtle13
1 Replies

3. UNIX for Advanced & Expert Users

Ksh93 on Linux compatible with ksh93 on AIX

Hi Experts, I have several shell scripts that have been developed on a Linux box for korn ksh93. If we want to run this software on an AIX 6.1 box that runs ksh88 by default can we just change the she-bang line to reference /bin/ksh93 which ships with AIX as its "enhanced shell" to ensure... (6 Replies)
Discussion started by: Keith Turley
6 Replies

4. UNIX for Dummies Questions & Answers

Shell and bash builtins...

Not sure if this is the right forum but I have collated a listing of shell and bash builtins. Builtins is a loose word and may include the '/bin' drawer/folder/directory but NOT any others in the path list. In the case of my Macbook Pro, OSX 10.7.5 the enabled internals is also listed... ... (1 Reply)
Discussion started by: wisecracker
1 Replies

5. UNIX for Dummies Questions & Answers

Why does /bin contain binaries for builtins?

Why do shell builtins like echo and pwd have binaries in /bin? When I do which pwd, I get the one in /bin. that means that I am not using the builtin version? What determines which one gets used? Is the which command a definitive way to determine what is being run when I enter pwd? (16 Replies)
Discussion started by: glev2005
16 Replies

6. Shell Programming and Scripting

Ksh93 vs. Pdksh88: Custom PS1 prompt not working

Greetings! I have to work with a NFS user id between two hosts: A running Ksh 93 and B running pdksh 88. My problem has to do with the custom prompt I created on A: it works like a charm and display colors: PS1="$'\E But I switch over to B, it all goes to hell (private info... (4 Replies)
Discussion started by: alan
4 Replies

7. Programming

pthread_mutex_lock in ANSI C vs using Atomic builtins of GCC

I have a program which has 7-8 threads, and lots of shared variables; these variables (and also they may not the primitive type, they may be enum or struct ), then they may read/write by different threads at the same time. Now, my design is like this, typedef unsigned short int UINT16;... (14 Replies)
Discussion started by: sehang
14 Replies

8. Shell Programming and Scripting

program name and function name builtins

Hi Is there a way to get the program/script name or function name usng built ins. Like in many languages arg holds the program name regards (2 Replies)
Discussion started by: xiamin
2 Replies

9. Shell Programming and Scripting

ksh88 or ksh93

Hi all! Does anybody know how can I check if any UNIX installation has implemented ksh88 or ksh93? Thanks in advance. Néstor. (3 Replies)
Discussion started by: Nestor
3 Replies

10. Shell Programming and Scripting

ksh93 deprecation...

Any means of running ksh93 in a ksh88-mode? Might sound odd, but I want/need to restrict U/Win-developed scripts to correspond to the ksh88 version on my Solaris environment(s). Thanks. (2 Replies)
Discussion started by: curleb
2 Replies
Login or Register to Ask a Question