#define MODULE_NAME	ODS2
#define MODULE_IDENT	"V1.3"

/*     Ods2.c v1.3   Mainline ODS2 program   */

/*
        This is part of ODS2 written by Paul Nankervis,
        email address:  Paulnank@au1.ibm.com

        ODS2 is distributed freely for all members of the
        VMS community to use. However all derived works
        must maintain comments in their source to acknowledge
        the contibution of the original author.

        The modules in ODS2 are:-

                ACCESS.C        Routines for accessing ODS2 disks
                CACHE.C         Routines for managing memory cache
                DEVICE.C        Routines to maintain device information
                DIRECT.C        Routines for handling directories
                ODS2.C          The mainline program
                PHYVMS.C        Routine to perform physical I/O
                RMS.C           Routines to handle RMS structures
                VMSTIME.C       Routines to handle VMS times

        On non-VMS platforms PHYVMS.C should be replaced as follows:-

                OS/2            PHYOS2.C
                Windows 95/NT   PHYNT.C

        For example under OS/2 the program is compiled using the GCC
        compiler with the single command:-

                gcc -fdollars-in-identifiers ods2.c,rms.c,direct.c,
                      access.c,device.c,cache.c,phyos2.c,vmstime.c
*/

/* Modified by:
 *
 *   31-AUG-2001 01:04	Hunter Goatley <goathunter@goatley.com>
 *
 *	For VMS, added routine getcmd() to read commands with full
 *	command recall capabilities.
 *
 */

/*  This version will compile and run using normal VMS I/O by
    defining VMSIO
*/

/*  This is the top level set of routines. It is fairly
    simple minded asking the user for a command, doing some
    primitive command parsing, and then calling a set of routines
    to perform whatever function is required (for example COPY).
    Some routines are implemented in different ways to test the
    underlying routines - for example TYPE is implemented without
    a NAM block meaning that it cannot support wildcards...
    (sorry! - could be easily fixed though!)
*/

#ifdef VMS
#ifdef __DECC
#pragma module MODULE_NAME MODULE_IDENT
#else
#ifdef vaxc
#module MODULE_NAME MODULE_IDENT
#endif /* vaxc */
#endif /* __DECC */
#endif /* VMS */

#define DEBUGx on
#define VMSIOx on

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


#ifdef VMSIO
#include <ssdef.h>
#include <descrip.h>
#include <starlet.h>
#include <rms.h>
#include <fiddef.h>
#define sys_parse       sys$parse
#define sys_search      sys$search
#define sys_open        sys$open
#define sys_close       sys$close
#define sys_connect     sys$connect
#define sys_disconnect  sys$disconnect
#define sys_get         sys$get
#define sys_put         sys$put
#define sys_create      sys$create
#define sys_erase       sys$erase
#define sys_extend      sys$extend
#define sys_asctim      sys$asctim
#define sys_setddir     sys$setddir
#define dsc_descriptor  dsc$descriptor
#define dsc_w_length    dsc$w_length
#define dsc_a_pointer   dsc$a_pointer

#else
#include "ssdef.h"
#include "descrip.h"
#include "access.h"
#include "rms.h"
#endif

#define PRINT_ATTR (FAB$M_CR | FAB$M_PRN | FAB$M_FTN)



/* keycomp: routine to compare parameter to a keyword - case insensitive! */

int keycomp(char *param,char *keywrd)
{
    while (*param != '\0') {
        if (tolower(*param++) != *keywrd++) return 0;
    }
    return 1;
}


/* checkquals: routine to find a qualifer in a list of possible values */

int checkquals(char *qualset[],int qualc,char *qualv[])
{
    int result = 0;
    while (qualc-- > 0) {
        int i = 0;
        while (qualset[i] != NULL) {
            if (keycomp(qualv[qualc],qualset[i])) {
                result |= 1 << i;
                i = -1;
                break;
            }
            i++;
        }
        if (i >= 0) printf("%%ODS2-W-ILLQUAL, Unknown qualifer '%s' ignored\n",qualv[qualc]);
    }
    return result;
}


/* dir: a directory routine */

char *dirquals[] = {"date","file","size",NULL};

