2 * Implements the tcl::prefix command for Jim Tcl
4 * (c) 2011 Steve Bennett <steveb@workware.net.au>
6 * See LICENSE for license details.
13 * Returns the common initial length of the two strings.
15 static int JimStringCommonLength(const char *str1
, int charlen1
, const char *str2
, int charlen2
)
18 while (charlen1
-- && charlen2
--) {
21 str1
+= utf8_tounicode(str1
, &c1
);
22 str2
+= utf8_tounicode(str2
, &c2
);
32 * Like Jim_StringCompareObj() except only matches as much as the length of firstObjPtr.
33 * So "abc" matches "abcdef" but "abcdef" does not match "abc".
35 int JimStringComparePrefix(Jim_Interp
*interp
, Jim_Obj
*firstObjPtr
, Jim_Obj
*secondObjPtr
)
37 /* We do this the easy way by creating a (possibly) shorter version of secondObjPtr */
38 int l1
= Jim_Utf8Length(interp
, firstObjPtr
);
39 const char *s2
= Jim_String(secondObjPtr
);
40 int l2
= Jim_Utf8Length(interp
, secondObjPtr
);
45 objPtr
= Jim_NewStringObjUtf8(interp
, s2
, l1
);
48 objPtr
= secondObjPtr
;
50 Jim_IncrRefCount(objPtr
);
52 ret
= Jim_StringCompareObj(interp
, firstObjPtr
, objPtr
, 0);
53 Jim_DecrRefCount(interp
, objPtr
);
59 static int Jim_TclPrefixCoreCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
64 static const char * const options
[] = { "match", "all", "longest", NULL
};
65 enum { OPT_MATCH
, OPT_ALL
, OPT_LONGEST
};
68 Jim_WrongNumArgs(interp
, 1, argv
, "subcommand ?arg ...?");
71 if (Jim_GetEnum(interp
, argv
[1], options
, &option
, NULL
, JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
)
72 return Jim_CheckShowCommands(interp
, argv
[1], options
);
81 Jim_Obj
*errorObj
= NULL
;
82 const char *message
= "option";
83 static const char * const matchoptions
[] = { "-error", "-exact", "-message", NULL
};
84 enum { OPT_MATCH_ERROR
, OPT_MATCH_EXACT
, OPT_MATCH_MESSAGE
};
85 int flags
= JIM_ERRMSG
| JIM_ENUM_ABBREV
;
88 Jim_WrongNumArgs(interp
, 2, argv
, "?options? table string");
91 tableObj
= argv
[argc
- 2];
92 stringObj
= argv
[argc
- 1];
94 for (i
= 2; i
< argc
; i
++) {
96 if (Jim_GetEnum(interp
, argv
[i
], matchoptions
, &matchoption
, "option", JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
)
98 switch (matchoption
) {
100 flags
&= ~JIM_ENUM_ABBREV
;
103 case OPT_MATCH_ERROR
:
105 Jim_SetResultString(interp
, "missing error options", -1);
109 if (Jim_Length(errorObj
) % 2) {
110 Jim_SetResultString(interp
, "error options must have an even number of elements", -1);
115 case OPT_MATCH_MESSAGE
:
117 Jim_SetResultString(interp
, "missing message", -1);
120 message
= Jim_String(argv
[i
]);
125 tablesize
= Jim_ListLength(interp
, tableObj
);
126 table
= Jim_Alloc((tablesize
+ 1) * sizeof(*table
));
127 for (i
= 0; i
< tablesize
; i
++) {
128 Jim_ListIndex(interp
, tableObj
, i
, &objPtr
, JIM_NONE
);
129 table
[i
] = Jim_String(objPtr
);
133 ret
= Jim_GetEnum(interp
, stringObj
, table
, &i
, message
, flags
);
136 Jim_ListIndex(interp
, tableObj
, i
, &objPtr
, JIM_NONE
);
137 Jim_SetResult(interp
, objPtr
);
140 if (tablesize
== 0) {
141 Jim_SetResultFormatted(interp
, "bad %s \"%#s\": no valid options", message
, stringObj
);
145 if (Jim_Length(errorObj
) == 0) {
146 Jim_SetEmptyResult(interp
);
149 /* Do this the easy way. Build a list to evaluate */
150 objPtr
= Jim_NewStringObj(interp
, "return -level 0 -code error", -1);
151 Jim_ListAppendList(interp
, objPtr
, errorObj
);
152 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
153 return Jim_EvalObjList(interp
, objPtr
);
160 Jim_WrongNumArgs(interp
, 2, argv
, "table string");
165 int listlen
= Jim_ListLength(interp
, argv
[2]);
166 objPtr
= Jim_NewListObj(interp
, NULL
, 0);
167 for (i
= 0; i
< listlen
; i
++) {
168 Jim_Obj
*valObj
= Jim_ListGetIndex(interp
, argv
[2], i
);
169 if (JimStringComparePrefix(interp
, argv
[3], valObj
) == 0) {
170 Jim_ListAppendElement(interp
, objPtr
, valObj
);
173 Jim_SetResult(interp
, objPtr
);
179 Jim_WrongNumArgs(interp
, 2, argv
, "table string");
182 else if (Jim_ListLength(interp
, argv
[2])) {
183 const char *longeststr
= NULL
;
186 int listlen
= Jim_ListLength(interp
, argv
[2]);
190 for (i
= 0; i
< listlen
; i
++) {
191 Jim_Obj
*valObj
= Jim_ListGetIndex(interp
, argv
[2], i
);
193 if (JimStringComparePrefix(interp
, stringObj
, valObj
)) {
194 /* Does not begin with 'string' */
198 if (longeststr
== NULL
) {
199 longestlen
= Jim_Utf8Length(interp
, valObj
);
200 longeststr
= Jim_String(valObj
);
203 longestlen
= JimStringCommonLength(longeststr
, longestlen
, Jim_String(valObj
), Jim_Utf8Length(interp
, valObj
));
207 Jim_SetResultString(interp
, longeststr
, longestlen
);
212 return JIM_ERR
; /* Cannot ever get here */
215 int Jim_tclprefixInit(Jim_Interp
*interp
)
217 if (Jim_PackageProvide(interp
, "tclprefix", "1.0", JIM_ERRMSG
)) {
221 Jim_CreateCommand(interp
, "tcl::prefix", Jim_TclPrefixCoreCommand
, NULL
, NULL
);