eventloop: Don't assume unistd.h and sys/time.h
[jimtcl.git] / jim-tclprefix.c
blobd7a1c45935b9cb409de154ee6611226601ed85da
1 /*
2 * Implements the tcl::prefix command for Jim Tcl
4 * (c) 2011 Steve Bennett <steveb@workware.net.au>
6 * See LICENSE for license details.
7 */
9 #include <jim.h>
10 #include "utf8.h"
12 /**
13 * Returns the common initial length of the two strings.
15 static int JimStringCommonLength(const char *str1, int charlen1, const char *str2, int charlen2)
17 int maxlen = 0;
18 while (charlen1-- && charlen2--) {
19 int c1;
20 int c2;
21 str1 += utf8_tounicode(str1, &c1);
22 str2 += utf8_tounicode(str2, &c2);
23 if (c1 != c2) {
24 break;
26 maxlen++;
28 return maxlen;
31 /* [tcl::prefix]
33 static int Jim_TclPrefixCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
35 Jim_Obj *objPtr;
36 Jim_Obj *stringObj;
37 int option;
38 static const char * const options[] = { "match", "all", "longest", NULL };
39 enum { OPT_MATCH, OPT_ALL, OPT_LONGEST };
41 if (argc < 2) {
42 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arg ...?");
43 return JIM_ERR;
45 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
46 return Jim_CheckShowCommands(interp, argv[1], options);
48 switch (option) {
49 case OPT_MATCH:{
50 int i;
51 int ret;
52 int tablesize;
53 const char **table;
54 Jim_Obj *tableObj;
55 Jim_Obj *errorObj = NULL;
56 Jim_Obj *messageObj = NULL;
57 static const char * const matchoptions[] = { "-error", "-exact", "-message", NULL };
58 enum { OPT_MATCH_ERROR, OPT_MATCH_EXACT, OPT_MATCH_MESSAGE };
59 int flags = JIM_ERRMSG | JIM_ENUM_ABBREV;
61 if (argc < 4) {
62 Jim_WrongNumArgs(interp, 2, argv, "?options? table string");
63 return JIM_ERR;
65 tableObj = argv[argc - 2];
66 stringObj = argv[argc - 1];
67 argc -= 2;
68 for (i = 2; i < argc; i++) {
69 int matchoption;
70 if (Jim_GetEnum(interp, argv[i], matchoptions, &matchoption, "option", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
71 return JIM_ERR;
72 switch (matchoption) {
73 case OPT_MATCH_EXACT:
74 flags &= ~JIM_ENUM_ABBREV;
75 break;
77 case OPT_MATCH_ERROR:
78 if (++i == argc) {
79 Jim_SetResultString(interp, "missing error options", -1);
80 return JIM_ERR;
82 errorObj = argv[i];
83 if (Jim_Length(errorObj) % 2) {
84 Jim_SetResultString(interp, "error options must have an even number of elements", -1);
85 return JIM_ERR;
87 break;
89 case OPT_MATCH_MESSAGE:
90 if (++i == argc) {
91 Jim_SetResultString(interp, "missing message", -1);
92 return JIM_ERR;
94 messageObj = argv[i];
95 break;
98 /* Do the match */
99 tablesize = Jim_ListLength(interp, tableObj);
100 table = Jim_Alloc((tablesize + 1) * sizeof(*table));
101 for (i = 0; i < tablesize; i++) {
102 Jim_ListIndex(interp, tableObj, i, &objPtr, JIM_NONE);
103 table[i] = Jim_String(objPtr);
105 table[i] = NULL;
107 ret = Jim_GetEnum(interp, stringObj, table, &i, messageObj ? Jim_String(messageObj) : NULL, flags);
108 Jim_Free(table);
109 if (ret == JIM_OK) {
110 Jim_ListIndex(interp, tableObj, i, &objPtr, JIM_NONE);
111 Jim_SetResult(interp, objPtr);
112 return JIM_OK;
114 if (tablesize == 0) {
115 Jim_SetResultFormatted(interp, "bad option \"%#s\": no valid options", stringObj);
116 return JIM_ERR;
118 if (errorObj) {
119 if (Jim_Length(errorObj) == 0) {
120 Jim_SetEmptyResult(interp);
121 return JIM_OK;
123 /* Do this the easy way. Build a list to evaluate */
124 objPtr = Jim_NewStringObj(interp, "return -level 0 -code error", -1);
125 Jim_ListAppendList(interp, objPtr, errorObj);
126 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
127 return Jim_EvalObjList(interp, objPtr);
129 return JIM_ERR;
132 case OPT_ALL:
133 if (argc != 4) {
134 Jim_WrongNumArgs(interp, 2, argv, "table string");
135 return JIM_ERR;
137 else {
138 int i;
139 int listlen = Jim_ListLength(interp, argv[2]);
140 objPtr = Jim_NewListObj(interp, NULL, 0);
141 for (i = 0; i < listlen; i++) {
142 Jim_Obj *valObj = Jim_ListGetIndex(interp, argv[2], i);
143 if (Jim_StringCompareLenObj(interp, argv[3], valObj, 0) == 0) {
144 Jim_ListAppendElement(interp, objPtr, valObj);
147 Jim_SetResult(interp, objPtr);
148 return JIM_OK;
151 case OPT_LONGEST:
152 if (argc != 4) {
153 Jim_WrongNumArgs(interp, 2, argv, "table string");
154 return JIM_ERR;
156 else if (Jim_ListLength(interp, argv[2])) {
157 const char *longeststr = NULL;
158 int longestlen = 0;
160 stringObj = argv[3];
162 int i;
163 int listlen = Jim_ListLength(interp, argv[2]);
164 for (i = 0; i < listlen; i++) {
165 Jim_Obj *valObj = Jim_ListGetIndex(interp, argv[2], i);
167 if (Jim_StringCompareLenObj(interp, stringObj, valObj, 0)) {
168 /* Does not begin with 'string' */
169 continue;
172 if (longeststr == NULL) {
173 longestlen = Jim_Utf8Length(interp, valObj);
174 longeststr = Jim_String(valObj);
176 else {
177 longestlen = JimStringCommonLength(longeststr, longestlen, Jim_String(valObj), Jim_Utf8Length(interp, valObj));
180 if (longeststr) {
181 Jim_SetResultString(interp, longeststr, longestlen);
183 return JIM_OK;
186 return JIM_ERR; /* Cannot ever get here */
189 int Jim_tclprefixInit(Jim_Interp *interp)
191 if (Jim_PackageProvide(interp, "tclprefix", "1.0", JIM_ERRMSG)) {
192 return JIM_ERR;
195 Jim_CreateCommand(interp, "tcl::prefix", Jim_TclPrefixCoreCommand, NULL, NULL);
196 return JIM_OK;