diff --git a/modules/database/src/ioc/as/asIocRegister.c b/modules/database/src/ioc/as/asIocRegister.c index f4426786a..f97917d8e 100644 --- a/modules/database/src/ioc/as/asIocRegister.c +++ b/modules/database/src/ioc/as/asIocRegister.c @@ -16,7 +16,7 @@ #include "asIocRegister.h" /* asSetFilename */ -static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString}; +static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgStringPath}; static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0}; static const iocshFuncDef asSetFilenameFuncDef = {"asSetFilename",1,asSetFilenameArgs, @@ -97,7 +97,7 @@ static void aspmemCallFunc(const iocshArgBuf *args) } /* astac */ -static const iocshArg astacArg0 = { "recordname",iocshArgString}; +static const iocshArg astacArg0 = { "recordname",iocshArgStringRecord}; static const iocshArg astacArg1 = { "user",iocshArgString}; static const iocshArg astacArg2 = { "host",iocshArgString}; static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2}; diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index 7d056eebf..8bea9c74d 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -8,10 +8,13 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +#define EPICS_PRIVATE_API + #include "iocsh.h" #include "callback.h" #include "dbAccess.h" +#include "dbStaticPvt.h" #include "dbBkpt.h" #include "dbCaTest.h" #include "dbEvent.h" @@ -28,8 +31,8 @@ DBCORE_API extern int callbackParallelThreadsDefault; /* dbLoadDatabase */ -static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString}; -static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgString}; +static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgStringPath}; +static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgStringPath}; static const iocshArg dbLoadDatabaseArg2 = { "substitutions",iocshArgString}; static const iocshArg * const dbLoadDatabaseArgs[3] = { @@ -49,7 +52,7 @@ static void dbLoadDatabaseCallFunc(const iocshArgBuf *args) } /* dbLoadRecords */ -static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString}; +static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgStringPath}; static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString}; static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1}; static const iocshFuncDef dbLoadRecordsFuncDef = { @@ -66,28 +69,28 @@ static void dbLoadRecordsCallFunc(const iocshArgBuf *args) } /* dbb */ -static const iocshArg dbbArg0 = { "record name",iocshArgString}; +static const iocshArg dbbArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbbArgs[1] = {&dbbArg0}; static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs, "Add breakpoint to a lock set.\n"}; static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);} /* dbd */ -static const iocshArg dbdArg0 = { "record name",iocshArgString}; +static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbdArgs[1] = {&dbdArg0}; static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs, "Remove breakpoint from a record.\n"}; static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);} /* dbc */ -static const iocshArg dbcArg0 = { "record name",iocshArgString}; +static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbcArgs[1] = {&dbcArg0}; static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs, "Continue processing in a lock set.\n"}; static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);} /* dbs */ -static const iocshArg dbsArg0 = { "record name",iocshArgString}; +static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbsArgs[1] = {&dbsArg0}; static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs, "Step through record processing.\n"}; @@ -99,7 +102,7 @@ static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0, static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();} /* dbp */ -static const iocshArg dbpArg0 = { "record name",iocshArgString}; +static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbpArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1}; static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs, @@ -108,7 +111,7 @@ static void dbpCallFunc(const iocshArgBuf *args) { dbp(args[0].sval,args[1].ival);} /* dbap */ -static const iocshArg dbapArg0 = { "record name",iocshArgString}; +static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbapArgs[1] = {&dbapArg0}; static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs, "toggle printing after processing a certain record.\n"}; @@ -122,7 +125,7 @@ static const iocshFuncDef dbsrFuncDef = {"dbsr",1,dbsrArgs, static void dbsrCallFunc(const iocshArgBuf *args) { dbsr(args[0].ival);} /* dbcar */ -static const iocshArg dbcarArg0 = { "record name",iocshArgString}; +static const iocshArg dbcarArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbcarArg1 = { "level",iocshArgInt}; static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1}; static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs, @@ -137,7 +140,7 @@ static void dbcarCallFunc(const iocshArgBuf *args) } /* dbjlr */ -static const iocshArg dbjlrArg0 = { "record name",iocshArgString}; +static const iocshArg dbjlrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbjlrArg1 = { "level",iocshArgInt}; static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1}; static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs, @@ -148,7 +151,7 @@ static void dbjlrCallFunc(const iocshArgBuf *args) } /* dbel */ -static const iocshArg dbelArg0 = { "record name",iocshArgString}; +static const iocshArg dbelArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbelArg1 = { "level",iocshArgInt}; static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1}; static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs, @@ -160,7 +163,7 @@ static void dbelCallFunc(const iocshArgBuf *args) } /* dba */ -static const iocshArg dbaArg0 = { "record name",iocshArgString}; +static const iocshArg dbaArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbaArgs[1] = {&dbaArg0}; static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs, "dbAddr info.\n"}; @@ -194,21 +197,21 @@ static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs, static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);} /* dbla */ -static const iocshArg dblaArg0 = { "pattern",iocshArgString}; +static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord}; static const iocshArg * const dblaArgs[1] = {&dblaArg0}; static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs, "List record alias()s by alias name pattern.\n"}; static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);} /* dbgrep */ -static const iocshArg dbgrepArg0 = { "pattern",iocshArgString}; +static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord}; static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0}; static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs, "List record names matching pattern.\n"}; static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);} /* dbgf */ -static const iocshArg dbgfArg0 = { "record name",iocshArgString}; +static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbgfArgs[1] = {&dbgfArg0}; static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs, "Database Get Field.\n" @@ -216,7 +219,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs, static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);} /* dbpf */ -static const iocshArg dbpfArg0 = { "record name",iocshArgString}; +static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbpfArg1 = { "value",iocshArgString}; static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1}; static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs, @@ -226,7 +229,7 @@ static void dbpfCallFunc(const iocshArgBuf *args) { dbpf(args[0].sval,args[1].sval);} /* dbpr */ -static const iocshArg dbprArg0 = { "record name",iocshArgString}; +static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbprArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1}; static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs, @@ -236,14 +239,14 @@ static void dbprCallFunc(const iocshArgBuf *args) { dbpr(args[0].sval,args[1].ival);} /* dbtr */ -static const iocshArg dbtrArg0 = { "record name",iocshArgString}; +static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbtrArgs[1] = {&dbtrArg0}; static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs, "Process record and then some fields.\n"}; static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);} /* dbtgf */ -static const iocshArg dbtgfArg0 = { "record name",iocshArgString}; +static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0}; static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs, "Database Test Get Field.\n" @@ -251,7 +254,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs, static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} /* dbtpf */ -static const iocshArg dbtpfArg0 = { "record name",iocshArgString}; +static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbtpfArg1 = { "value",iocshArgString}; static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1}; static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs, @@ -274,14 +277,14 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0, static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();} /* gft */ -static const iocshArg gftArg0 = { "record name",iocshArgString}; +static const iocshArg gftArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const gftArgs[1] = {&gftArg0}; static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs, "Report dbChannel info and value.\n"}; static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);} /* pft */ -static const iocshArg pftArg0 = { "record name",iocshArgString}; +static const iocshArg pftArg0 = { "record name",iocshArgStringRecord}; static const iocshArg pftArg1 = { "value",iocshArgString}; static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1}; static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs, @@ -290,7 +293,7 @@ static void pftCallFunc(const iocshArgBuf *args) { pft(args[0].sval,args[1].sval);} /* dbtpn */ -static const iocshArg dbtpnArg0 = { "record name",iocshArgString}; +static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbtpnArg1 = { "value",iocshArgString}; static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1}; static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs, @@ -318,7 +321,7 @@ static void dbPutAttrCallFunc(const iocshArgBuf *args) { dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);} /* tpn */ -static const iocshArg tpnArg0 = { "record name",iocshArgString}; +static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord}; static const iocshArg tpnArg1 = { "value",iocshArgString}; static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1}; static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs, @@ -327,7 +330,7 @@ static void tpnCallFunc(const iocshArgBuf *args) { tpn(args[0].sval,args[1].sval);} /* dblsr */ -static const iocshArg dblsrArg0 = { "record name",iocshArgString}; +static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dblsrArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1}; static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs, @@ -500,6 +503,8 @@ static void dbStateShowAllCallFunc (const iocshArgBuf *args) void dbIocRegister(void) { + iocshCompleteRecord = &dbCompleteRecord; + iocshRegister(&dbbFuncDef,dbbCallFunc); iocshRegister(&dbdFuncDef,dbdCallFunc); iocshRegister(&dbcFuncDef,dbcCallFunc); diff --git a/modules/database/src/ioc/dbStatic/Makefile b/modules/database/src/ioc/dbStatic/Makefile index c962501d9..e9bd95b8d 100644 --- a/modules/database/src/ioc/dbStatic/Makefile +++ b/modules/database/src/ioc/dbStatic/Makefile @@ -28,5 +28,6 @@ dbCore_SRCS += dbYacc.c dbCore_SRCS += dbPvdLib.c dbCore_SRCS += dbStaticRun.c dbCore_SRCS += dbStaticIocRegister.c +dbCore_SRCS += dbCompleteRecord.cpp CLEANS += dbLex.c dbYacc.c diff --git a/modules/database/src/ioc/dbStatic/dbCompleteRecord.cpp b/modules/database/src/ioc/dbStatic/dbCompleteRecord.cpp new file mode 100644 index 000000000..73ad6164f --- /dev/null +++ b/modules/database/src/ioc/dbStatic/dbCompleteRecord.cpp @@ -0,0 +1,161 @@ +/*************************************************************************\ +* Copyright (c) 2022 Michael Davidsaver +* SPDX-License-Identifier: EPICS +* EPICS Base is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include + +#include + +#include +#include +#include "dbStaticPvt.h" + +namespace { + +// immutable C string slice. (avoid allocating many temporary std::string) +class CStr { + const char* p; + size_t l; +public: + CStr() :p(NULL), l(0u) {} + CStr(const CStr& o) :p(o.p), l(o.l) {} + explicit CStr(const char* p) :p(p), l(p ? strlen(p) : 0u) {} + CStr(const char* p, size_t n) :p(p), l(n) {} + + CStr& operator=(const CStr& o) { + p = o.p; + l = o.l; + return *this; + } + + bool operator==(const CStr& o) const { + return l==o.l && (p==o.p || memcmp(p, o.p, l)==0); + } + bool operator!=(const CStr& o) const { + return !(*this==o); + } + bool operator<(const CStr& o) const { + size_t pl = std::min(l, o.l); + int cmp = memcmp(p, o.p, pl); + return cmp<0 || (cmp==0 && l < o.l); + } + + bool empty() const { return !l; } + size_t size() const { return l; } + + bool prefixOf(const CStr& full) const { + return full.l >= l && memcmp(full.p, p, l)==0; + } + + CStr commonPrefix(const CStr& o, size_t startFrom=0u) const { + size_t n, N; + for(n=startFrom, N=std::min(l, o.l); nrecordname); + + if(!word.prefixOf(name)) + continue; + + if(first) { // first match + prefix = name; + first = false; + + } else { + prefix = prefix.commonPrefix(name, word.size()); + } + } + } + + // with prefix size known, iterate again to find suggestions + typedef std::set suggestions_t; + suggestions_t suggestions; + + for(long status = dbFirstRecordType(&ent); !status; status = dbNextRecordType(&ent)) { + for(status = dbFirstRecord(&ent); !status; status = dbNextRecord(&ent)) { + CStr name(ent.precnode->recordname); + + if(!prefix.prefixOf(name)) + continue; + + name.chop_at_first_of(":<>{}-", prefix.size()); + suggestions.insert(name); + } + } + + dbFinishEntry(&ent); + + char** ret = NULL; + + if(!prefix.empty() || !suggestions.empty()) { + ret = (char**)malloc(sizeof(*ret)*(2u + suggestions.size())); + ret[0] = prefix.dup(); + size_t n=1u; + for(suggestions_t::iterator it(suggestions.begin()), end(suggestions.end()); + it!=end; ++it) + { + ret[n++] = it->dup(); + } + ret[n] = NULL; + } + + return ret; + + } catch(std::exception& e){ + fprintf(stderr, "dbCompleteRecord error: %s\n", e.what()); + dbFinishEntry(&ent); + return NULL; + } + +} diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h index c6151994e..cce4d8cb8 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h +++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h @@ -115,6 +115,9 @@ PVDENTRY *dbPvdAdd(DBBASE *pdbbase,dbRecordType *precordType,dbRecordNode *precn void dbPvdDelete(DBBASE *pdbbase,dbRecordNode *precnode); void dbPvdFreeMem(DBBASE *pdbbase); +DBCORE_API +char** dbCompleteRecord(const char *word); + #ifdef __cplusplus } #endif diff --git a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c index b6b0c30e6..f4cfaf5ab 100644 --- a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c +++ b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c @@ -13,7 +13,7 @@ /* dbLoadTemplate */ -static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString}; +static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgStringPath}; static const iocshArg dbLoadTemplateArg1 = {"var1=value1,var2=value2", iocshArgString}; static const iocshArg * const dbLoadTemplateArgs[2] = { &dbLoadTemplateArg0, &dbLoadTemplateArg1 diff --git a/modules/database/src/ioc/misc/dlload.c b/modules/database/src/ioc/misc/dlload.c index 544e46098..8b2a76371 100644 --- a/modules/database/src/ioc/misc/dlload.c +++ b/modules/database/src/ioc/misc/dlload.c @@ -17,7 +17,7 @@ IOCSH_STATIC_FUNC void dlload(const char* name) } } -static const iocshArg dlloadArg0 = { "path/library.so", iocshArgString}; +static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath}; static const iocshArg * const dlloadArgs[] = {&dlloadArg0}; static const iocshFuncDef dlloadFuncDef = { "dlload", diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index 5e8b39a1a..07d3fc6fd 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -23,6 +23,8 @@ #include #include +#define EPICS_PRIVATE_API + #include "epicsMath.h" #include "errlog.h" #include "macLib.h" @@ -216,6 +218,8 @@ showError (const char *filename, int lineno, const char *msg, ...) va_end (ap); } +char** (*iocshCompleteRecord)(const char *word) = NULL; + namespace { struct Tokenize { @@ -495,6 +499,74 @@ char** iocsh_attempt_completion(const char* word, int start, int end) return rl_completion_matches(word, iocsh_complete_command); } else { // complete some argument + // make a copy as split() will insert nils + size_t linelen = strlen(line); + std::vector lbuf(linelen+1u); + memcpy(&lbuf[0], line, linelen); + lbuf[linelen] = '\0'; + + int pos = 0; + while(isspace(lbuf[pos])) + pos++; + + Tokenize tokenize; + // don't complain about "Unbalanced quote when completing + tokenize.noise = false; + bool err = tokenize.split(NULL, -1, &lbuf[0], pos); + + if(!err) + err = tokenize.empty() && tokenize.redirects.empty(); + + // look up command name + iocshCmdDef *def = NULL; + if(!err) + err = !(def = (iocshCmdDef *) registryFind(iocshCmdID, tokenize.argv[0])); + + // Find out which argument 'start' is. + // arg is index into tokenize.argv[] + // argv[0] is command name, argv[1] would be first argument. + size_t arg; + if(!err) { + for(arg=1; arg= size_t(start) && eoffset <= size_t(end)) { + break; + } + } + err = arg-1u >= size_t(def->pFuncDef->nargs); + } + + if(!err) { + // we are being asked to complete a valid command, + // for which we have split argument strings. + + const iocshArg * argdef = def->pFuncDef->arg[arg-1u]; + + // known argument type + rl_attempted_completion_over = 1; + + if(argdef->type==iocshArgStringRecord && iocshCompleteRecord) { + return (*iocshCompleteRecord)(word); + + } else if(argdef->type==iocshArgStringPath) { + // use default completion (filesystem) + rl_attempted_completion_over = 0; + + } else if(argdef->type==iocshArgPdbbase) { + char **ret = (char**)calloc(2, sizeof(*ret)); + if(ret) + ret[0] = strdup("pdbbase"); + return ret; + + } else if(arg==1 && strcmp(def->pFuncDef->name, "help")==0) { + return rl_completion_matches(word, iocsh_complete_command); + } + + } + return NULL; } } @@ -701,6 +773,8 @@ cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf, break; case iocshArgString: + case iocshArgStringRecord: + case iocshArgStringPath: argBuf->sval = arg; break; diff --git a/modules/libcom/src/iocsh/iocsh.h b/modules/libcom/src/iocsh/iocsh.h index 6ea76e6e4..70bf9af37 100644 --- a/modules/libcom/src/iocsh/iocsh.h +++ b/modules/libcom/src/iocsh/iocsh.h @@ -65,7 +65,9 @@ typedef enum { iocshArgString, iocshArgPdbbase, iocshArgArgv, - iocshArgPersistentString + iocshArgPersistentString, + iocshArgStringRecord, + iocshArgStringPath, }iocshArgType; /** @@ -297,6 +299,12 @@ LIBCOM_API void epicsStdCall iocshEnvClear(const char *name); /* 'weak' link to pdbbase */ LIBCOM_API extern struct dbBase **iocshPpdbbase; +#ifdef EPICS_PRIVATE_API + +LIBCOM_API +extern char** (*iocshCompleteRecord)(const char *word); +#endif + #ifdef __cplusplus } #endif diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index 431951a01..f09d5ca48 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -98,7 +98,7 @@ static void echoCallFunc(const iocshArgBuf *args) } /* chdir */ -static const iocshArg chdirArg0 = { "directory name",iocshArgString}; +static const iocshArg chdirArg0 = { "directory name",iocshArgStringPath}; static const iocshArg * const chdirArgs[1] = {&chdirArg0}; static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs, "Change directory to new directory provided as parameter\n"};