summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornwrl <n/a>2025-08-09 19:11:13 -0500
committernwrl <n/a>2025-08-09 19:11:13 -0500
commit241d430ad2eaefa63a675e1418dff8ad1a01b3f0 (patch)
treedd547fe31aae85976d5457607e420839b168f253
parent223a5c54b2453685eb380ed691281c724c688c09 (diff)
Flesh out sql commands
-rw-r--r--LICENSE.md (renamed from LICENSE)0
-rw-r--r--README.md4
-rw-r--r--nameblocker.sp264
3 files changed, 108 insertions, 160 deletions
diff --git a/LICENSE b/LICENSE.md
index c76e02b..c76e02b 100644
--- a/LICENSE
+++ b/LICENSE.md
diff --git a/README.md b/README.md
index 896c70b..20fc575 100644
--- a/README.md
+++ b/README.md
@@ -6,3 +6,7 @@ TF2 Name Blocker is a simple sourcemod plugin that prevents users with certain n
6 6
7## Features 7## Features
8 8
9- Add, remove, and replace regular expression patterns to a filter-list
10- Kick, ban, or do nothing to users with offending names
11- Target admins based off of immunity flags
12- Change the compilation flags used to create the regular expression
diff --git a/nameblocker.sp b/nameblocker.sp
index 8e6ab33..9611efa 100644
--- a/nameblocker.sp
+++ b/nameblocker.sp
@@ -52,6 +52,8 @@ public Plugin myinfo = {
52// This exists because you can't sizeof() a Handle, but it IS specified to be a 32bit integer. This should also equal the size of 52// This exists because you can't sizeof() a Handle, but it IS specified to be a 32bit integer. This should also equal the size of
53// any other methodmap or descendant of Handle (like Regex) 53// any other methodmap or descendant of Handle (like Regex)
54 54
55#define STEAMID64LENGTH 17
56
55#define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */ 57#define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */
56#define DATABASE_FAIL_MSG "Could not populate regex & pattern lists from database" 58#define DATABASE_FAIL_MSG "Could not populate regex & pattern lists from database"
57 59
@@ -65,11 +67,18 @@ enum OperatingMode {
65ArrayList regexlist; 67ArrayList regexlist;
66ArrayList patternlist; 68ArrayList patternlist;
67 69
68Database db; static const char DBTABLENAME[] = "163687013_SMNameBlocker"; 70#define DBTABLENAME "163687013_SMNameBlocker"
69DBStatement dbInsert; static const char DBINSERTSTATEMENT[] = "INSERT OR IGNORE INTO ? (regexstr, steamid64) VALUES (?, ?);"; 71#define DBCREATETABLE "CREATE TABLE IF NOT EXISTS " ... DBTABLENAME ... " (id INTEGER NOT NULL, regexstr TEXT NOT NULL ON CONFLICT IGNORE, steamid64 TEXT NOT NULL ON CONFLICT IGNORE, dateof TEXT DEFAULT CURRENT_DATE, timeof TEXT DEFAULT CURRENT_TIME, PRIMARY KEY (id), UNIQUE (regexstr));"
70DBStatement dbDelete; static const char DBDELETESTATEMENT[] = "DELETE FROM ? WHERE regexstr=?;"; 72#define DBINSERTSTATEMENT "INSERT OR IGNORE INTO " ... DBTABLENAME ... " (regexstr, steamid64) VALUES (?, ?);"
71DBStatement dbReplace; static const char DBREPLACESTATEMENT[] = "UPDATE OR IGNORE ? SET regexstr=?, steamid64=? WHERE regexstr=?;"; 73#define DBDELETESTATEMENT "DELETE FROM " ... DBTABLENAME ... " WHERE regexstr=?;"
72DBStatement dbPopulate; static const char DBPOPULATESTATEMENT[] = "SELECT regexstr FROM ?"; 74#define DBREPLACESTATEMENT "UPDATE OR IGNORE " ... DBTABLENAME ... " SET regexstr=?, steamid64=? WHERE regexstr=?;"
75#define DBPOPULATESTATEMENT "SELECT regexstr FROM " ... DBTABLENAME
76
77Database db;
78DBStatement dbInsert;
79DBStatement dbDelete;
80DBStatement dbReplace;
81DBStatement dbPopulate;
73 82
74 83
75ConVar gcvarOperMode; static const char OPERMODENAME[] = "nameblock_OperatingMode"; const OperatingMode DEFAULTOPERMODE = OP_KICK; 84ConVar gcvarOperMode; static const char OPERMODENAME[] = "nameblock_OperatingMode"; const OperatingMode DEFAULTOPERMODE = OP_KICK;
@@ -125,85 +134,66 @@ void xRegisterIntConVar(ConVar& cv, int defaultVal, const char[] name, const cha
125 134
126int initPrepStatements() { 135int initPrepStatements() {
127 char sqlerr[256 + 1]; 136 char sqlerr[256 + 1];
128 137 int err = 0;
138
129 SQL_LockDatabase(db); 139 SQL_LockDatabase(db);
130 if((dbInsert = SQL_PrepareQuery(db, DBINSERTSTATEMENT, sqlerr, sizeof(sqlerr))) == null) { 140 if((dbInsert = SQL_PrepareQuery(db, DBINSERTSTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err)
131 SQL_UnlockDatabase(db); 141 err = 1;
132 logAndFail("Could not prepare insert statement: %s", sqlerr);
133 }
134 if((dbDelete = SQL_PrepareQuery(db, DBDELETESTATEMENT, sqlerr, sizeof(sqlerr))) == null) {
135 SQL_UnlockDatabase(db);
136 logAndFail("Could not prepare delete statement: %s", sqlerr);
137 }
138 if((dbReplace = SQL_PrepareQuery(db, DBREPLACESTATEMENT, sqlerr, sizeof(sqlerr))) == null) {
139 SQL_UnlockDatabase(db);
140 logAndFail("Could not prepare replace statement: %s", sqlerr);
141 }
142 if((dbPopulate = SQL_PrepareQuery(db, DBPOPULATESTATEMENT, sqlerr, sizeof(sqlerr))) == null) {
143 SQL_UnlockDatabase(db);
144 logAndFail("Could not prepare populate statement: %s", sqlerr);
145 }
146 142
147 // This might not work / I might have to use Format() instead of binding. We will see 143 if((dbDelete = SQL_PrepareQuery(db, DBDELETESTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err)
148 if(SQL_BindParamString(dbInsert, 0, DBTABLENAME, true)) { 144 err = 2;
149 SQL_GetError(dbInsert, sqlerr, sizeof(sqlerr));
150 SQL_UnlockDatabase(db);
151 logAndFail("Could not bind tablename to insert statement: %s", sqlerr);
152 }
153 if(SQL_BindParamString(dbDelete, 0, DBTABLENAME, true)) {
154 SQL_GetError(dbDelete, sqlerr, sizeof(sqlerr));
155 SQL_UnlockDatabase(db);
156 logAndFail("Could not bind tablename to delete statement: %s", sqlerr);
157 }
158 if( SQL_BindParamString(dbReplace, 0, DBTABLENAME, true)) {
159 SQL_GetError(dbReplace, sqlerr, sizeof(sqlerr));
160 SQL_UnlockDatabase(db);
161 logAndFail("Could not bind tablename to replace statement: %s", sqlerr);
162 }
163 if(SQL_BindParamString(dbPopulate, 0, DBTABLENAME, true)) {
164 SQL_GetError(dbPopulate, sqlerr, sizeof(sqlerr));
165 SQL_UnlockDatabase(db);
166 logAndFail("Could not bind tablename to populate statement: %s", sqlerr);
167 }
168 145
169 // If I knew how to do macros this would be much nicer 146 if((dbReplace = SQL_PrepareQuery(db, DBREPLACESTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err)
147 err = 3;
148
149 if((dbPopulate = SQL_PrepareQuery(db, DBPOPULATESTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err)
150 err = 4;
170 151
171 SQL_UnlockDatabase(db); 152 SQL_UnlockDatabase(db);
153 switch(err) {
154 case 1: {logAndFail("Could not prepare insert statement: %s", sqlerr);}
155 case 2: {logAndFail("Could not prepare delete statement: %s", sqlerr);}
156 case 3: {logAndFail("Could not prepare replace statement: %s", sqlerr);}
157 case 4: {logAndFail("Could not prepare populate statement: %s", sqlerr);}
158 }
159
172 return 0; 160 return 0;
173} 161}
174 162
175int loadFromDatabase() { 163int loadFromDatabase() {
176 // Initialize and populate datatypes 164 // Initialize and populate datatypes
177 regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); 165 regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE));
178 if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList"); 166 if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList");
179 167
180 patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN)); 168 patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN));
181 if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList"); 169 if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList");
182 170
171
183 // Get database handle 172 // Get database handle
184 char sqlerr[256 + 1]; 173 char sqlerr[256 + 1];
185 db = SQLite_UseDatabase("sourcemod-local", sqlerr, sizeof(sqlerr)); 174 db = SQLite_UseDatabase("sourcemod-local", sqlerr, sizeof(sqlerr));
186 if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr); 175 if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr);
187 176
188 // Initialize table if it doesn't exist 177 // Initialize table if it doesn't exist
189 // I could make this a prepared statement, but I don't believe it's entirely necessary 178 int err = 0;
190 char sqlcbuf[256 + 1];
191 Format(sqlcbuf, sizeof(sqlcbuf), "CREATE TABLE IF NOT EXISTS \"%s\" (id INTEGER NOT NULL, regexstr TEXT NOT NULL ON CONFLICT IGNORE, steamid64 TEXT NOT NULL ON CONFLICT IGNORE, dateof TEXT DEFAULT CURRENT_DATE, timeof TEXT DEFAULT CURRENT_TIME, PRIMARY KEY (id), UNIQUE (regexstr));", DBTABLENAME);
192
193 SQL_LockDatabase(db); 179 SQL_LockDatabase(db);
194 if(!SQL_FastQuery(db, sqlcbuf)) { 180 if(!SQL_FastQuery(db, DBCREATETABLE)) {
195 SQL_GetError(db, sqlerr, sizeof(sqlerr)); 181 SQL_GetError(db, sqlerr, sizeof(sqlerr));
196 SQL_UnlockDatabase(db); 182 err = 1;
197 logAndFail("Could not initialize nameblocker table: %s", sqlerr);
198 } 183 }
199 184
200 // select patterns from nameblock table/database 185 // select patterns from nameblock table/database
201 if(SQL_Execute(dbPopulate)) { 186 if(SQL_Execute(dbPopulate) && !err) {
202 SQL_GetError(dbPopulate, sqlerr, sizeof(sqlerr)); 187 SQL_GetError(dbPopulate, sqlerr, sizeof(sqlerr));
203 SQL_UnlockDatabase(db); 188 err = 2;
204 logAndFail("Population query failed");
205 } 189 }
190
206 SQL_UnlockDatabase(db); 191 SQL_UnlockDatabase(db);
192 switch(err) {
193 case 1: {logAndFail("Could not initialize nameblocker table: %s", sqlerr);}
194 case 2: {logAndFail("Population query failed: %s", sqlerr);}
195 }
196
207 197
208 // compile each pattern & insert 198 // compile each pattern & insert
209 Regex cur; char reerr[256]; RegexError reenum; 199 Regex cur; char reerr[256]; RegexError reenum;
@@ -231,10 +221,10 @@ public void OnAllPluginsLoaded() {
231 loadFromDatabase(); 221 loadFromDatabase();
232 222
233 // Register convars 223 // Register convars
234 xRegisterIntConVar(gcvarOperMode, view_as<int>(DEFAULTOPERMODE), OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)"); 224 xRegisterIntConVar(gcvarOperMode, DEFAULTOPERMODE, OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)");
235 xRegisterIntConVar(gcvarAdmCmdFlag, DEFAULTADMCMDFLAG, ADMCMDFLAGNAME, "Admin flag to modify pattern list"); 225 xRegisterIntConVar(gcvarAdmCmdFlag, DEFAULTADMCMDFLAG, ADMCMDFLAGNAME, "Admin flag to modify pattern list");
236 xRegisterIntConVar(gcvarRegexCompFlags, DEFAULTREGEXCOMPFLAGS, REGEXCOMPFLAGSNAME, "Regular expression compilation flags"); 226 xRegisterIntConVar(gcvarRegexCompFlags, DEFAULTREGEXCOMPFLAGS, REGEXCOMPFLAGSNAME, "Regular expression compilation flags");
237 xRegisterIntConVar(gcvarAmdImmFlag, DEFAULTADMIMMFLAG, ADMINIMMUNITYFLAGNAME, "Admin immunity flag"); 227 xRegisterIntConVar(gcvarAmdImmFlag, DEFAULTADMIMMFLAG, ADMINIMMUNITYFLAGNAME, "Admin immunity flag");
238 228
239 AutoExecConfig(true, "nameblocker_cvars"); 229 AutoExecConfig(true, "nameblocker_cvars");
240 230
@@ -341,49 +331,74 @@ enum MOD_MODE {
341 MM_TOOBIG 331 MM_TOOBIG
342}; 332};
343 333
344int modPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=0) { 334int __modPattern__insert(int index, char[] pattern, int patternlen, int client) {
345 if(index < 0 || index > regexlist.Length) return -1; 335 if(IsNullString(pattern) || patternlen < 0 || patternlen > PATTERN_MAX_LEN || client < 0) {return -1;}
346 336
347 switch(mode) { 337 char steamid64[STEAMID64LENGTH + 1];
348 case MM_INSERT: { 338 if(GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64))) {} // TODO: Error handling
349 if(IsNullString(pattern) || patternlen < 0 || patternlen > PATTERN_MAX_LEN) {return -1;}
350 339
351 // Add pattern to database 340 SQL_LockDatabase(db);
352 SQL_LockDatabase(db); 341 SQL_BindParamString(dbInsert, 0, pattern, false);
353 SQL_UnlockDatabase(db); 342 SQL_BindParamString(dbInsert, 1, steamid64, false);
343 if(!SQL_Execute(dbInsert)) {} // TODO: Error handling
344 SQL_UnlockDatabase(db);
354 345
355 aModPattern(mode, index, pattern); 346 if(aModPattern(MM_INSERT, index, pattern)) {} // TODO: Error handling
356 }
357 347
358 case MM_DELETE: { 348 return 0;
359 // Detele from database 349}
360 SQL_LockDatabase(db); 350int __modPattern__delete(int index) {
361 SQL_UnlockDatabase(db); 351 char pattern[PATTERN_MAX_LEN];
352 patternlist.GetString(index, pattern, sizeof(pattern), ByteCountToCells(PATTERN_MAX_LEN));
362 353
363 aModPattern(mode, index); 354 SQL_LockDatabase(db);
364 } 355 SQL_BindParamString(dbDelete, 0, pattern, false);
356 if(SQL_Execute(dbDelete)) {} // TODO: Error handling
357 SQL_UnlockDatabase(db);
365 358
366 case MM_REPLACE: { 359 if(aModPattern(MM_DELETE, index)) {} // TODO: Error handling
367 LogError("Replace mode not implemented yet"); 360
368 return -1; 361 return 0;
362}
363int __modPattern__replace(int index, char[] pattern, int patternlen, int client) {
364 if(IsNullString(pattern) || patternlen < 0 || patternlen > PATTERN_MAX_LEN || client < 0) {return -1;}
369 365
370 // Replace in database 366 char oldpattern[PATTERN_MAX_LEN];
371 // SQL_LockDatabase(db); 367 patternlist.GetString(index, oldpattern, sizeof(oldpattern), ByteCountToCells(PATTERN_MAX_LEN));
372 // SQL_UnlockDatabase(db);
373 368
374 // aModPattern(mode, index, pattern); 369 char steamid64[STEAMID64LENGTH];
375 } 370 if(GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64))) {} // TODO: Error handling
371
372 SQL_LockDatabase(db);
373 SQL_BindParamString(dbReplace, 0, pattern, false);
374 SQL_BindParamString(dbReplace, 1, steamid64, false);
375 SQL_BindParamString(dbReplace, 2, oldpattern, false);
376
377 if(SQL_Execute(dbReplace)) {} // TODO: Error handling
378 SQL_UnlockDatabase(db);
379
380 if(aModPattern(MM_REPLACE, index, pattern)) {} // TODO: Error handling
381
382 return 0;
383}
384
385int modPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=-1, int client=-1) {
386 if(index < 0 || index > regexlist.Length) return -1;
376 387
388 switch(mode) {
389 case MM_INSERT: {return __modPattern__insert(index, pattern, patternlen, client);}
390 case MM_DELETE: {return __modPattern__delete(index);}
391 case MM_REPLACE: {return __modPattern__replace(index, pattern, patternlen, client);}
377 default: { 392 default: {
378 LogError("Given invalid DBMOD_MODE"); 393 LogError("Given invalid DBMOD_MODE");
379 return -1; 394 return -1;
380 } 395 }
381 } 396 }
382
383 return 0;
384} 397}
385 398
386int aModPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=0) { 399int aModPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=0) {
400 LogError("aModPattern not implemented"); return -1;
401
387 if(mode <= MM_UNDEF || mode >= MM_TOOBIG || index < 0 || index > patternlist.Length) return -1; 402 if(mode <= MM_UNDEF || mode >= MM_TOOBIG || index < 0 || index > patternlist.Length) return -1;
388 403
389 if(mode > MM_DELETE) { 404 if(mode > MM_DELETE) {
@@ -448,7 +463,7 @@ int checkName(int client) {
448 m = MatchRegex(regexlist.Get(i), name, reerr); 463 m = MatchRegex(regexlist.Get(i), name, reerr);
449 464
450 if(m < 0) { 465 if(m < 0) {
451 handleFailedRegex(client, reerr); 466 handleFailedRegex(client);
452 return -1; 467 return -1;
453 } 468 }
454 if(m == 0) continue; 469 if(m == 0) continue;
@@ -481,87 +496,16 @@ int handleNameHit(int client) {
481 // BanClient() 496 // BanClient()
482 // TODO: Interop with other ban systems 497 // TODO: Interop with other ban systems
483 // Log ban 498 // Log ban
499 return 0;
484 } 500 }
485 default: { 501 default: {
486 LogError("%L failed a name check, but the operating mode in an invalid state", client); 502 LogError("%L failed a name check, but the operating mode in an invalid state", client);
487 return -1; 503 return -1;
488 } 504 }
489 } 505 }
490
491 LogError("Broke out of switch statement that shouldn't have happened");
492 return -1; // Shouldn't get to this point
493} 506}
494 507
495int handleFailedRegex(int client, RegexError reerr) { 508int handleFailedRegex(int client) {
496 char regstr[128]; 509 LogError("Ran into regex error when trying to check user %L's name", client);
497 RegexStrError(reerr, regstr, sizeof(regstr));
498
499 LogError("Ran into regex error when trying to check user %L's name. Reported regex error: %s", client, regstr);
500 return 0; 510 return 0;
501}
502
503
504
505// Note: May or may not be particularly descriptive for any given error
506int RegexStrError(RegexError err, char[] buf, int buflen) {
507 if(IsNullString(buf)) return -1;
508
509 char tmp[64];
510 switch(err) {
511 case REGEX_ERROR_NONE: {tmp = "No error";}
512
513 case REGEX_ERROR_ASSERT: {tmp = "Internal error";}
514 case REGEX_ERROR_BADBR: {tmp = "Invalid repeat counts in {}";}
515 case REGEX_ERROR_BADPAT: {tmp = "Pattern error";}
516 case REGEX_ERROR_BADRPT: {tmp = "? * + invalid";}
517 case REGEX_ERROR_EBRACE: {tmp = "Unbalanced {}";}
518 case REGEX_ERROR_EBRACK: {tmp = "Unbalanced []";}
519 case REGEX_ERROR_ECOLLATE: {tmp = "Collation error - not relevant";}
520 case REGEX_ERROR_ECTYPE: {tmp = "Bad class";}
521 case REGEX_ERROR_EESCAPE: {tmp = "Bad escape sequence";}
522 case REGEX_ERROR_EMPTY: {tmp = "Empty expression";}
523 case REGEX_ERROR_EPAREN: {tmp = "Unbalanced ()";}
524 case REGEX_ERROR_ERANGE: {tmp = "Bad range inside []";}
525 case REGEX_ERROR_ESIZE: {tmp = "Expression too big";}
526 case REGEX_ERROR_ESPACE: {tmp = "Failed to get memory";}
527 case REGEX_ERROR_ESUBREG: {tmp = "Bad back reference";}
528 case REGEX_ERROR_INVARG: {tmp = "Bad argument";}
529
530 case REGEX_ERROR_NOMATCH: {tmp = "No match was found";}
531 case REGEX_ERROR_NULL: {tmp = "Null";}
532 case REGEX_ERROR_BADOPTION: {tmp = "Bad Option";}
533 case REGEX_ERROR_BADMAGIC: {tmp = "Bad Magic";}
534 case REGEX_ERROR_UNKNOWN_OPCODE: {tmp = "Unknown OpCode";}
535 case REGEX_ERROR_NOMEMORY: {tmp = "No Memory";}
536 case REGEX_ERROR_NOSUBSTRING: {tmp = "No substring";}
537 case REGEX_ERROR_MATCHLIMIT: {tmp = "Match limit";}
538 case REGEX_ERROR_CALLOUT: {tmp = "Callout";} // Never used by PCRE itself
539 case REGEX_ERROR_BADUTF8: {tmp = "Bad UTF8";}
540 case REGEX_ERROR_BADUTF8_OFFSET: {tmp = "Bad UTF8 offset";}
541 case REGEX_ERROR_PARTIAL: {tmp = "Partial";}
542 case REGEX_ERROR_BADPARTIAL: {tmp = "Bad Partial";}
543 case REGEX_ERROR_INTERNAL: {tmp = "Internal error";}
544 case REGEX_ERROR_BADCOUNT: {tmp = "Bad count";}
545 case REGEX_ERROR_DFA_UITEM: {tmp = "DFA UItem";}
546 case REGEX_ERROR_DFA_UCOND: {tmp = "DFA UCOND";}
547 case REGEX_ERROR_DFA_UMLIMIT: {tmp = "DFA UMLIMIT";}
548 case REGEX_ERROR_DFA_WSSIZE: {tmp = "DFA WSSIZE";}
549 case REGEX_ERROR_DFA_RECURSE: {tmp = "DFA recurse";}
550 case REGEX_ERROR_RECURSIONLIMIT: {tmp = "Recursion Limit";}
551 case REGEX_ERROR_NULLWSLIMIT: {tmp = "NULL WSLIMIT";} /* No longer actually used */
552 case REGEX_ERROR_BADNEWLINE: {tmp = "Bad newline";}
553 case REGEX_ERROR_BADOFFSET: {tmp = "Bad offset";}
554 case REGEX_ERROR_SHORTUTF8: {tmp = "Short UFT8";}
555 case REGEX_ERROR_RECURSELOOP: {tmp = "Recurse loop";}
556 case REGEX_ERROR_JIT_STACKLIMIT: {tmp = "JIT Stacklimit";}
557 case REGEX_ERROR_BADMODE: {tmp = "Bad mode";}
558 case REGEX_ERROR_BADENDIANNESS: {tmp = "Bad endianness";}
559 case REGEX_ERROR_DFA_BADRESTART: {tmp = "DFA Bad Restart";}
560 case REGEX_ERROR_JIT_BADOPTION: {tmp = "JIT bad option";}
561 case REGEX_ERROR_BADLENGTH: {tmp = "Bad length";}
562
563 default: {tmp = "Unknown Error";}
564 }
565
566 return strcopy(buf, buflen, tmp);
567} \ No newline at end of file 511} \ No newline at end of file