unsigned dir(int argc,char *argv[],int qualc,char *qualv[])
{
    char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
    int sts,options;
    int filecount = 0;
    struct NAM nam = cc$rms_nam;
    struct FAB fab = cc$rms_fab;
    struct XABDAT dat = cc$rms_xabdat;
    struct XABFHC fhc = cc$rms_xabfhc;
    nam.nam$l_esa = res;
    nam.nam$b_ess = NAM$C_MAXRSS;
    fab.fab$l_nam = &nam;
    fab.fab$l_xab = &dat;
    dat.xab$l_nxt = &fhc;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    fab.fab$l_dna = "*.*;*";
    fab.fab$b_dns = strlen(fab.fab$l_dna);
    options = checkquals(dirquals,qualc,qualv);
    sts = sys_parse(&fab);
    if (sts & 1) {
        char dir[NAM$C_MAXRSS + 1];
        int namelen;
        int dirlen = 0;
        int dirfiles = 0,dircount = 0;
        int dirblocks = 0,totblocks = 0;
        int printcol = 0;
#ifdef DEBUG
        res[nam.nam$b_esl] = '\0';
        printf("Parse: %s\n",res);
#endif
        nam.nam$l_rsa = rsa;
        nam.nam$b_rss = NAM$C_MAXRSS;
        fab.fab$l_fop = FAB$M_NAM;
        while ((sts = sys_search(&fab)) & 1) {
            if (dirlen != nam.nam$b_dev + nam.nam$b_dir ||
                memcmp(rsa,dir,nam.nam$b_dev + nam.nam$b_dir) != 0) {
                if (dirfiles > 0) {
                    if (printcol > 0) printf("\n");
                    printf("\nTotal of %d file%s",dirfiles,(dirfiles == 1 ? "" : "s"));
                    if (options & 4) {
                        printf(", %d block%s.\n",dirblocks,(dirblocks == 1 ? "" : "s"));
                    } else {
                        fputs(".\n",stdout);
                    }
                }
                dirlen = nam.nam$b_dev + nam.nam$b_dir;
                memcpy(dir,rsa,dirlen);
                dir[dirlen] = '\0';
                printf("\nDirectory %s\n\n",dir);
                filecount += dirfiles;
                totblocks += dirblocks;
                dircount++;
                dirfiles = 0;
                dirblocks = 0;
                printcol = 0;
            }
            rsa[nam.nam$b_rsl] = '\0';
            namelen = nam.nam$b_name + nam.nam$b_type + nam.nam$b_ver;
            if (options == 0) {
                if (printcol > 0) {
                    int newcol = (printcol + 20) / 20 * 20;
                    if (newcol + namelen >= 80) {
                        fputs("\n",stdout);
                        printcol = 0;
                    } else {
                        printf("%*s",newcol - printcol," ");
                        printcol = newcol;
                    }
                }
                fputs(rsa + dirlen,stdout);
                printcol += namelen;
            } else {
                if (namelen > 18) {
                    printf("%s\n                   ",rsa + dirlen);
                } else {
                    printf("%-19s",rsa + dirlen);
                }
                sts = sys_open(&fab);
                if ((sts & 1) == 0) {
                    printf("Open error: %d\n",sts);
                } else {
                    sts = sys_close(&fab);
                    if (options & 2) {
                        char fileid[100];
                        sprintf(fileid,"(%d,%d,%d)",
                                (nam.nam$b_fid_nmx << 16) | nam.nam$w_fid_num,
                                nam.nam$w_fid_seq,nam.nam$b_fid_rvn);
                        printf("  %-22s",fileid);
                    }
                    if (options & 4) {
                        unsigned filesize = fhc.xab$l_ebk;
                        if (fhc.xab$w_ffb == 0) filesize--;
                        printf("%9d",filesize);
                        dirblocks += filesize;
                    }
                    if (options & 1) {
                        char tim[24];
                        struct dsc_descriptor timdsc;
                        timdsc.dsc_w_length = 23;
                        timdsc.dsc_a_pointer = tim;
                        sts = sys_asctim(0,&timdsc,dat.xab$q_cdt,0);
                        if ((sts & 1) == 0) printf("Asctim error: %d\n",sts);
                        tim[23] = '\0';
                        printf("  %s",tim);
                    }
                    printf("\n");
                }
            }
            dirfiles++;
        }
        if (sts == RMS$_NMF) sts = 1;
        if (printcol > 0) printf("\n");
        if (dirfiles > 0) {
            printf("\nTotal of %d file%s",dirfiles,(dirfiles == 1 ? "" : "s"));
            if (options & 4) {
                printf(", %d block%s.\n",dirblocks,(dirblocks == 1 ? "" : "s"));
            } else {
                fputs(".\n",stdout);
            }
            filecount += dirfiles;
            totblocks += dirblocks;
            if (dircount > 1) {
                printf("\nGrand total of %d director%s, %d file%s",
                       dircount,(dircount == 1 ? "y" : "ies"),
                       filecount,(filecount == 1 ? "" : "s"));
                if (options & 4) {
                    printf(", %d block%s.\n",totblocks,(totblocks == 1 ? "" : "s"));
                } else {
                    fputs(".\n",stdout);
                }
            }
        }
    }
    if (sts & 1) {
        if (filecount < 1) printf("%%DIRECT-W-NOFILES, no files found\n");
    } else {
        printf("%%DIR-E-ERROR Status: %d\n",sts);
    }
    return sts;
}


