// stringutil.cpp -- string utilities. // // $Id: stringutil.cpp,v 1.71 2005/08/05 17:11:07 sdennis Exp $ // // MUX 2.4 // Copyright (C) 1998 through 2004 Solid Vertical Domains, Ltd. All // rights not explicitly given are reserved. // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include "ansi.h" #include "pcre.h" const bool mux_isprint[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 7 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, // 8 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 // F }; const bool mux_isdigit[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_ishex[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isalpha[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 // F }; const bool mux_isalnum[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 // F }; const bool mux_isupper[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_islower[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 // F }; const bool mux_isspace[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // The first character of an attribute name must be either alphabetic, // '_', '#', '.', or '~'. It's handled by the following table. // // Characters thereafter may be letters, numbers, and characters from // the set {'?!`/-_.@#$^&~=+<>()}. Lower-case letters are turned into // uppercase before being used, but lower-case letters are valid input. // bool mux_AttrNameInitialSet[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 7 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 // F }; bool mux_AttrNameSet[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 7 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 // F }; // Valid characters for an object name are all printable // characters except those from the set {=&|}. // const bool mux_ObjectNameSet[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, // 7 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, // 8 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 // F }; // Valid characters for a player name are all alphanumeric plus // {`$_-.,'} plus SPACE depending on configuration. // bool mux_PlayerNameSet[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0 // F }; // Characters which should be escaped for the secure() // function: '%$\[](){},;'. // const bool mux_issecure[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Characters which should be escaped for the escape() // function: '%\[]{};,()^$'. // const bool mux_isescape[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool ANSI_TokenTerminatorTable[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const unsigned char mux_hex2dec[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 3 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const unsigned char mux_toupper[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 4 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 6 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 8 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x8A, 0x9B, 0x8C, 0x9D, 0x8E, 0x9F, // 9 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // A 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // B 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, // C 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, // D 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, // E 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xF7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xFF // F }; const unsigned char mux_tolower[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 4 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 6 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x9A, 0x8B, 0x9C, 0x8D, 0x9E, 0x8F, // 8 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0xFF, // 9 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // A 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // B 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, // C 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xD7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, // D 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, // E 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF // F }; const unsigned char mux_StripAccents[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 4 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 6 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 8 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // 9 0xA0, 0x21, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0x22, 0xAC, 0xAD, 0xAE, 0xAF, // A 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0x22, 0xBC, 0xBD, 0xBE, 0x3F, // B 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0xC6, 0x43, 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, // C 0x44, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0xD7, 0x4F, 0x55, 0x55, 0x55, 0x55, 0x59, 0x50, 0x42, // D 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xE6, 0x63, 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69, // E 0x6F, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0xF7, 0x6F, 0x75, 0x75, 0x75, 0x75, 0x79, 0x70, 0x79, // F }; // ANSI_lex - This function parses a string and returns two token types. // The type identifies the token type of length nLengthToken0. nLengthToken1 // may also be present and is a token of the -other- type. // int ANSI_lex(int nString, const char *pString, int *nLengthToken0, int *nLengthToken1) { *nLengthToken0 = 0; *nLengthToken1 = 0; const char *p = pString; for (;;) { // Look for an ESC_CHAR // p = strchr(p, ESC_CHAR); if (!p) { // This is the most common case by far. // *nLengthToken0 = nString; return TOKEN_TEXT_ANSI; } // We have an ESC_CHAR. Let's look at the next character. // if (p[1] != '[') { // Could be a '\0' or another non-'[' character. // Move the pointer to position ourselves over it. // And continue looking for an ESC_CHAR. // p = p + 1; continue; } // We found the beginning of an ANSI sequence. // Find the terminating character. // const char *q = p+2; while (ANSI_TokenTerminatorTable[(unsigned char)*q] == 0) { q++; } if (q[0] == '\0') { // There was no good terminator. Treat everything like text. // Also, we are at the end of the string, so just return. // *nLengthToken0 = q - pString; return TOKEN_TEXT_ANSI; } else { // We found an ANSI sequence. // if (p == pString) { // The ANSI sequence started it. // *nLengthToken0 = q - pString + 1; return TOKEN_ANSI; } else { // We have TEXT followed by an ANSI sequence. // *nLengthToken0 = p - pString; *nLengthToken1 = q - p + 1; return TOKEN_TEXT_ANSI; } } } } char *strip_ansi(const char *szString, size_t *pnString) { static char Buffer[LBUF_SIZE]; char *pBuffer = Buffer; const char *pString = szString; if (!pString) { if (pnString) { *pnString = 0; } *pBuffer = '\0'; return Buffer; } size_t nString = strlen(szString); while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { memcpy(pBuffer, pString, nTokenLength0); pBuffer += nTokenLength0; int nSkipLength = nTokenLength0 + nTokenLength1; nString -= nSkipLength; pString += nSkipLength; } else { // TOKEN_ANSI // nString -= nTokenLength0; pString += nTokenLength0; } } if (pnString) { *pnString = pBuffer - Buffer; } *pBuffer = '\0'; return Buffer; } char *strip_accents(const char *szString, size_t *pnString) { static char Buffer[LBUF_SIZE]; char *pBuffer = Buffer; const char *pString = szString; if (pString) { while (*pString) { *pBuffer = mux_StripAccents(*pString); pBuffer++; pString++; } } if (pnString) { *pnString = pBuffer - Buffer; } *pBuffer = '\0'; return Buffer; } #define ANSI_COLOR_INDEX_BLACK 0 #define ANSI_COLOR_INDEX_RED 1 #define ANSI_COLOR_INDEX_GREEN 2 #define ANSI_COLOR_INDEX_YELLOW 3 #define ANSI_COLOR_INDEX_BLUE 4 #define ANSI_COLOR_INDEX_MAGENTA 5 #define ANSI_COLOR_INDEX_CYAN 6 #define ANSI_COLOR_INDEX_WHITE 7 #define ANSI_COLOR_INDEX_DEFAULT 8 const ANSI_ColorState acsRestingStates[3] = { {true, false, false, false, false, ANSI_COLOR_INDEX_DEFAULT, ANSI_COLOR_INDEX_DEFAULT}, {false, false, false, false, false, ANSI_COLOR_INDEX_WHITE, ANSI_COLOR_INDEX_DEFAULT}, {true, false, false, false, false, ANSI_COLOR_INDEX_DEFAULT, ANSI_COLOR_INDEX_DEFAULT} }; void ANSI_Parse_m(ANSI_ColorState *pacsCurrent, int nANSI, const char *pANSI, bool *pbSawNormal) { // If the last character isn't an 'm', then it's an ANSI sequence we // don't support, yet. TODO: There should be a ANSI_Parse() function // that calls into this one -only- if there's an 'm', but since 'm' // is the only command this game understands at the moment, it's easier // to put the test here. // if (pANSI[nANSI-1] != 'm') { return; } // Process entire string and update the current color state structure. // while (nANSI) { // Process the next attribute phrase (terminated by ';' or 'm' // typically). // const char *p = pANSI; while (mux_isdigit(*p)) { p++; } size_t nLen = p - pANSI + 1; if (p[0] == 'm' || p[0] == ';') { // We have an attribute. // if (nLen == 2) { int iCode = pANSI[0] - '0'; switch (iCode) { case 0: // Normal. // *pacsCurrent = acsRestingStates[ANSI_ENDGOAL_NORMAL]; *pbSawNormal = true; break; case 1: // High Intensity. // pacsCurrent->bHighlite = true; pacsCurrent->bNormal = false; break; case 2: // Low Intensity. // pacsCurrent->bHighlite = false; pacsCurrent->bNormal = false; break; case 4: // Underline. // pacsCurrent->bUnder = true; pacsCurrent->bNormal = false; break; case 5: // Blinking. // pacsCurrent->bBlink = true; pacsCurrent->bNormal = false; break; case 7: // Reverse Video // pacsCurrent->bInverse = true; pacsCurrent->bNormal = false; break; } } else if (nLen == 3) { int iCode0 = pANSI[0] - '0'; int iCode1 = pANSI[1] - '0'; if (iCode0 == 3) { // Foreground Color // if (iCode1 <= 7) { pacsCurrent->iForeground = iCode1; pacsCurrent->bNormal = false; } } else if (iCode0 == 4) { // Background Color // if (iCode1 <= 7) { pacsCurrent->iBackground = iCode1; pacsCurrent->bNormal = false; } } } } pANSI += nLen; nANSI -= nLen; } } // The following is really 30 (E[0mE[1mE[4mE[5mE[7mE[33mE[43m) but we are // being conservative. // #define ANSI_MAXIMUM_BINARY_TRANSITION_LENGTH 60 // Generate the minimal ANSI sequence that will transition from one color state // to another. // char *ANSI_TransitionColorBinary ( ANSI_ColorState *acsCurrent, const ANSI_ColorState *pcsNext, int *nTransition, int iEndGoal ) { static char Buffer[ANSI_MAXIMUM_BINARY_TRANSITION_LENGTH+1]; if (memcmp(acsCurrent, pcsNext, sizeof(ANSI_ColorState)) == 0) { *nTransition = 0; Buffer[0] = '\0'; return Buffer; } ANSI_ColorState tmp = *acsCurrent; char *p = Buffer; if (pcsNext->bNormal) { // With NOBLEED, we can't stay in the normal mode. We must eventually // be on a white foreground. // pcsNext = &acsRestingStates[iEndGoal]; } // Do we need to go through the normal state? // if ( tmp.bHighlite && !pcsNext->bHighlite || tmp.bUnder && !pcsNext->bUnder || tmp.bBlink && !pcsNext->bBlink || tmp.bInverse && !pcsNext->bInverse || ( tmp.iBackground != ANSI_COLOR_INDEX_DEFAULT && pcsNext->iBackground == ANSI_COLOR_INDEX_DEFAULT) || ( tmp.iForeground != ANSI_COLOR_INDEX_DEFAULT && pcsNext->iForeground == ANSI_COLOR_INDEX_DEFAULT)) { memcpy(p, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1); p += sizeof(ANSI_NORMAL)-1; tmp = acsRestingStates[ANSI_ENDGOAL_NORMAL]; } if (tmp.bHighlite != pcsNext->bHighlite) { memcpy(p, ANSI_HILITE, sizeof(ANSI_HILITE)-1); p += sizeof(ANSI_HILITE)-1; } if (tmp.bUnder != pcsNext->bUnder) { memcpy(p, ANSI_UNDER, sizeof(ANSI_UNDER)-1); p += sizeof(ANSI_UNDER)-1; } if (tmp.bBlink != pcsNext->bBlink) { memcpy(p, ANSI_BLINK, sizeof(ANSI_BLINK)-1); p += sizeof(ANSI_BLINK)-1; } if (tmp.bInverse != pcsNext->bInverse) { memcpy(p, ANSI_INVERSE, sizeof(ANSI_INVERSE)-1); p += sizeof(ANSI_INVERSE)-1; } if (tmp.iForeground != pcsNext->iForeground) { memcpy(p, ANSI_FOREGROUND, sizeof(ANSI_FOREGROUND)-1); p += sizeof(ANSI_FOREGROUND)-1; *p++ = pcsNext->iForeground + '0'; *p++ = ANSI_ATTR_CMD; } if (tmp.iBackground != pcsNext->iBackground) { memcpy(p, ANSI_BACKGROUND, sizeof(ANSI_BACKGROUND)-1); p += sizeof(ANSI_BACKGROUND)-1; *p++ = pcsNext->iBackground + '0'; *p++ = ANSI_ATTR_CMD; } *p = '\0'; *nTransition = p - Buffer; return Buffer; } // The following is really 21 (%xn%xh%xu%xi%xf%xR%xr) but we are being conservative // #define ANSI_MAXIMUM_ESCAPE_TRANSITION_LENGTH 42 // Generate the minimal MU ANSI %-sequence that will transition from one color state // to another. // char *ANSI_TransitionColorEscape ( ANSI_ColorState *acsCurrent, ANSI_ColorState *acsNext, int *nTransition ) { static char Buffer[ANSI_MAXIMUM_ESCAPE_TRANSITION_LENGTH+1]; static const char cForegroundColors[9] = "xrgybmcw"; static const char cBackgroundColors[9] = "XRGYBMCW"; if (memcmp(acsCurrent, acsNext, sizeof(ANSI_ColorState)) == 0) { *nTransition = 0; Buffer[0] = '\0'; return Buffer; } ANSI_ColorState tmp = *acsCurrent; int i = 0; // Do we need to go through the normal state? // if ( tmp.bBlink && !acsNext->bBlink || tmp.bHighlite && !acsNext->bHighlite || tmp.bInverse && !acsNext->bInverse || ( tmp.iBackground != ANSI_COLOR_INDEX_DEFAULT && acsNext->iBackground == ANSI_COLOR_INDEX_DEFAULT) || ( tmp.iForeground != ANSI_COLOR_INDEX_DEFAULT && acsNext->iForeground == ANSI_COLOR_INDEX_DEFAULT)) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = 'n'; i = i + 3; tmp = acsRestingStates[ANSI_ENDGOAL_NORMAL]; } if (tmp.bHighlite != acsNext->bHighlite) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = 'h'; i = i + 3; } if (tmp.bUnder != acsNext->bUnder) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = 'u'; i = i + 3; } if (tmp.bBlink != acsNext->bBlink) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = 'f'; i = i + 3; } if (tmp.bInverse != acsNext->bInverse) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = 'i'; i = i + 3; } if (tmp.iForeground != acsNext->iForeground) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = cForegroundColors[acsNext->iForeground]; i = i + 3; } if (tmp.iBackground != acsNext->iBackground) { Buffer[i ] = '%'; Buffer[i+1] = 'x'; Buffer[i+2] = cBackgroundColors[acsNext->iBackground]; i = i + 3; } Buffer[i] = '\0'; *nTransition = i; return Buffer; } void ANSI_String_In_Init ( struct ANSI_In_Context *pacIn, const char *szString, int iEndGoal ) { pacIn->m_acs = acsRestingStates[iEndGoal]; pacIn->m_p = szString; pacIn->m_n = strlen(szString); pacIn->m_bSawNormal = false; } void ANSI_String_Out_Init ( struct ANSI_Out_Context *pacOut, char *pField, int nField, int vwMax, int iEndGoal ) { pacOut->m_acs = acsRestingStates[ANSI_ENDGOAL_NORMAL]; pacOut->m_bDone = false; pacOut->m_iEndGoal = iEndGoal; pacOut->m_n = 0; pacOut->m_nMax = nField; pacOut->m_p = pField; pacOut->m_vw = 0; pacOut->m_vwMax = vwMax; } void ANSI_String_Skip ( struct ANSI_In_Context *pacIn, int maxVisualWidth, int *pnVisualWidth) { *pnVisualWidth = 0; while (pacIn->m_n) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(pacIn->m_n, pacIn->m_p, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // Process TEXT // int nTextToSkip = maxVisualWidth - *pnVisualWidth; if (nTokenLength0 > nTextToSkip) { // We have reached the limits of the field // *pnVisualWidth += nTextToSkip; pacIn->m_p += nTextToSkip; pacIn->m_n -= nTextToSkip; return; } pacIn->m_p += nTokenLength0; pacIn->m_n -= nTokenLength0; *pnVisualWidth += nTokenLength0; if (nTokenLength1) { // Process ANSI // ANSI_Parse_m(&(pacIn->m_acs), nTokenLength1, pacIn->m_p, &(pacIn->m_bSawNormal)); pacIn->m_p += nTokenLength1; pacIn->m_n -= nTokenLength1; } } else { // Process ANSI // ANSI_Parse_m(&(pacIn->m_acs), nTokenLength0, pacIn->m_p, &(pacIn->m_bSawNormal)); pacIn->m_n -= nTokenLength0; pacIn->m_p += nTokenLength0; } } } // TODO: Rework comment block. // // ANSI_String_Copy -- Copy characters into a buffer starting at // pField0 with maximum size of nField. Truncate the string if it would // overflow the buffer -or- if it would have a visual with of greater // than maxVisualWidth. Returns the number of ANSI-encoded characters // copied to. Also, the visual width produced by this is returned in // *pnVisualWidth. // // There are three ANSI color states that we deal with in this routine: // // 1. acsPrevious is the color state at the current end of the field. // It has already been encoded into the field. // // 2. acsCurrent is the color state that the current TEXT will be shown // with. It hasn't been encoded into the field, yet, and if we don't // have enough room for at least one character of TEXT, then it may // never be encoded into the field. // // 3. acsFinal is the required color state at the end. This is usually // the normal state or in the case of NOBLEED, it's a specific (and // somewhate arbitrary) foreground/background combination. // void ANSI_String_Copy ( struct ANSI_Out_Context *pacOut, struct ANSI_In_Context *pacIn, int maxVisualWidth0 ) { // Check whether we have previous struck the session limits (given // by ANSI_String_Out_Init() for field size or visual width. // if (pacOut->m_bDone) { return; } // What is the working limit for visual width. // int vw = 0; int vwMax = pacOut->m_vwMax; if (maxVisualWidth0 < vwMax) { vwMax = maxVisualWidth0; } // What is the working limit for field size. // int nMax = pacOut->m_nMax; char *pField = pacOut->m_p; while (pacIn->m_n) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(pacIn->m_n, pacIn->m_p, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // We have a TEXT+[ANSI] phrase. The text length is given // by nTokenLength0, and the ANSI characters that follow // (if present) are of length nTokenLength1. // // Process TEXT part first. // // TODO: If there is a maximum size for the transitions, // and we have gobs of space, don't bother calculating // sizes so carefully. It might be faster // nFieldEffective is used to allocate and plan space for // the rest of the physical field (given by the current // nField length). // int nFieldEffective = nMax - 1; // Leave room for '\0'. int nTransitionFinal = 0; if (pacOut->m_iEndGoal <= ANSI_ENDGOAL_NOBLEED) { // If we lay down -any- of the TEXT part, we need to make // sure we always leave enough room to get back to the // required final ANSI color state. // if (memcmp( &(pacIn->m_acs), &acsRestingStates[pacOut->m_iEndGoal], sizeof(ANSI_ColorState)) != 0) { // The color state of the TEXT isn't the final state, // so how much room will the transition back to the // final state take? // ANSI_TransitionColorBinary( &(pacIn->m_acs), &acsRestingStates[pacOut->m_iEndGoal], &nTransitionFinal, pacOut->m_iEndGoal); nFieldEffective -= nTransitionFinal; } } // If we lay down -any- of the TEXT part, it needs to be // the right color. // int nTransition = 0; char *pTransition = ANSI_TransitionColorBinary( &(pacOut->m_acs), &(pacIn->m_acs), &nTransition, pacOut->m_iEndGoal); nFieldEffective -= nTransition; // If we find that there is no room for any of the TEXT, // then we're done. // // TODO: The visual width test can be done further up to save time. // if ( nFieldEffective <= nTokenLength0 || vw + nTokenLength0 > vwMax) { // We have reached the limits of the field. // if (nFieldEffective > 0) { // There was enough physical room in the field, but // we would have exceeded the maximum visual width // if we used all the text. // if (nTransition) { // Encode the TEXT color. // memcpy(pField, pTransition, nTransition); pField += nTransition; } // Place just enough of the TEXT in the field. // int nTextToAdd = vwMax - vw; if (nTextToAdd < nFieldEffective) { nFieldEffective = nTextToAdd; } memcpy(pField, pacIn->m_p, nFieldEffective); pField += nFieldEffective; pacIn->m_p += nFieldEffective; pacIn->m_n -= nFieldEffective; vw += nFieldEffective; pacOut->m_acs = pacIn->m_acs; // Was this visual width limit related to the session or // the call? // if (vwMax != maxVisualWidth0) { pacOut->m_bDone = true; } } else { // Was size limit related to the session or the call? // pacOut->m_bDone = true; } pacOut->m_n += pField - pacOut->m_p; pacOut->m_nMax -= pField - pacOut->m_p; pacOut->m_p = pField; pacOut->m_vw += vw; return; } if (nTransition) { memcpy(pField, pTransition, nTransition); pField += nTransition; nMax -= nTransition; } memcpy(pField, pacIn->m_p, nTokenLength0); pField += nTokenLength0; nMax -= nTokenLength0; pacIn->m_p += nTokenLength0; pacIn->m_n -= nTokenLength0; vw += nTokenLength0; pacOut->m_acs = pacIn->m_acs; if (nTokenLength1) { // Process ANSI // ANSI_Parse_m(&(pacIn->m_acs), nTokenLength1, pacIn->m_p, &(pacIn->m_bSawNormal)); pacIn->m_p += nTokenLength1; pacIn->m_n -= nTokenLength1; } } else { // Process ANSI // ANSI_Parse_m(&(pacIn->m_acs), nTokenLength0, pacIn->m_p, &(pacIn->m_bSawNormal)); pacIn->m_n -= nTokenLength0; pacIn->m_p += nTokenLength0; } } pacOut->m_n += pField - pacOut->m_p; pacOut->m_nMax -= pField - pacOut->m_p; pacOut->m_p = pField; pacOut->m_vw += vw; } int ANSI_String_Finalize ( struct ANSI_Out_Context *pacOut, int *pnVisualWidth ) { char *pField = pacOut->m_p; if (pacOut->m_iEndGoal <= ANSI_ENDGOAL_NOBLEED) { int nTransition = 0; char *pTransition = ANSI_TransitionColorBinary( &(pacOut->m_acs), &acsRestingStates[pacOut->m_iEndGoal], &nTransition, pacOut->m_iEndGoal); if (nTransition) { memcpy(pField, pTransition, nTransition); pField += nTransition; } } *pField = '\0'; pacOut->m_n += pField - pacOut->m_p; pacOut->m_p = pField; *pnVisualWidth = pacOut->m_vw; return pacOut->m_n; } // Take an ANSI string and fit as much of the information as possible // into a field of size nField. Truncate text. Also make sure that no color // leaks out of the field. // int ANSI_TruncateToField ( const char *szString, int nField, char *pField0, int maxVisualWidth, int *pnVisualWidth, int iEndGoal ) { if (!szString) { pField0[0] = '\0'; return 0; } struct ANSI_In_Context aic; struct ANSI_Out_Context aoc; ANSI_String_In_Init(&aic, szString, iEndGoal); ANSI_String_Out_Init(&aoc, pField0, nField, maxVisualWidth, iEndGoal); ANSI_String_Copy(&aoc, &aic, maxVisualWidth); return ANSI_String_Finalize(&aoc, pnVisualWidth); } char *ANSI_TruncateAndPad_sbuf(const char *pString, int nMaxVisualWidth, char fill) { char *pStringModified = alloc_sbuf("ANSI_TruncateAndPad_sbuf"); int nAvailable = SBUF_SIZE - nMaxVisualWidth; int nVisualWidth; int nLen = ANSI_TruncateToField(pString, nAvailable, pStringModified, nMaxVisualWidth, &nVisualWidth, ANSI_ENDGOAL_NORMAL); for (int i = nMaxVisualWidth - nVisualWidth; i > 0; i--) { pStringModified[nLen] = fill; nLen++; } pStringModified[nLen] = '\0'; return pStringModified; } char *normal_to_white(const char *szString) { static char Buffer[LBUF_SIZE]; int nVisualWidth; ANSI_TruncateToField( szString, sizeof(Buffer), Buffer, sizeof(Buffer), &nVisualWidth, ANSI_ENDGOAL_NOBLEED ); return Buffer; } typedef struct { int len; char *p; } LITERAL_STRING_STRUCT; LITERAL_STRING_STRUCT MU_Substitutes[] = { { 1, " " }, // 0 { 1, " " }, // 1 { 2, "%t" }, // 2 { 2, "%r" }, // 3 { 0, NULL }, // 4 { 2, "%b" }, // 5 { 2, "%%" }, // 6 { 2, "%(" }, // 7 { 2, "%)" }, // 8 { 2, "%[" }, // 9 { 2, "%]" }, // 10 { 2, "%{" }, // 11 { 2, "%}" }, // 12 { 2, "\\\\" } // 13 }; const unsigned char MU_EscapeConvert[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 0, 6, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,13,10, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,11, 0,12, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const unsigned char MU_EscapeNoConvert[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Convert raw character sequences into MUX substitutions (type = 1) // or strips them (type = 0). // char *translate_string(const char *szString, bool bConvert) { static char szTranslatedString[LBUF_SIZE]; char *pTranslatedString = szTranslatedString; const char *pString = szString; if (!szString) { *pTranslatedString = '\0'; return szTranslatedString; } size_t nString = strlen(szString); ANSI_ColorState acsCurrent; ANSI_ColorState acsPrevious; acsCurrent = acsRestingStates[ANSI_ENDGOAL_NOBLEED]; acsPrevious = acsCurrent; bool bSawNormal = false; const unsigned char *MU_EscapeChar = (bConvert)? MU_EscapeConvert : MU_EscapeNoConvert; while (nString) { int nTokenLength0; int nTokenLength1; int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1); if (iType == TOKEN_TEXT_ANSI) { // Process TEXT // int nTransition = 0; if (bConvert) { char *pTransition = ANSI_TransitionColorEscape(&acsPrevious, &acsCurrent, &nTransition); safe_str(pTransition, szTranslatedString, &pTranslatedString); } nString -= nTokenLength0; while (nTokenLength0--) { unsigned char ch = *pString++; unsigned char code = MU_EscapeChar[ch]; if (code) { // The following can look one ahead off the end of the // current token (and even at the '\0' at the end of the // string, but this is acceptable. An extra look will // always see either ESC from the next ANSI sequence, // or the '\0' on the end of the string. No harm done. // if (ch == ' ' && pString[0] == ' ') { code = 5; } safe_copy_buf(MU_Substitutes[code].p, MU_Substitutes[code].len, szTranslatedString, &pTranslatedString); } else { safe_chr(ch, szTranslatedString, &pTranslatedString); } } acsPrevious = acsCurrent; if (nTokenLength1) { // Process ANSI // ANSI_Parse_m(&acsCurrent, nTokenLength1, pString, &bSawNormal); pString += nTokenLength1; nString -= nTokenLength1; } } else { // Process ANSI // ANSI_Parse_m(&acsCurrent, nTokenLength0, pString, &bSawNormal); nString -= nTokenLength0; pString += nTokenLength0; } } *pTranslatedString = '\0'; return szTranslatedString; } /* --------------------------------------------------------------------------- * munge_space: Compress multiple spaces to one space, also remove leading and * trailing spaces. */ char *munge_space(const char *string) { char *buffer = alloc_lbuf("munge_space"); const char *p = string; char *q = buffer; if (p) { // Remove initial spaces. // while (mux_isspace(*p)) p++; while (*p) { while (*p && !mux_isspace(*p)) *q++ = *p++; while (mux_isspace(*p)) { p++; } if (*p) *q++ = ' '; } } // Remove terminal spaces and terminate string. // *q = '\0'; return buffer; } /* --------------------------------------------------------------------------- * trim_spaces: Remove leading and trailing spaces. */ char *trim_spaces(char *string) { char *buffer = alloc_lbuf("trim_spaces"); char *p = string; char *q = buffer; if (p) { // Remove initial spaces. // while (mux_isspace(*p)) { p++; } while (*p) { // Copy non-space characters. // while (*p && !mux_isspace(*p)) { *q++ = *p++; } // Compress spaces. // while (mux_isspace(*p)) { p++; } // Leave one space. // if (*p) { *q++ = ' '; } } } // Terminate string. // *q = '\0'; return buffer; } /* * --------------------------------------------------------------------------- * * grabto: Return portion of a string up to the indicated character. Also * * returns a modified pointer to the string ready for another call. */ char *grabto(char **str, char targ) { char *savec, *cp; if (!str || !*str || !**str) return NULL; savec = cp = *str; while (*cp && *cp != targ) cp++; if (*cp) *cp++ = '\0'; *str = cp; return savec; } int string_compare(const char *s1, const char *s2) { if ( mudstate.bStandAlone || mudconf.space_compress) { while (mux_isspace(*s1)) { s1++; } while (mux_isspace(*s2)) { s2++; } while ( *s1 && *s2 && ( (mux_tolower(*s1) == mux_tolower(*s2)) || (mux_isspace(*s1) && mux_isspace(*s2)))) { if (mux_isspace(*s1) && mux_isspace(*s2)) { // skip all other spaces. // do { s1++; } while (mux_isspace(*s1)); do { s2++; } while (mux_isspace(*s2)); } else { s1++; s2++; } } if ( *s1 && *s2) { return 1; } if (mux_isspace(*s1)) { while (mux_isspace(*s1)) { s1++; } return *s1; } if (mux_isspace(*s2)) { while (mux_isspace(*s2)) { s2++; } return *s2; } if ( *s1 || *s2) { return 1; } return 0; } else { return mux_stricmp(s1, s2); } } int string_prefix(const char *string, const char *prefix) { int count = 0; while (*string && *prefix && (mux_tolower(*string) == mux_tolower(*prefix))) { string++, prefix++, count++; } if (*prefix == '\0') { // Matched all of prefix. // return count; } else { return 0; } } /* * accepts only nonempty matches starting at the beginning of a word */ const char *string_match(const char *src, const char *sub) { if ((*sub != '\0') && (src)) { while (*src) { if (string_prefix(src, sub)) { return src; } // else scan to beginning of next word // while (mux_isalnum(*src)) { src++; } while (*src && !mux_isalnum(*src)) { src++; } } } return 0; } /* * --------------------------------------------------------------------------- * * replace_string: Returns an lbuf containing string STRING with all occurances * * of OLD replaced by NEW. OLD and NEW may be different lengths. * * (mitch 1 feb 91) */ char *replace_string(const char *old, const char *new0, const char *s) { if (!s) { return NULL; } size_t olen = strlen(old); char *result = alloc_lbuf("replace_string"); char *r = result; while (*s) { // Find next occurrence of the first character of OLD string. // const char *p; if ( olen && (p = strchr(s, old[0]))) { // Copy up to the next occurrence of the first char of OLD. // size_t n = p - s; if (n) { safe_copy_buf(s, n, result, &r); s += n; } // If we are really at an complete OLD, append NEW to the result // and bump the input string past the occurrence of OLD. // Otherwise, copy the character and try matching again. // if (!strncmp(old, s, olen)) { safe_str(new0, result, &r); s += olen; } else { safe_chr(*s, result, &r); s++; } } else { // Finish copying source string. No matches. No further // work to perform. // safe_str(s, result, &r); break; } } *r = '\0'; return result; } // --------------------------------------------------------------------------- // replace_tokens: Performs ## and #@ substitution. // char *replace_tokens ( const char *s, const char *pBound, const char *pListPlace, const char *pSwitch ) { if (!s) { return NULL; } char *result = alloc_lbuf("replace_tokens"); char *r = result; while (*s) { // Find next '#'. // const char *p = strchr(s, '#'); if (p) { // Copy up to the next occurrence of the first character. // size_t n = p - s; if (n) { safe_copy_buf(s, n, result, &r); s += n; } if ( s[1] == '#' && pBound) { // BOUND_VAR // safe_str(pBound, result, &r); s += 2; } else if ( s[1] == '@' && pListPlace) { // LISTPLACE_VAR // safe_str(pListPlace, result, &r); s += 2; } else if ( s[1] == '$' && pSwitch) { // SWITCH_VAR // safe_str(pSwitch, result, &r); s += 2; } else { safe_chr(*s, result, &r); s++; } } else { // Finish copying source string. No matches. No further // work to perform. // safe_str(s, result, &r); break; } } *r = '\0'; return result; } #if 0 // Returns the number of identical characters in the two strings. // int prefix_match(const char *s1, const char *s2) { int count = 0; while (*s1 && *s2 && (mux_tolower(*s1) == mux_tolower(*s2))) { s1++, s2++, count++; } // If the whole string matched, count the null. (Yes really.) // if (!*s1 && !*s2) { count++; } return count; } #endif // 0 bool minmatch(char *str, char *target, int min) { while (*str && *target && (mux_tolower(*str) == mux_tolower(*target))) { str++; target++; min--; } if (*str) { return false; } if (!*target) { return true; } return (min <= 0); } // -------------------------------------------------------------------------- // StringCloneLen: allocate memory and copy string // char *StringCloneLen(const char *str, size_t nStr) { char *buff = (char *)MEMALLOC(nStr+1); ISOUTOFMEMORY(buff); memcpy(buff, str, nStr); buff[nStr] = '\0'; return buff; } // -------------------------------------------------------------------------- // StringClone: allocate memory and copy string // char *StringClone(const char *str) { return StringCloneLen(str, strlen(str)); } #if 0 // -------------------------------------------------------------------------- // BufferCloneLen: allocate memory and copy buffer // char *BufferCloneLen(const char *pBuffer, unsigned int nBuffer) { char *buff = (char *)MEMALLOC(nBuffer); ISOUTOFMEMORY(buff); memcpy(buff, pBuffer, nBuffer); return buff; } #endif // 0 /* --------------------------------------------------------------------------- * safe_copy_str, safe_copy_chr - Copy buffers, watching for overflows. */ void safe_copy_str(const char *src, char *buff, char **bufp, int nSizeOfBuffer) { if (src == NULL) return; char *tp = *bufp; char *maxtp = buff + nSizeOfBuffer; while (tp < maxtp && *src) { *tp++ = *src++; } *bufp = tp; } void safe_copy_str_lbuf(const char *src, char *buff, char **bufp) { if (src == NULL) { return; } char *tp = *bufp; char *maxtp = buff + LBUF_SIZE - 1; while (tp < maxtp && *src) { *tp++ = *src++; } *bufp = tp; } size_t safe_copy_buf(const char *src, size_t nLen, char *buff, char **bufc) { size_t left = LBUF_SIZE - (*bufc - buff) - 1; if (left < nLen) { nLen = left; } memcpy(*bufc, src, nLen); *bufc += nLen; return nLen; } size_t safe_fill(char *buff, char **bufc, char chFill, size_t nSpaces) { // Check for buffer limits. // size_t nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (nSpaces > nBufferAvailable) { nSpaces = nBufferAvailable; } // Fill with spaces. // memset(*bufc, chFill, nSpaces); *bufc += nSpaces; return nSpaces; } bool matches_exit_from_list(char *str, const char *pattern) { char *s; while (*pattern) { for (s = str; // check out this one ( *s && (mux_tolower(*s) == mux_tolower(*pattern)) && *pattern && (*pattern != EXIT_DELIMITER)); s++, pattern++) ; // Did we match it all? // if (*s == '\0') { // Make sure nothing afterwards // while (mux_isspace(*pattern)) { pattern++; } // Did we get it? // if ( !*pattern || (*pattern == EXIT_DELIMITER)) { return true; } } // We didn't get it, find next string to test // while ( *pattern && *pattern++ != EXIT_DELIMITER) { ; // Nothing. } while (mux_isspace(*pattern)) { pattern++; } } return false; } const char Digits100[201] = "001020304050607080900111213141516171819102122232425262728292\ 031323334353637383930414243444546474849405152535455565758595\ 061626364656667686960717273747576777879708182838485868788898\ 09192939495969798999"; size_t mux_ltoa(long val, char *buf) { char *p = buf; if (val < 0) { *p++ = '-'; val = -val; } unsigned long uval = (unsigned long)val; char *q = p; const char *z; while (uval > 99) { z = Digits100 + ((uval % 100) << 1); uval /= 100; *p++ = *z; *p++ = *(z+1); } z = Digits100 + (uval << 1); *p++ = *z; if (uval > 9) { *p++ = *(z+1); } size_t nLength = p - buf; *p-- = '\0'; // The digits are in reverse order with a possible leading '-' // if the value was negative. q points to the first digit, // and p points to the last digit. // while (q < p) { // Swap characters are *p and *q // char temp = *p; *p = *q; *q = temp; // Move p and first digit towards the middle. // --p; ++q; // Stop when we reach or pass the middle. // } return nLength; } char *mux_ltoa_t(long val) { static char buff[12]; mux_ltoa(val, buff); return buff; } void safe_ltoa(long val, char *buff, char **bufc) { static char temp[12]; size_t n = mux_ltoa(val, temp); safe_copy_buf(temp, n, buff, bufc); } size_t mux_i64toa(INT64 val, char *buf) { char *p = buf; if (val < 0) { *p++ = '-'; val = -val; } UINT64 uval = (UINT64)val; char *q = p; const char *z; while (uval > 99) { z = Digits100 + ((uval % 100) << 1); uval /= 100; *p++ = *z; *p++ = *(z+1); } z = Digits100 + (uval << 1); *p++ = *z; if (uval > 9) { *p++ = *(z+1); } size_t nLength = p - buf; *p-- = '\0'; // The digits are in reverse order with a possible leading '-' // if the value was negative. q points to the first digit, // and p points to the last digit. // while (q < p) { // Swap characters are *p and *q // char temp = *p; *p = *q; *q = temp; // Move p and first digit towards the middle. // --p; ++q; // Stop when we reach or pass the middle. // } return nLength; } #if 0 char *mux_i64toa_t(INT64 val) { static char buff[22]; mux_i64toa(val, buff); return buff; } #endif void safe_i64toa(INT64 val, char *buff, char **bufc) { static char temp[22]; size_t n = mux_i64toa(val, temp); safe_copy_buf(temp, n, buff, bufc); } const char TableATOI[16][10] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}, { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}, { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}, { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, { 60, 61, 62, 63, 64, 65, 66, 67, 68, 69}, { 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89}, { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99} }; long mux_atol(const char *pString) { long sum = 0; int LeadingCharacter = 0; // Convert ASCII digits // unsigned int c1; unsigned int c0 = pString[0]; if (!mux_isdigit(c0)) { while (mux_isspace(pString[0])) { pString++; } LeadingCharacter = pString[0]; if ( LeadingCharacter == '-' || LeadingCharacter == '+') { pString++; } c0 = pString[0]; if (!mux_isdigit(c0)) { return 0; } } do { c1 = pString[1]; if (mux_isdigit(c1)) { sum = 100 * sum + TableATOI[c0-'0'][c1-'0']; pString += 2; } else { sum = 10 * sum + (c0-'0'); break; } } while (mux_isdigit(c0 = pString[0])); // Interpret sign // if (LeadingCharacter == '-') { sum = -sum; } return sum; } INT64 mux_atoi64(const char *pString) { INT64 sum = 0; int LeadingCharacter = 0; // Convert ASCII digits // unsigned int c1; unsigned int c0 = pString[0]; if (!mux_isdigit(c0)) { while (mux_isspace(pString[0])) { pString++; } LeadingCharacter = pString[0]; if ( LeadingCharacter == '-' || LeadingCharacter == '+') { pString++; } c0 = pString[0]; if (!mux_isdigit(c0)) { return 0; } } do { c1 = pString[1]; if (mux_isdigit(c1)) { sum = 100 * sum + TableATOI[c0-'0'][c1-'0']; pString += 2; } else { sum = 10 * sum + (c0-'0'); break; } } while (mux_isdigit(c0 = pString[0])); // Interpret sign // if (LeadingCharacter == '-') { sum = -sum; } return sum; } // Floating-point strings match one of the following patterns: // // [+-]?[0-9]?(.[0-9]+)([eE][+-]?[0-9]{1,3})? // [+-]?[0-9]+(.[0-9]?)([eE][+-]?[0-9]{1,3})? // +Inf // -Inf // Ind // NaN // bool ParseFloat(PARSE_FLOAT_RESULT *pfr, const char *str, bool bStrict) { memset(pfr, 0, sizeof(PARSE_FLOAT_RESULT)); // Parse Input // unsigned char ch; pfr->pMeat = str; if ( !mux_isdigit(*str) && *str != '.') { while (mux_isspace(*str)) { str++; } pfr->pMeat = str; if (*str == '-') { pfr->iLeadingSign = '-'; str++; } else if (*str == '+') { pfr->iLeadingSign = '+'; str++; } if ( !mux_isdigit(*str) && *str != '.') { // Look for three magic strings. // ch = mux_toupper(str[0]); if (ch == 'I') { // Could be 'Inf' or 'Ind' // ch = mux_toupper(str[1]); if (ch == 'N') { ch = mux_toupper(str[2]); if (ch == 'F') { // Inf // if (pfr->iLeadingSign == '-') { pfr->iString = IEEE_MAKE_NINF; } else { pfr->iString = IEEE_MAKE_PINF; } str += 3; goto LastSpaces; } else if (ch == 'D') { // Ind // pfr->iString = IEEE_MAKE_IND; str += 3; goto LastSpaces; } } } else if (ch == 'N') { // Could be 'Nan' // ch = mux_toupper(str[1]); if (ch == 'A') { ch = mux_toupper(str[2]); if (ch == 'N') { // Nan // pfr->iString = IEEE_MAKE_NAN; str += 3; goto LastSpaces; } } } return false; } } // At this point, we have processed the leading sign, handled all // the magic strings, skipped the leading spaces, and best of all // we either have a digit or a decimal point. // pfr->pDigitsA = str; while (mux_isdigit(*str)) { pfr->nDigitsA++; str++; } if (*str == '.') { str++; } pfr->pDigitsB = str; while (mux_isdigit(*str)) { pfr->nDigitsB++; str++; } if ( pfr->nDigitsA == 0 && pfr->nDigitsB == 0) { return false; } ch = mux_toupper(*str); if (ch == 'E') { // There is an exponent portion. // str++; if (*str == '-') { pfr->iExponentSign = '-'; str++; } else if (*str == '+') { pfr->iExponentSign = '+'; str++; } pfr->pDigitsC = str; while (mux_isdigit(*str)) { pfr->nDigitsC++; str++; } if ( pfr->nDigitsC < 1 || 4 < pfr->nDigitsC) { return false; } } LastSpaces: pfr->nMeat = str - pfr->pMeat; // Trailing spaces. // while (mux_isspace(*str)) { str++; } if (bStrict) { return (!*str); } else { return true; } } #define ATOF_LIMIT 100 static const double powerstab[10] = { 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0 }; double mux_atof(char *szString, bool bStrict) { PARSE_FLOAT_RESULT pfr; if (!ParseFloat(&pfr, szString, bStrict)) { return 0.0; } if (pfr.iString) { // Return the double value which corresponds to the // string when HAVE_IEEE_FORMAT. // #ifdef HAVE_IEEE_FP_FORMAT return MakeSpecialFloat(pfr.iString); #else // HAVE_IEEE_FP_FORMAT return 0.0; #endif // HAVE_IEEE_FP_FORMAT } // See if we can shortcut the decoding process. // double ret; if ( pfr.nDigitsA <= 9 && pfr.nDigitsC == 0) { if (pfr.nDigitsB <= 9) { if (pfr.nDigitsB == 0) { // This 'floating-point' number is just an integer. // ret = (double)mux_atol(pfr.pDigitsA); } else { // This 'floating-point' number is fixed-point. // double rA = (double)mux_atol(pfr.pDigitsA); double rB = (double)mux_atol(pfr.pDigitsB); double rScale = powerstab[pfr.nDigitsB]; ret = rA + rB/rScale; // As it is, ret is within a single bit of what a // a call to atof would return. However, we can // achieve that last lowest bit of precision by // computing a residual. // double residual = (ret - rA)*rScale; ret += (rB - residual)/rScale; } if (pfr.iLeadingSign == '-') { ret = -ret; } return ret; } } const char *p = pfr.pMeat; size_t n = pfr.nMeat; // We need to protect certain libraries from going nuts from being // force fed lots of ASCII. // char *pTmp = NULL; if (n > ATOF_LIMIT) { pTmp = alloc_lbuf("mux_atof"); memcpy(pTmp, p, ATOF_LIMIT); pTmp[ATOF_LIMIT] = '\0'; p = pTmp; } ret = mux_strtod(p, NULL); if (pTmp) { free_lbuf(pTmp); } return ret; } extern char *mux_dtoa(double d, int mode, int nRequest, int *iDecimalPoint, int *sign, char **rve); char *mux_ftoa(double r, bool bRounded, int frac) { static char buffer[100]; char *q = buffer; char *rve = NULL; int iDecimalPoint = 0; int bNegative = 0; int mode = 0; int nRequest = 50; if (bRounded) { mode = 3; nRequest = frac; if (50 < nRequest) { nRequest = 50; } else if (nRequest < -20) { nRequest = -20; } } char *p = mux_dtoa(r, mode, nRequest, &iDecimalPoint, &bNegative, &rve); int nSize = rve - p; if (nSize > 50) { nSize = 50; } if (bNegative) { *q++ = '-'; } if (iDecimalPoint == 9999) { // Inf or NaN // memcpy(q, p, nSize); q += nSize; } else if (nSize <= 0) { // Zero // *q++ = '0'; if ( bRounded && 0 < nRequest) { *q++ = '.'; memset(q, '0', nRequest); q += nRequest; } } else if ( iDecimalPoint <= -6 || 18 <= iDecimalPoint) { *q++ = *p++; if (1 < nSize) { *q++ = '.'; memcpy(q, p, nSize-1); q += nSize-1; } *q++ = 'E'; q += mux_ltoa(iDecimalPoint-1, q); } else if (iDecimalPoint <= 0) { // iDecimalPoint = -5 to 0 // *q++ = '0'; *q++ = '.'; memset(q, '0', -iDecimalPoint); q += -iDecimalPoint; memcpy(q, p, nSize); q += nSize; if (bRounded) { int nPad = nRequest - (nSize - iDecimalPoint); if (0 < nPad) { memset(q, '0', nPad); q += nPad; } } } else { // iDecimalPoint = 1 to 17 // if (nSize <= iDecimalPoint) { memcpy(q, p, nSize); q += nSize; memset(q, '0', iDecimalPoint - nSize); q += iDecimalPoint - nSize; if ( bRounded && 0 < nRequest) { *q++ = '.'; memset(q, '0', nRequest); q += nRequest; } } else { memcpy(q, p, iDecimalPoint); q += iDecimalPoint; p += iDecimalPoint; *q++ = '.'; memcpy(q, p, nSize - iDecimalPoint); q += nSize - iDecimalPoint; if (bRounded) { int nPad = nRequest - (nSize - iDecimalPoint); if (0 < nPad) { memset(q, '0', nPad); q += nPad; } } } } *q = '\0'; return buffer; } bool is_integer(char *str, int *pDigits) { int nDigits = 0; if (pDigits) { *pDigits = 0; } // Leading spaces. // while (mux_isspace(*str)) { str++; } // Leading minus or plus // if (*str == '-' || *str == '+') { str++; // Just a sign by itself isn't an integer. // if (!*str) { return false; } } // Need at least 1 integer // if (!mux_isdigit(*str)) { return false; } // The number (int) // do { str++; nDigits++; } while (mux_isdigit(*str)); if (pDigits) { *pDigits = nDigits; } // Trailing Spaces. // while (mux_isspace(*str)) { str++; } return (!*str); } bool is_rational(char *str) { // Leading spaces. // while (mux_isspace(*str)) { str++; } // Leading minus or plus sign. // if (*str == '-' || *str == '+') { str++; // But not if just a sign. // if (!*str) { return false; } } // Need at least one digit. // bool got_one = false; if (mux_isdigit(*str)) { got_one = true; } // The number (int) // while (mux_isdigit(*str)) { str++; } // Decimal point. // if (*str == '.') { str++; } // Need at least one digit // if (mux_isdigit(*str)) { got_one = true; } if (!got_one) { return false; } // The number (fract) // while (mux_isdigit(*str)) { str++; } // Trailing spaces. // while (mux_isspace(*str)) { str++; } // There must be nothing else after the trailing spaces. // return (!*str); } bool is_real(char *str) { PARSE_FLOAT_RESULT pfr; return ParseFloat(&pfr, str); } // mux_strtok_src, mux_strtok_ctl, mux_strtok_parse. // // These three functions work together to replace the functionality of the // strtok() C runtime library function. Call mux_strtok_src() first with // the string to parse, then mux_strtok_ctl() with the control // characters, and finally mux_strtok_parse() to parse out the tokens. // // You may call mux_strtok_ctl() to change the set of control characters // between mux_strtok_parse() calls, however keep in mind that the parsing // may not occur how you intend it to as mux_strtok_parse() does not // consume -all- of the controlling delimiters that separate two tokens. // It consumes only the first one. // void mux_strtok_src(MUX_STRTOK_STATE *tts, char *arg_pString) { if (!tts || !arg_pString) return; // Remember the string to parse. // tts->pString = arg_pString; } void mux_strtok_ctl(MUX_STRTOK_STATE *tts, char *pControl) { if (!tts || !pControl) return; // No character is a control character. // memset(tts->aControl, 0, sizeof(tts->aControl)); // The NULL character is always a control character. // tts->aControl[0] = 1; // Record the user-specified control characters. // while (*pControl) { tts->aControl[(unsigned char)*pControl] = 1; pControl++; } } char *mux_strtok_parseLEN(MUX_STRTOK_STATE *tts, int *pnLen) { *pnLen = 0; if (!tts) { return NULL; } char *p = tts->pString; if (!p) { return NULL; } // Skip over leading control characters except for the NUL character. // while (tts->aControl[(unsigned char)*p] && *p) { p++; } char *pReturn = p; // Skip over non-control characters. // while (tts->aControl[(unsigned char)*p] == 0) { p++; } // What is the length of this token? // *pnLen = p - pReturn; // Terminate the token with a NUL. // if (p[0]) { // We found a non-NUL delimiter, so the next call will begin parsing // on the character after this one. // tts->pString = p+1; } else { // We hit the end of the string, so the end of the string is where // the next call will begin. // tts->pString = p; } // Did we find a token? // if (*pnLen > 0) { return pReturn; } else { return NULL; } } char *mux_strtok_parse(MUX_STRTOK_STATE *tts) { int nLen; char *p = mux_strtok_parseLEN(tts, &nLen); if (p) { p[nLen] = '\0'; } return p; } // This function will filter out any characters in the the set from // the string. // char *RemoveSetOfCharacters(char *pString, char *pSetToRemove) { static char Buffer[LBUF_SIZE]; char *pBuffer = Buffer; int nLen; int nLeft = sizeof(Buffer) - 1; char *p; MUX_STRTOK_STATE tts; mux_strtok_src(&tts, pString); mux_strtok_ctl(&tts, pSetToRemove); for ( p = mux_strtok_parseLEN(&tts, &nLen); p && nLeft; p = mux_strtok_parseLEN(&tts, &nLen)) { if (nLeft < nLen) { nLen = nLeft; } memcpy(pBuffer, p, nLen); pBuffer += nLen; nLeft -= nLen; } *pBuffer = '\0'; return Buffer; } void ItemToList_Init(ITL *p, char *arg_buff, char **arg_bufc, char arg_chPrefix, char arg_chSep) { p->bFirst = true; p->chPrefix = arg_chPrefix; p->chSep = arg_chSep; p->buff = arg_buff; p->bufc = arg_bufc; p->nBufferAvailable = LBUF_SIZE - (*arg_bufc - arg_buff) - 1; } bool ItemToList_AddInteger(ITL *pContext, int i) { char smbuf[SBUF_SIZE]; char *p = smbuf; if ( !pContext->bFirst && pContext->chSep) { *p++ = pContext->chSep; } if (pContext->chPrefix) { *p++ = pContext->chPrefix; } p += mux_ltoa(i, p); size_t nLen = p - smbuf; if (nLen > pContext->nBufferAvailable) { // Out of room. // return false; } if (pContext->bFirst) { pContext->bFirst = false; } memcpy(*(pContext->bufc), smbuf, nLen); *(pContext->bufc) += nLen; pContext->nBufferAvailable -= nLen; return true; } bool ItemToList_AddStringLEN(ITL *pContext, size_t nStr, char *pStr) { size_t nLen = nStr; if ( !pContext->bFirst && pContext->chSep) { nLen++; } if (pContext->chPrefix) { nLen++; } if (nLen > pContext->nBufferAvailable) { // Out of room. // return false; } char *p = *(pContext->bufc); if (pContext->bFirst) { pContext->bFirst = false; } else if (pContext->chSep) { *p++ = pContext->chSep; } if (pContext->chPrefix) { *p++ = pContext->chPrefix; } memcpy(p, pStr, nStr); *(pContext->bufc) += nLen; pContext->nBufferAvailable -= nLen; return true; } bool ItemToList_AddString(ITL *pContext, char *pStr) { size_t nStr = strlen(pStr); return ItemToList_AddStringLEN(pContext, nStr, pStr); } void ItemToList_Final(ITL *pContext) { **(pContext->bufc) = '\0'; } // mux_stricmp - Compare two strings ignoring case. // int mux_stricmp(const char *a, const char *b) { while ( *a && *b && mux_tolower(*a) == mux_tolower(*b)) { a++; b++; } int c1 = mux_tolower(*a); int c2 = mux_tolower(*b); if (c1 < c2) { return -1; } else if (c1 > c2) { return 1; } else { return 0; } } // mux_memicmp - Compare two buffers ignoring case. // int mux_memicmp(const void *p1_arg, const void *p2_arg, size_t n) { unsigned char *p1 = (unsigned char *)p1_arg; unsigned char *p2 = (unsigned char *)p2_arg; while ( n && mux_tolower(*p1) == mux_tolower(*p2)) { n--; p1++; p2++; } if (n) { int c1 = mux_tolower(*p1); int c2 = mux_tolower(*p2); if (c1 < c2) { return -1; } else if (c1 > c2) { return 1; } } return 0; } // mux_strlwr - Convert string to all lower case. // void mux_strlwr(char *a) { while (*a) { *a = mux_tolower(*a); a++; } } // mux_strupr - Convert string to all upper case. // void mux_strupr(char *a) { while (*a) { *a = mux_toupper(*a); a++; } } #ifdef WIN32 #define VSNPRINTF _vsnprintf #else // WIN32 #ifdef NEED_VSPRINTF_DCL extern char *vsprintf(char *, char *, va_list); #endif // NEED_VSPRINTF_DCL #define VSNPRINTF vsnprintf #endif // WIN32 // mux_vsnprintf - Is an sprintf-like function that will not overflow // a buffer of specific size. The size is give by count, and count // should be chosen to include the '\0' termination. // // Returns: A number from 0 to count-1 that is the string length of // the returned (possibly truncated) buffer. // int DCL_CDECL mux_vsnprintf(char *buff, int count, const char *fmt, va_list va) { // From the manuals: // // vsnprintf returns the number of characters written, not // including the terminating '\0' character. // // It returns a -1 if an output error occurs. // // It can return a number larger than the size of the buffer // on some systems to indicate how much space it -would- have taken // if not limited by the request. // // On Win32, it can fill the buffer completely without a // null-termination and return -1. // To favor the Unix case, if there is an output error, but // vsnprint doesn't touch the buffer, we avoid undefined trash by // null-terminating the buffer to zero-length before the call. // Not sure that this happens, but it's a cheap precaution. // buff[0] = '\0'; // If Unix version does start touching the buffer, null-terminates, // and returns -1, we are still safe. However, if Unix version // touches the buffer writes garbage, and then returns -1, we may // pass garbage, but this possibility seems very unlikely. // int len = VSNPRINTF(buff, count, fmt, va); if (len < 0 || len > count-1) { if (buff[0] == '\0') { // vsnprintf did not touch the buffer. // len = 0; } else { len = count-1; } } buff[len] = '\0'; return len; } // This function acts like fgets except that any data on the end of the // line past the buffer size is truncated instead of being returned on // the next call. // int GetLineTrunc(char *Buffer, size_t nBuffer, FILE *fp) { size_t lenBuffer = 0; if (fgets(Buffer, nBuffer, fp)) { lenBuffer = strlen(Buffer); } if (lenBuffer <= 0) { memcpy(Buffer, "\n", 2); return 1; } if (Buffer[lenBuffer-1] != '\n') { // The line was too long for the buffer. Continue reading until the // end of the line. // char TruncBuffer[SBUF_SIZE]; size_t lenTruncBuffer; do { if (!fgets(TruncBuffer, sizeof(TruncBuffer), fp)) { break; } lenTruncBuffer = strlen(TruncBuffer); } while (TruncBuffer[lenTruncBuffer-1] != '\n'); } return lenBuffer; } // Method: Boyer-Moore-Horspool // // This method is a simplification of the Boyer-Moore String Searching // Algorithm, but a useful one. It does not require as much temporary // storage, and the setup costs are not as high as the full Boyer-Moore. // // If we were searching megabytes of data instead of 8KB at most, then // the full Boyer-Moore would make more sense. // #define BMH_LARGE 32767 void BMH_Prepare(BMH_State *bmhs, int nPat, const char *pPat) { if (nPat <= 0) { return; } int k; for (k = 0; k < 256; k++) { bmhs->m_d[k] = nPat; } char chLastPat = pPat[nPat-1]; bmhs->m_skip2 = nPat; for (k = 0; k < nPat - 1; k++) { bmhs->m_d[(unsigned char)pPat[k]] = nPat - k - 1; if (pPat[k] == chLastPat) { bmhs->m_skip2 = nPat - k - 1; } } bmhs->m_d[(unsigned char)chLastPat] = BMH_LARGE; } int BMH_Execute(BMH_State *bmhs, int nPat, const char *pPat, int nSrc, const char *pSrc) { if (nPat <= 0) { return -1; } for (int i = nPat-1; i < nSrc; i += bmhs->m_skip2) { while ((i += bmhs->m_d[(unsigned char)(pSrc[i])]) < nSrc) { ; // Nothing. } if (i < BMH_LARGE) { break; } i -= BMH_LARGE; int j = nPat - 1; const char *s = pSrc + (i - j); while (--j >= 0 && s[j] == pPat[j]) { ; // Nothing. } if (j < 0) { return s-pSrc; } } return -1; } int BMH_StringSearch(int nPat, const char *pPat, int nSrc, const char *pSrc) { BMH_State bmhs; BMH_Prepare(&bmhs, nPat, pPat); return BMH_Execute(&bmhs, nPat, pPat, nSrc, pSrc); } void BMH_PrepareI(BMH_State *bmhs, int nPat, const char *pPat) { if (nPat <= 0) { return; } int k; for (k = 0; k < 256; k++) { bmhs->m_d[k] = nPat; } char chLastPat = pPat[nPat-1]; bmhs->m_skip2 = nPat; for (k = 0; k < nPat - 1; k++) { bmhs->m_d[mux_toupper(pPat[k])] = nPat - k - 1; bmhs->m_d[mux_tolower(pPat[k])] = nPat - k - 1; if (pPat[k] == chLastPat) { bmhs->m_skip2 = nPat - k - 1; } } bmhs->m_d[mux_toupper(chLastPat)] = BMH_LARGE; bmhs->m_d[mux_tolower(chLastPat)] = BMH_LARGE; } int BMH_ExecuteI(BMH_State *bmhs, int nPat, const char *pPat, int nSrc, const char *pSrc) { if (nPat <= 0) { return -1; } for (int i = nPat-1; i < nSrc; i += bmhs->m_skip2) { while ((i += bmhs->m_d[(unsigned char)(pSrc[i])]) < nSrc) { ; // Nothing. } if (i < BMH_LARGE) { break; } i -= BMH_LARGE; int j = nPat - 1; const char *s = pSrc + (i - j); while ( --j >= 0 && mux_toupper(s[j]) == mux_toupper(pPat[j])) { ; // Nothing. } if (j < 0) { return s-pSrc; } } return -1; } int BMH_StringSearchI(int nPat, const char *pPat, int nSrc, const char *pSrc) { BMH_State bmhs; BMH_PrepareI(&bmhs, nPat, pPat); return BMH_ExecuteI(&bmhs, nPat, pPat, nSrc, pSrc); } // --------------------------------------------------------------------------- // cf_art_except: // // Add an article rule to the ruleset. // extern void DCL_CDECL cf_log_syntax(dbref player, char *cmd, const char *fmt, ...); CF_HAND(cf_art_rule) { char* pCurrent = str; while (mux_isspace(*pCurrent)) { pCurrent++; } char* pArticle = pCurrent; while ( !mux_isspace(*pCurrent) && *pCurrent != '\0') { pCurrent++; } if (*pCurrent == '\0') { cf_log_syntax(player, cmd, "No article or regexp specified."); return -1; } bool bUseAn = false; bool bOkay = false; if (pCurrent - pArticle <= 2) { if (mux_tolower(pArticle[0]) == 'a') { if (mux_tolower(pArticle[1]) == 'n') { bUseAn = true; bOkay = true; } if (mux_isspace(pArticle[1])) { bOkay = true; } } } if (!bOkay) { *pCurrent = '\0'; cf_log_syntax(player, cmd, "Invalid article '%s'.", pArticle); return -1; } while (mux_isspace(*pCurrent)) { pCurrent++; } if (*pCurrent == '\0') { cf_log_syntax(player, cmd, "No regexp specified."); return -1; } const char *errptr; int erroffset; pcre* reNewRegexp = pcre_compile(pCurrent, 0, &errptr, &erroffset, NULL); if (!reNewRegexp) { cf_log_syntax(player, cmd, "Error processing regexp '%s':.", pCurrent, errptr); return -1; } pcre_extra *study = pcre_study(reNewRegexp, 0, &errptr); // Push new rule at head of list. ArtRuleset** arRules = (ArtRuleset **) vp; ArtRuleset* arNewRule = (ArtRuleset *) MEMALLOC(sizeof(ArtRuleset)); arNewRule->m_pNextRule = *arRules; arNewRule->m_bUseAn = bUseAn; arNewRule->m_pRegexp = reNewRegexp; arNewRule->m_pRegexpStudy = study; *arRules = arNewRule; return 0; }