## root/source/dbgen.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. main
2. parse_comm_line
3. do_switch
4. c_initialize
5. c_initialize
6. c_ipc_socloc_init
7. gen_rec
8. do_sf
9. get_value
10. term_app
11. usage

    /* dbgen - Program to generate random table data for the Bbuuzzb
       database engine/server.
       Rick Smereka, Copyright (C) 2002-2006.

       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program; if not, get a copy via the Internet at
       http://gnu.org/copyleft/gpl.html or write to the Free Software
       Foundation, Inc., 59 Temple Place, Suite 330, Boston,
       MA 02111-1307 USA

       You can contact the author via email at rsmereka@future-lab.com

       Original Linux version. Program syntax:

          dbgen[m] -v -u -rnn -fnn rec_num table_name

       Where:

       v - generate variable length numbers (default=no, fixed at 3 digits)
       u - generate random number of sub-fields and sub-sub-fields
       r - maximum number generated (default=999, cannot exceed three digits)
       f - number of fields to generate in each record (default=random,max=25)

       rec_num - number of records to generate
       table_name - name of table

       There are two versions of this program. The single user version
       called 'dbgen' and the client/server version called 'dbgenm'.
       The 'table_name' must be given in accordance with the
       'dbeng_new_table' guidelines. This program will attempt to create
       a new table. It is an error if the table already exists.
       Some execution examples:

          dbgen 100000 /bbuuzzb/tables/test.buz

       The above example will execute the single user version of this
       program and generate a new table with 100,000 records. A random
       number of fields per record will be generated. The number in
       each field will be fixed at three digits with zero padding on
       the left and will be a maximum of 999. No sub-fields or
       sub-sub-fields will be generated.

          dbgenm 250000 "test.table,/bbuuzzb/tables/test.buz"

       The above example will execute the multiuser version of this
       program and generate a new table with 250,000 records. Note
       that a logical and physical file name are supplied. Also
       notice that quotes are placed around the 'table_name'
       parameter. All default options as described in the previous
       example will apply. Jan/2002, Rick Smereka

       Ported to QNX 4.x and 32bit Windows. Feb/2002, Rick Smereka

       Excluded include of 'flsocket.h' in the case of DOS.
       Apr/2002, Rick Smereka

       Changed call to 'sys_log_init' to include the application
       name. Jun/2002, Rick Smereka

       Fixed bug in 'do_switch' which caused an error if the maximum
       number generated was greater than 99. Nov/2002, Rick Smereka

       Ported to Debian Linux. Jan/2003, Rick Smereka

       Modified for the updated 'socloc' API ('sloc.c').
       May/2003, Rick Smereka

       Modified for the updated 'sys_log' API ('sys_log.c').
       Modified for the updated 'bbuuzzb' API.
       Jun/2003, Rick Smereka

       Re-compiled for the new 'bbuuzzb' API which implements the
       catalog table in single user applications as well as
       client/server. When this application starts and the catalog
       is enabled in single user mode (see 'dbengcfg.c') both the
       logical table name and the physical file name are required.
       Feb/2004, Rick Smereka

       Added include of 'appinit.h' and call to 'appinit_register_name'
       from function 'main'. Changed debug code to use the log manager
       ('logman'). Recoded function 'term_app'. Apr/2004,
       Rick Smereka

       Re-compile after changing the 'socloc' API.
       Jan/2005, Rick Smereka

       Re-compile after modifications to low level TCP socket communication
       module (ipcomm.c). Feb/2006, Rick Smereka */

    #include "stdhead.h"
    #ifndef OS_DOS
    #include "flsocket.h"
    #include "appinit.h"
    #endif
    #include "dbmess.h"
    #include "dbiocode.h"
    #include "dbcomm.h"

    #ifdef IPC_TCP
    #include "socloc.h"
    #include "sloc.h"
    #include "slconfig.h"
    #include "sliocode.h"
    #ifdef OS_WIN32
    WSADATA wsaData;
    #endif
    #endif

    /* high level database API changes depending
       on whether we are compiling for stand-alone
       or multiuser */

    #ifdef MULTIUSER
    #include "dbcs.h"
    #include "dbcscfg.h"
    #else
    #include "dbeng.h"
    #include "dblocal.h"
    #include "dbengcfg.h"
    #include "dblocfg.h"
    #endif

    #define VERSION "1.08.01-2006.02.23"

    #ifdef MULTIUSER
    #define APNAME "dbgenm"
    #else
    #define APNAME "dbgen"
    #endif

    #define MAXFIELD 25
    #define MAXSUBFIELD 10
    #define MAXSUBSUBFIELD 10
    /* #define DEBUG 1 */

    int main(int, char **);
    int parse_comm_line(int, char **);
    int do_switch(char *);
    int c_initialize(void);
    void gen_rec(void);
    int do_sf(long, int);
    void get_value(char *);
    void term_app(void);
    void usage(void);

    #ifdef IPC_TCP
    int c_ipc_socloc_init(void);
    #endif

    /* option or switch structure */

    struct
       {
       int is_var;          // are fields variable length
       int is_sub_field;    // generate random number of sub-fields/sub-sub-fields
       int char_range;      // upper range of char number to generate
       int num_fields;      // number of fields to generate
       long maxrec;         // number of records to generate
       char outfile[255];   // name of output table
       } opt;

    /* global data */

    int tid;

    int main(int argc, char **argv)
    {
       char mess[75];
       long now;
       int ret;

          /* display logo based on platform */
    #ifdef OS_UNIX
       printf("%s for %s Version %s\n", APNAME, SUB_PLATFORM_STRING, VERSION);
    #else
       printf("%s for %s Version %s\n", APNAME, PLATFORM_STRING, VERSION);
    #endif

       printf("by Rick Smereka, Copyright (c) 2002-2006\n");
       printf("%s comes with ABSOLUTELY NO WARRANTY\n", APNAME);
       printf("This is free software, and you are welcome to redistribute it\n");
       printf("under certain conditions; see 'gpl.txt' for information.\n");

       /* check number of parameters passed */

       if (argc == 1)
          {
          usage();
          return(0);
          }

       /* initialize */

       opt.is_var = FALSE;
       opt.is_sub_field = FALSE;
       opt.char_range = 999;
       opt.num_fields = -1;
       opt.maxrec = 0L;

       /* parse switches */

       if (!parse_comm_line(argc, argv))
          exit(0);

    #ifndef OS_DOS
       // register application name with 'appinit'

       if (!appinit_register_name(APNAME))
          {
          printf("%s:fatal error registering app name with appinit\n", APNAME);
          return(0);
          }
    #endif

       /* initialize local engine if we are running locally or
          startup the 'socloc' interface (TCP only) and connect to a
          Bbuuzzb database server if we are running in
          client/server mode */

       if (!c_initialize())
          return(0);

    #ifdef DEBUG
       /* debugging via system logger except for DOS which will use
          the personal logger to create a log file in the current directory */

    #ifdef OS_DOS
       sprintf(mess, "%s.log", APNAME);

       if (logman_start(mess, APNAME))
          {
          printf("%s:unable to open log file '%s',program abort\n", APNAME, mess);
          term_app();
          return(0);
          }
    #else
       if (logman_start(SYS_LOG_SERVICE_NAME, APNAME))
          {
          printf("%s:unable to start system logging,program abort\n", APNAME);
          term_app();
          return(0);
          }
    #endif
    #endif

       srand(time(&now) % 37);    // seed random generator
       tid = 1;                   // indicate open after create

       /* attempt to create a new table and open it */

       if ((ret = db_new_table(opt.outfile, &tid)) != DBENG_OK)
          {
          db_io_code_string(ret, mess);
          printf("%s:bad rc[%s] from db_new_table,program abort\n", APNAME, mess);
          term_app();
          return(0);
          }

       gen_rec();

       if ((ret = db_close(tid)) != DBENG_OK)
          {
          db_io_code_string(ret, mess);
          printf("%s:bad rc[%s] from db_close\n", APNAME, mess);
          }

       printf("\n%s:done,created %ld records\n", APNAME, opt.maxrec);
       term_app();
       return(0);
    }

    int parse_comm_line(int gc, char **gv)
    {
       /* Parse entire command line. Function returns 'TRUE' upon success,
          'FALSE' otherwise. */

       char prm[255];
       int curparm = 1;
       int man_parms = 1;
       int req_man_parms = 2;

       while(curparm < gc)
          {
          strcpy(prm, gv[curparm]);

          if (prm[0] == SWITCH_CHAR)
             {
             if (!do_switch(prm))
                return(FALSE);

             curparm++;
             continue;
             }

          switch(man_parms)
             {
             case 1:
                if ((opt.maxrec = atol(prm)) <= 0L)
                   {
                   printf("%s:out of range[maxrec][%ld]\n", APNAME, opt.maxrec);
                   return(FALSE);
                   }

                man_parms++;
                break;

             case 2:
                strcpy(opt.outfile, prm);

                if (!strlen(opt.outfile))
                   {
                   printf("%s:output table name is empty\n", APNAME);
                   return(FALSE);
                   }

                man_parms++;
                break;
             };

          curparm++;
          }

       if (man_parms < (req_man_parms + 1))
          {
          printf("%s:incorrect number of parameters\n", APNAME);
          usage();
          return(FALSE);
          }

       return(TRUE);
    }

    int do_switch(char *parm)
    {
       /* Parse and set one option based on a switch. Function returns
          'TRUE' upon success, 'FALSE' otherwise. */

       switch(toupper(parm[1]))
          {
          case 'V':
             opt.is_var = TRUE;
             break;

          case 'S':
             opt.is_sub_field = TRUE;
             break;

          case 'R':
             if (strlen(parm) > 5)
                {
                printf("%s:out of range[max number used],s/b 10-999\n", APNAME);
                return(FALSE);
                }

             if ((opt.char_range = atoi(&parm[2])) < 10)
                {
                printf("%s:out of range[max number used],s/b 10-999\n", APNAME);
                return(FALSE);
                }

             if (opt.char_range > 999)
                {
                printf("%s:out of range[max number used],s/b 10-999\n", APNAME);
                return(FALSE);
                }

             break;

          case 'F':
             if ((opt.num_fields = atoi(&parm[2])) < 1)
                {
                printf("%s:out of range[number of fields]\n", APNAME);
                return(FALSE);
                }

             break;

          default:
             printf("%s:unknown parameter '%s'\n", APNAME, parm);
          };

       return(TRUE);
    }

    #ifdef MULTIUSER
    int c_initialize(void)
    {
       /* Initialize socket communication, form connection to
          'socloc' server (TCP only), update the config list and connect
          to a 'Bbuuzzb' server. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char mname[] = "c_initialize";
       char mes[128];
       int ret;

    #ifdef IPC_TCP
       if (!c_ipc_socloc_init())
          return(FALSE);
    #endif

       /* attempt to locate a 'Bbuuzzb' server and connect to it */

       if ((ret = db_initialize()) != DBENG_OK)
          {
          db_io_code_string(ret, mes);
          printf("%s:error connecting to the Bbuuzzb server,rc[%s]. "
                 "Program abort\n", mname, mes);
          return(FALSE);
          }

       /* display current connected server host name and
          TCP port number */

    #ifdef IPC_TCP
       (void)db_get_active(mes, &ret);
       printf("%s:connected to Bbuuzzb server %s on TCP port %d (decimal)\n",
              mname, mes, ret);
    #endif

       return(TRUE);
    }
    #endif

    #ifndef MULTIUSER
    int c_initialize(void)
    {
       /* Start the 'DBENG' local engine, read the config file
          and display the result settings. Function returns
          'TRUE' upon success, 'FALSE' otherwise. */

       char mname[] = "c_initialize";
       char *mes;
       int ret, flag;

    #ifdef DEBUG
    #ifdef IPC_TCP
       if (!c_ipc_socloc_init())
          return(FALSE);
    #endif
    #endif

       if ((mes = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
          {
          printf("%s:alloc fail[mes]. Program abort\n", mname);
          return(FALSE);
          }

       ret = db_initialize();

       /* interpret code from init which may be
          a fatal config error */

       switch(ret)
          {
          case DBENG_OK:
             printf("%s:read config file successfully\n", mname);
             break;

          case DBENG_CONFIG_UNABLE_TO_OPEN:
             printf("%s:config file not found, loaded defaults\n", mname);
             break;

          default:
             db_io_code_string(ret, mes);
             printf("%s:fatal config error[%s]\n", mname, mes);
             printf("%s:terminating\n", mname);
             free(mes);
             return(FALSE);
             break;
          };

       (void)db_config_get_tmp_path(mes);
       printf("%s:tmp path is '%s'\n", mname, mes);
       (void)db_config_get_log(mes);
       printf("%s:log is '%s'\n", mname, mes);

       if ((ret = db_config_get_catalog(mes)) == DBENG_OK)
          printf("%s:catalog is '%s'\n", mname, mes);
       else
          printf("%s:catalog name not loaded\n", mname);

       (void)db_config_get_catalog_flag(&flag);

       if (flag_2_logic_string(flag, mes))
          printf("%s:catalog is %s\n", mname, mes);

       free(mes);
       return(TRUE);
    }
    #endif

    #ifdef IPC_TCP
    int c_ipc_socloc_init(void)
    {
       /* Initialize socket communication, form connection to
          'socloc' server (TCP only) and update the config list.
          Function returns 'TRUE' upon success, 'FALSE' otherwise. */

       char mname[] = "c_ipc_socloc_init";
       char *thelist, mes[128];
       int ret;

       /* startup WinSock (if Windoze) */

    #ifdef OS_WIN32
       if (WSAStartup(WINSOCK_VERSION, &wsaData))
          {
          printf("%s:unable to start WinSock. "
                "Program abort\n", mname);
          return(FALSE);
          }
    #endif

       /* attempt to connect to a 'socloc' server */

       if ((ret = sloc_initialize()) != SL_OK)
          {
          sl_code_string(ret, mes);
          printf("%s:error initializing socloc interface,rc[%s]. "
                "Program abort\n", mname, mes);
          return(FALSE);
          }

       /* get config list from 'socloc' server and update
          the client list with it */

       if ((thelist = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          printf("%s:alloc fail[thelist]. Program abort\n", mname);
          return(FALSE);
          }

       if ((ret = sloc_config_get_list(thelist)) != SL_OK)
          {
          free(thelist);
          sl_code_string(ret, mes);
          printf("%s:error getting socloc config list,rc[%s]. "
                 "Program abort\n", mname, mes);
          return(FALSE);
          }

       if ((ret = sl_config_put_list(thelist)) != SL_OK)
          {
          sl_code_string(ret, mes);
          printf("%s:error putting socloc config list,rc[%s]. ",
                 "Program abort\n", mname, mes);
          free(thelist);
          return(FALSE);
          }

       free(thelist);
       return(TRUE);
    }
    #endif

    void gen_rec(void)
    {
       /* Generate the records. Table is assumed to be open
          with the 'tid' loaded. */

       char thenum[4], mess[75], mname[] = "gen_rec";
       long i;
       int ret, j, nfields;

       /* record generation loop */

       for(i = 1L; i <= opt.maxrec; i++)
          {
          /* initialize new record */

          if ((ret = db_new(tid)) != DBENG_OK)
             {
             db_io_code_string(ret, mess);
             printf("%s:%s:bad rc[%s] from db_new[%ld]\n", APNAME, mname,
                    mess, i);
             return;
             }

          /* number of fields in record */

          nfields = (opt.num_fields == -1) ? ((rand() % MAXFIELD) + 1) :
                    opt.num_fields;

          /* field generation loop */

          for(j = 1; j <= nfields; j++)
             {
             /* get field value */

             get_value(thenum);

             /* put the field into record */

             if ((ret = db_put_field(tid, j, thenum)) != DBENG_OK)
                {
                db_io_code_string(ret, mess);
                printf("%s:%s:bad rc[%s] from db_put_field[%ld,%d]\n", APNAME,
                       mname, mess, i, j);
                return;
                }

             /* put sub-fields and sub-sub-fields if requested */

             if (opt.is_sub_field)
                if (!do_sf(i, j))
                   return;
             }

          /* write the new record */

          if ((ret = db_write(tid)) != DBENG_OK)
             {
             db_io_code_string(ret, mess);
             printf("%s:%s:bad rc[%s] from db_write[%ld]\n", APNAME,
                    mname, mess, i);
             return;
             }

          if (((i + 1L) % 5) == 0L)
             printf("%s:%s:processed record %ld\r", APNAME, mname, i);
          }
    }

    int do_sf(long rec_num, int field_num)
    {
       /* Decide whether to create sub-fields and sub-sub-fields
          within the field data. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char thenum[4], mess[75], mname[] = "do_sf";
       int ret, has_sf, has_ssf, num_sf, num_ssf, i, j;

       has_sf = rand() % 2;

       if (!has_sf)
          return(TRUE);

       num_sf = (rand() % MAXSUBFIELD) + 1;

       for(i = 1; i <= num_sf; i++)
          {
          get_value(thenum);

          if ((ret = db_put_subfield(tid, field_num, i, thenum)) != DBENG_OK)
             {
             db_io_code_string(ret, mess);
             printf("%s:%s:bad rc[%s] from db_put_subfield[%ld,%d,%d]\n",
                    APNAME, mname, mess, rec_num, field_num, i);
             return(FALSE);
             }

          has_ssf = rand() % 2;

          if (has_ssf)
             {
             num_ssf = (rand() % MAXSUBSUBFIELD) + 1;

             for(j = 1; j <= num_ssf; j++)
                {
                get_value(thenum);

                if ((ret = db_put_subsubfield(tid, field_num, i, j, thenum))
                    != DBENG_OK)
                   {
                   db_io_code_string(ret, mess);
                   printf("%s:%s:bad rc[%s] from db_put_subsubfield[%ld,%d,%d"
                          ",%d]\n", APNAME, mname, mess, rec_num, field_num,
                          i, j);
                   return(FALSE);
                   }
                }
             }
          }

       return(TRUE);
    }

    void get_value(char *vout)
    {
       /* Create a random number in proper range and return it in
          'vout' which must already be allocated to sufficient size. */

       int ival;

       ival = (rand() % opt.char_range) + 1;

       if (opt.is_var)
          sprintf(vout, "%d", ival);
       else
          sprintf(vout, "%03d", ival);
    }

    void term_app(void)
    {
       // Prepare to terminate the application. Shutdown all IPC API's.

       db_end();

       if (logman_is_active())
          logman_end();

    #ifdef IPC_TCP
       if (sloc_is_init())
          sloc_term_api();

    #ifdef OS_WIN32
       WSACleanup();
    #endif
    #endif

    #ifndef OS_DOS
       (void)appinit_remove_name();
    #endif
    }

    void usage(void)
    {
       /* Display program usage. */

       printf("%s: A program to generate random data for Bbuuzzb.\n", APNAME);
       printf("usage: %s %cv %cs %crnn %cfnn maxrec table_name\n\n", APNAME,
              SWITCH_CHAR, SWITCH_CHAR, SWITCH_CHAR, SWITCH_CHAR);
       printf("Where:\n");
       printf("'maxrec' is the number of records to generate\n");
       printf("'table_name' is the output table name\n");
       printf("'%cv' is generate variable length fields (instead of all being "
              "3 char)\n", SWITCH_CHAR);
       printf("'%cs' is generate random number of sub-fields/sub-sub-fields"
              " (default=off)\n", SWITCH_CHAR);
       printf("'%crnn' is the maximum number to generate (default=999,"
              "range 10-999)\n", SWITCH_CHAR);
       printf("'%cfnn' is the number of fields to generate (default="
              "random,max=%d)\n", SWITCH_CHAR, MAXFIELD);
    }

/* [<][>][^][v][top][bottom][index][help] */