/* copy: a file copy routine */

#define MAXREC 32767

char *copyquals[] = {"binary",NULL};

unsigned copy(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts,options;
    struct NAM nam = cc$rms_nam;
    struct FAB fab = cc$rms_fab;
    char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
    int filecount = 0;
    nam.nam$l_esa = res;
    nam.nam$b_ess = NAM$C_MAXRSS;
    fab.fab$l_nam = &nam;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    options = checkquals(copyquals,qualc,qualv);
    sts = sys_parse(&fab);
    if (sts & 1) {
        nam.nam$l_rsa = rsa;
        nam.nam$b_rss = NAM$C_MAXRSS;
        fab.fab$l_fop = FAB$M_NAM;
        while ((sts = sys_search(&fab)) & 1) {
            sts = sys_open(&fab);
            if ((sts & 1) == 0) {
                printf("%%COPY-F-OPENFAIL, Open error: %d\n",sts);
                perror("-COPY-F-ERR ");
            } else {
                struct RAB rab = cc$rms_rab;
                rab.rab$l_fab = &fab;
                if ((sts = sys_connect(&rab)) & 1) {
                    FILE *tof;
                    char name[NAM$C_MAXRSS + 1];
                    unsigned records = 0;
                    {
                        char *out = name,*inp = argv[2];
                        int dot = 0;
                        while (*inp != '\0') {
                            if (*inp == '*') {
                                inp++;
                                if (dot) {
                                    memcpy(out,nam.nam$l_type + 1,nam.nam$b_type - 1);
                                    out += nam.nam$b_type - 1;
                                } else {
                                    unsigned length = nam.nam$b_name;
                                    if (*inp == '\0') length += nam.nam$b_type;
                                    memcpy(out,nam.nam$l_name,length);
                                    out += length;
                                }
                            } else {
                                if (*inp == '.') {
                                    dot = 1;
                                } else {
                                    if (strchr(":]\\/",*inp)) dot = 0;
                                }
                                *out++ = *inp++;
                            }
                        }
                        *out++ = '\0';
                    }
#ifndef _WIN32
                    tof = fopen(name,"w");
#else
		    if ((options & 1) == 0 && fab.fab$b_rat & PRINT_ATTR) {
                        tof = fopen(name,"w");
		    } else {
		        tof = fopen(name,"wb");
		    }
#endif
                    if (tof == NULL) {
                        printf("%%COPY-F-OPENOUT, Could not open %s\n",name);
                        perror("-COPY-F-ERR ");
                    } else {
                        char rec[MAXREC + 2];
                        filecount++;
                        rab.rab$l_ubf = rec;
                        rab.rab$w_usz = MAXREC;
                        while ((sts = sys_get(&rab)) & 1) {
                            unsigned rsz = rab.rab$w_rsz;
                            if ((options & 1) == 0 &&
                                 fab.fab$b_rat & PRINT_ATTR) rec[rsz++] = '\n';
                            if (fwrite(rec,rsz,1,tof) == 1) {
                                records++;
                            } else {
                                printf("%%COPY-F- fwrite error!!\n");
                                perror("-COPY-F-ERR ");
                                break;
                            }
                        }
                        if (fclose(tof)) {
                            printf("%%COPY-F- fclose error!!\n");
                            perror("-COPY-F-ERR ");
                        }
                    }
                    sys_disconnect(&rab);
                    rsa[nam.nam$b_rsl] = '\0';
                    if (sts == RMS$_EOF) {
                        printf("%%COPY-S-COPIED, %s copied to %s (%d record%s)\n",
                               rsa,name,records,(records == 1 ? "" : "s"));
                    } else {
                        printf("%%COPY-F-ERROR Status: %d for %s\n",sts,rsa);
                        sts = 1;
                    }
                }
                sys_close(&fab);
            }
        }
        if (sts == RMS$_NMF) sts = 1;
    }
    if (sts & 1) {
        if (filecount > 0) printf("%%COPY-S-NEWFILES, %d file%s created\n",
                                  filecount,(filecount == 1 ? "" : "s"));
    } else {
        printf("%%COPY-F-ERROR Status: %d\n",sts);
    }
    return sts;
}

/* import: a file copy routine */


unsigned import(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts;
    FILE *fromf;
    fromf = fopen(argv[1],"r");
    if (fromf != NULL) {
        struct FAB fab = cc$rms_fab;
        fab.fab$l_fna = argv[2];
        fab.fab$b_fns = strlen(fab.fab$l_fna);
        if ((sts = sys_create(&fab)) & 1) {
            struct RAB rab = cc$rms_rab;
            rab.rab$l_fab = &fab;
            if ((sts = sys_connect(&rab)) & 1) {
                char rec[MAXREC + 2];
                rab.rab$l_rbf = rec;
                rab.rab$w_usz = MAXREC;
                while (fgets(rec,sizeof(rec),fromf) != NULL) {
                    rab.rab$w_rsz = strlen(rec);
                    sts = sys_put(&rab);
                    if ((sts & 1) == 0) break;
                }
                sys_disconnect(&rab);
            }
            sys_close(&fab);
        }
        fclose(fromf);
        if (!(sts & 1)) {
            printf("%%IMPORT-F-ERROR Status: %d\n",sts);
        }
    } else {
        printf("Can't open %s\n",argv[1]);
    }
    return sts;
}


/* diff: a simple file difference routine */

unsigned diff(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts;
    struct FAB fab = cc$rms_fab;
    FILE *tof;
    int records = 0;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    tof = fopen(argv[2],"r");
    if (tof == NULL) {
        printf("Could not open file %s\n",argv[1]);
        sts = 0;
    } else {
        if ((sts = sys_open(&fab)) & 1) {
            struct RAB rab = cc$rms_rab;
            rab.rab$l_fab = &fab;
            if ((sts = sys_connect(&rab)) & 1) {
                char rec[MAXREC + 2],cpy[MAXREC + 1];
                rab.rab$l_ubf = rec;
                rab.rab$w_usz = MAXREC;
                while ((sts = sys_get(&rab)) & 1) {
                    strcpy(rec + rab.rab$w_rsz,"\n");
                    fgets(cpy,MAXREC,tof);
                    if (strcmp(rec,cpy) != 0) {
                        printf("%%DIFF-F-DIFFERENT Files are different!\n");
                        sts = 4;
                        break;
                    } else {
                        records++;
                    }
                }
                sys_disconnect(&rab);
            }
            sys_close(&fab);
        }
        fclose(tof);
        if (sts == RMS$_EOF) sts = 1;
    }
    if (sts & 1) {
        printf("%%DIFF-I-Compared %d records\n",records);
    } else {
        printf("%%DIFF-F-Error %d in difference\n",sts);
    }
    return sts;
}


/* typ: a file TYPE routine */

unsigned typ(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts;
    int records = 0;
    struct FAB fab = cc$rms_fab;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    if ((sts = sys_open(&fab)) & 1) {
        struct RAB rab = cc$rms_rab;
        rab.rab$l_fab = &fab;
        if ((sts = sys_connect(&rab)) & 1) {
            char rec[MAXREC + 2];
            rab.rab$l_ubf = rec;
            rab.rab$w_usz = MAXREC;
            while ((sts = sys_get(&rab)) & 1) {
                unsigned rsz = rab.rab$w_rsz;
                if (fab.fab$b_rat & PRINT_ATTR) rec[rsz++] = '\n';
                rec[rsz++] = '\0';
                fputs(rec,stdout);
                records++;
            }
            sys_disconnect(&rab);
        }
        sys_close(&fab);
        if (sts == RMS$_EOF) sts = 1;
    }
    if ((sts & 1) == 0) {
        printf("%%TYPE-F-ERROR Status: %d\n",sts);
    }
    return sts;
}



/* search: a simple file search routine */

unsigned search(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts = 0;
    int filecount = 0;
    int findcount = 0;
    char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
    struct NAM nam = cc$rms_nam;
    struct FAB fab = cc$rms_fab;
    register char *searstr = argv[2];
    register char firstch = tolower(*searstr++);
    register char *searend = searstr + strlen(searstr);
    {
        char *str = searstr;
        while (str < searend) {
            *str = tolower(*str);
            str++;
        }
    }
    nam = cc$rms_nam;
    fab = cc$rms_fab;
    nam.nam$l_esa = res;
    nam.nam$b_ess = NAM$C_MAXRSS;
    fab.fab$l_nam = &nam;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    fab.fab$l_dna = "";
    fab.fab$b_dns = strlen(fab.fab$l_dna);
    sts = sys_parse(&fab);
    if (sts & 1) {
        nam.nam$l_rsa = rsa;
        nam.nam$b_rss = NAM$C_MAXRSS;
        fab.fab$l_fop = FAB$M_NAM;
        while ((sts = sys_search(&fab)) & 1) {
            sts = sys_open(&fab);
            if ((sts & 1) == 0) {
                printf("%%SEARCH-F-OPENFAIL, Open error: %d\n",sts);
            } else {
                struct RAB rab = cc$rms_rab;
                rab.rab$l_fab = &fab;
                if ((sts = sys_connect(&rab)) & 1) {
                    int printname = 1;
                    char rec[MAXREC + 2];
                    filecount++;
                    rab.rab$l_ubf = rec;
                    rab.rab$w_usz = MAXREC;
                    while ((sts = sys_get(&rab)) & 1) {
                        register char *strng = rec;
                        register char *strngend = strng + (rab.rab$w_rsz - (searend - searstr));
                        while (strng < strngend) {
                            register char ch = *strng++;
                            if (ch == firstch || (ch >= 'A' && ch <= 'Z' && ch + 32 == firstch)) {
                                register char *str = strng;
                                register char *cmp = searstr;
                                while (cmp < searend) {
                                    register char ch2 = *str++;
                                    ch = *cmp;
                                    if (ch2 != ch && (ch2 < 'A' || ch2 > 'Z' || ch2 + 32 != ch)) break;
                                    cmp++;
                                }
                                if (cmp >= searend) {
                                    findcount++;
                                    rec[rab.rab$w_rsz] = '\0';
                                    if (printname) {
                                        rsa[nam.nam$b_rsl] = '\0';
                                        printf("\n******************************\n%s\n\n",rsa);
                                        printname = 0;
                                    }
                                    fputs(rec,stdout);
                                    if (fab.fab$b_rat & PRINT_ATTR) fputc('\n',stdout);
                                    break;
                                }
                            }
                        }
                    }
                    sys_disconnect(&rab);
                }
                if (sts == SS$_NOTINSTALL) {
                    printf("%%SEARCH-W-NOIMPLEM, file operation not implemented\n");
                    sts = 1;
                }
                sys_close(&fab);
            }
        }
        if (sts == RMS$_NMF || sts == RMS$_FNF) sts = 1;
    }
    if (sts & 1) {
        if (filecount < 1) {
            printf("%%SEARCH-W-NOFILES, no files found\n");
        } else {
            if (findcount < 1) printf("%%SEARCH-I-NOMATCHES, no strings matched\n");
        }
    } else {
        printf("%%SEARCH-F-ERROR Status: %d\n",sts);
    }
    return sts;
}


/* del: you don't want to know! */

unsigned del(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts = 0;
    struct NAM nam = cc$rms_nam;
    struct FAB fab = cc$rms_fab;
    char res[NAM$C_MAXRSS + 1],rsa[NAM$C_MAXRSS + 1];
    int filecount = 0;
    nam.nam$l_esa = res;
    nam.nam$b_ess = NAM$C_MAXRSS;
    fab.fab$l_nam = &nam;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    sts = sys_parse(&fab);
    if (sts & 1) {
        if (nam.nam$b_ver < 2) {
            printf("%%DELETE-F-NOVER, you must specify a version!!\n");
        } else {
            nam.nam$l_rsa = rsa;
            nam.nam$b_rss = NAM$C_MAXRSS;
            fab.fab$l_fop = FAB$M_NAM;
            while ((sts = sys_search(&fab)) & 1) {
                sts = sys_erase(&fab);
                if ((sts & 1) == 0) {
                    printf("%%DELETE-F-DELERR, Delete error: %d\n",sts);
                } else {
                    filecount++;
                    rsa[nam.nam$b_rsl] = '\0';
                    printf("%%DELETE-I-DELETED, Deleted %s\n",rsa);
                }
            }
            if (sts == RMS$_NMF) sts = 1;
        }
        if (sts & 1) {
            if (filecount < 1) {
                printf("%%DELETE-W-NOFILES, no files deleted\n");
            }
        } else {
            printf("%%DELETE-F-ERROR Status: %d\n",sts);
        }
    }
    return sts;
}

/* test: you don't want to know! */
struct VCB *test_vcb;

unsigned test(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts = 0;
    struct fiddef fid;
    sts = update_create(test_vcb,NULL,"Test.File",&fid,NULL);
    printf("Test status of %d (%s)\n",sts,argv[1]);
    return sts;
}

/* more test code... */

unsigned extend(int argc,char *argv[],int qualc,char *qualv[])
{
    int sts;
    struct FAB fab = cc$rms_fab;
    fab.fab$l_fna = argv[1];
    fab.fab$b_fns = strlen(fab.fab$l_fna);
    fab.fab$b_fac = FAB$M_UPD;
    if ((sts = sys_open(&fab)) & 1) {
        fab.fab$l_alq = 32;
        sts = sys_extend(&fab);
        sys_close(&fab);
    }
    if ((sts & 1) == 0) {
        printf("%%EXTEND-F-ERROR Status: %d\n",sts);
    }
    return sts;
}




/* show: the show command */

unsigned show(int argc,char *argv[],int qualc,char *qualv[])
{
    unsigned sts = 1;
    if (keycomp(argv[1],"default")) {
        unsigned short curlen;
        char curdir[NAM$C_MAXRSS + 1];
        struct dsc_descriptor curdsc;
        curdsc.dsc_w_length = NAM$C_MAXRSS;
        curdsc.dsc_a_pointer = curdir;
        if ((sts = sys_setddir(NULL,&curlen,&curdsc)) & 1) {
            curdir[curlen] = '\0';
            printf(" %s\n",curdir);
        } else {
            printf("Error %d getting default\n",sts);
        }
    } else {
        if (keycomp(argv[1],"time")) {
            unsigned sts;
            char timstr[24];
            unsigned short timlen;
            struct dsc_descriptor timdsc;
            timdsc.dsc_w_length = 20;
            timdsc.dsc_a_pointer = timstr;
            sts = sys_asctim(&timlen,&timdsc,NULL,0);
            if (sts & 1) {
                timstr[timlen] = '\0';
                printf("  %s\n",timstr);
            } else {
                printf("%%SHOW-W-TIMERR error %d\n",sts);
            }
        } else {
            printf("%%SHOW-W-WHAT '%s'?\n",argv[1]);
        }
    }
    return sts;
}

unsigned setdef_count = 0;

void setdef(char *newdef)
{
    register unsigned sts;
    struct dsc_descriptor defdsc;
    defdsc.dsc_a_pointer = newdef;
    defdsc.dsc_w_length = strlen(defdsc.dsc_a_pointer);
    if ((sts = sys_setddir(&defdsc,NULL,NULL)) & 1) {
        setdef_count++;
    } else {
        printf("Error %d setting default to %s\n",sts,newdef);
    }
}

/* set: the set command */

unsigned set(int argc,char *argv[],int qualc,char *qualv[])
{
    unsigned sts = 1;
    if (keycomp(argv[1],"default")) {
        setdef(argv[2]);
    } else {
        printf("%%SET-W-WHAT '%s'?\n",argv[1]);
    }
    return sts;
}


#ifndef VMSIO

/* The bits we need when we don't have real VMS routines underneath... */

unsigned dodismount(int argc,char *argv[],int qualc,char *qualv[])
{
    struct DEV *dev;
    register int sts = device_lookup(strlen(argv[1]),argv[1],0,&dev);
    if (sts & 1) {
        if (dev->vcb != NULL) {
            sts = dismount(dev->vcb);
        } else {
            sts = SS$_DEVNOTMOUNT;
        }
    }
    if ((sts & 1) == 0) printf("%%DISMOUNT-E-STATUS Error: %d\n",sts);
    return sts;
}



char *mouquals[] = {"write",NULL};

unsigned domount(int argc,char *argv[],int qualc,char *qualv[])
{
    char *dev = argv[1];
    char *lab = argv[2];
    int sts = 1,devices = 0;
    char *devs[100],*labs[100];
    int options = checkquals(mouquals,qualc,qualv);
    while (*lab != '\0') {
        labs[devices++] = lab;
        while (*lab != ',' && *lab != '\0') lab++;
        if (*lab != '\0') {
            *lab++ = '\0';
        } else {
            break;
        }
    }
    devices = 0;
    while (*dev != '\0') {
        devs[devices++] = dev;
        while (*dev != ',' && *dev != '\0') dev++;
        if (*dev != '\0') {
            *dev++ = '\0';
        } else {
            break;
        }
    }
    if (devices > 0) {
        unsigned i;
        struct VCB *vcb;
        sts = mount(options,devices,devs,labs,&vcb);
        if (sts & 1) {
            for (i = 0; i < vcb->devices; i++)
                if (vcb->vcbdev[i].dev != NULL)
                    printf("%%MOUNT-I-MOUNTED, Volume %12.12s mounted on %s\n",
                           vcb->vcbdev[i].home.hm2$t_volname,vcb->vcbdev[i].dev->devnam);
            if (setdef_count == 0) {
                char *colon,defdir[256];
                strcpy(defdir,vcb->vcbdev[0].dev->devnam);
                colon = strchr(defdir,':');
                if (colon != NULL) *colon = '\0';
                strcpy(defdir + strlen(defdir),":[000000]");
                setdef(defdir);
                test_vcb = vcb;
            }
        } else {
            printf("Mount failed with %d\n",sts);
        }
    }
    return sts;
}


void direct_show(void);
void phyio_show(void);

/* statis: print some simple statistics */

unsigned statis(int argc,char *argv[],int qualc,char *qualv[])
{
    printf("Statistics:-\n");
    direct_show();
    cache_show();
    phyio_show();
    return 1;
}

#endif


/* help: a routine to print a pre-prepared help text... */

unsigned help(int argc,char *argv[],int qualc,char *qualv[])
{
    printf("\nODS2 %s\n", MODULE_IDENT);
    printf(" Please send problems/comments to Paulnank@au1.ibm.com\n");
    printf(" Commands are:\n");
    printf("  copy        difference      directory     exit\n");
    printf("  mount       show_default    show_time     search\n");
    printf("  set_default type\n");
    printf(" Example:-\n    $ mount e:\n");
    printf("    $ search e:[vms_common.decc*...]*.h rms$_wld\n");
    printf("    $ set default e:[sys0.sysmgr]\n");
    printf("    $ copy *.com;-1 c:\\*.*\n");
    printf("    $ directory/file/size/date [-.sys*...].%%\n");
    printf("    $ exit\n");
    return 1;
}


/* informaion about the commands we know... */

struct CMDSET {
    char *name;
    unsigned (*proc) (int argc,char *argv[],int qualc,char *qualv[]);
    int minlen;
    int minargs;
    int maxargs;
    int maxquals;
} cmdset[] = {
    {
        "copy",copy,3,3,3,1
},
    {
        "import",import,3,3,3,0
},
    {
        "delete",del,3,2,2,0
},
    {
        "difference",diff,3,3,3,0
},
    {
        "directory",dir,3,1,2,6
},
    {
        "exit",NULL,2,0,0,0
},
    {
        "extend",extend,3,2,2,0
},
    {
        "help",help,2,1,1,0
},
    {
        "quit",NULL,2,0,0,0
},
    {
        "show",show,2,2,2,0
},
    {
        "search",search,3,3,3,0
},
    {
        "set",set,3,2,3,0
},
#ifndef VMSIO
    {
        "dismount",dodismount,3,2,2,0
},
    {
        "mount",domount,3,2,3,2
},
    {
        "statistics",statis,3,1,1,0
},
#endif
    {
        "test",test,4,2,2,0
},
    {
        "type",typ,3,2,2,0
},
    {
        NULL,NULL,0,0,0,0
}
};


/* cmdexecute: identify and execute a command */

int cmdexecute(int argc,char *argv[],int qualc,char *qualv[])
{
    char *ptr = argv[0];
    struct CMDSET *cmd = cmdset;
    unsigned cmdsiz = strlen(ptr);
    while (*ptr != '\0') {
        *ptr = tolower(*ptr);
        ptr++;
    }
    while (cmd->name != NULL) {
        if (cmdsiz >= cmd->minlen && cmdsiz <= strlen(cmd->name)) {
            if (keycomp(argv[0],cmd->name)) {
                if (cmd->proc == NULL) {
                    return 0;
                } else {
                    if (argc < cmd->minargs || argc > cmd->maxargs) {
                        printf("%%ODS2-E-PARAMS, Incorrect number of command parameters\n");
                    } else {
                        if (qualc > cmd->maxquals) {
                            printf("%%ODS2-E-QUALS, Too many command qualifiers\n");
                        } else {
                            (*cmd->proc) (argc,argv,qualc,qualv);
#ifndef VMSIO
                            /* cache_flush();  */
#endif
                        }
                    }
                    return 1;
                }
            }
        }
        cmd++;
    }
    printf("%%ODS2-E-ILLCMD, Illegal or ambiguous command '%s'\n",argv[0]);
    return 1;
}

/* cmdsplit: break a command line into its components */

int cmdsplit(char *str)
{
    int argc = 0,qualc = 0;
    char *argv[32],*qualv[32];
    char *sp = str;
    int i;
    for (i = 0; i < 32; i++) argv[i] = qualv[i] = "";
    while (*sp != '\0') {
        while (*sp == ' ') sp++;
        if (*sp != '\0') {
            if (*sp == '/') {
                *sp++ = '\0';
                qualv[qualc++] = sp;
            } else {
                argv[argc++] = sp;
            }
            while (*sp != ' ' && *sp != '/' && *sp != '\0') sp++;
            if (*sp == '\0') {
                break;
            } else {
                if (*sp != '/') *sp++ = '\0';
            }
        }
    }
    if (argc > 0) return cmdexecute(argc,argv,qualc,qualv);
    return 1;
}

#ifdef VMS
#include <smgdef.h>
#include <smg$routines.h>

char *getcmd(char *inp, char *prompt)
{
    struct dsc_descriptor prompt_d = {strlen(prompt),DSC$K_DTYPE_T,
					DSC$K_CLASS_S, prompt};
    struct dsc_descriptor input_d = {1024,DSC$K_DTYPE_T,
					DSC$K_CLASS_S, inp};
    int status;
    char *retstat;
    static unsigned long key_table_id = 0;
    static unsigned long keyboard_id = 0;
    
    if (key_table_id == 0)
	{
	status = smg$create_key_table (&key_table_id);
	if (status & 1)
	    status = smg$create_virtual_keyboard (&keyboard_id);
	if (!(status & 1)) return (NULL);
	}

    status = smg$read_composed_line (&keyboard_id, &key_table_id,
		&input_d, &prompt_d, &input_d, 0,0,0,0,0,0,0);

    if (status == SMG$_EOF)
	retstat = NULL;
    else
	{
	inp[input_d.dsc_w_length] = '\0';
	retstat = inp;
	}

    return(retstat);
}
#endif /* VMS */


/* main: the simple mainline of this puppy... */

int main(int argc,char *argv[])
{
    char str[2048];
    FILE *atfile = NULL;
    printf(" ODS2 %s\n", MODULE_IDENT);
    while (1) {
        char *ptr;
        if (atfile != NULL) {
            if (fgets(str,sizeof(str),atfile) == NULL) {
                fclose(atfile);
                atfile = NULL;
                *str = '\0';
            } else {
                ptr = strchr(str,'\n');
                if (ptr != NULL) *ptr = '\0';
                printf("$> %s\n",str);
            }
        } else {
#ifdef VMS
	    if (getcmd (str, "$> ") == NULL) break;
#else
          printf("$> ");
          if (fgets(str, sizeof(str), stdin) == NULL)
              break;
          str[strlen(str)-1] = '\0'; /* strip newline from str */
#endif
        }
        ptr = str;
        while (*ptr == ' ' || *ptr == '\t') ptr++;
        if (strlen(ptr) && *ptr != '!') {
            if (*ptr == '@') {
                if (atfile != NULL) {
                    printf("%%ODS2-W-INDIRECT, indirect indirection not permitted\n");
                } else {
                    if ((atfile = fopen(ptr + 1,"r")) == NULL) {
                        perror("%%Indirection failed");
                        printf("\n");
                    }
                }
            } else {
                if ((cmdsplit(ptr) & 1) == 0) break;
            }
        }
    }
    if (atfile != NULL) fclose(atfile);
    return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1