├── wstring_fix ├── README.md ├── WString.h └── WString.cpp ├── keywords.txt ├── README.md ├── CommandHandler.h ├── examples └── Demo │ └── Demo.ino ├── LICENSE └── CommandHandler.cpp /wstring_fix/README.md: -------------------------------------------------------------------------------- 1 | Ubunto arduino package is old, wstring is not suitable for the library we developed. 2 | 3 | Copy/Paste/Replace the wstring files from here to: /usr/share/arduino/hardware/arduino/cores/arduino, or the equivalent location on your system. 4 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Datatypes (KEYWORD1) 3 | ####################################### 4 | 5 | CommandHandler KEYWORD1 6 | 7 | ####################################### 8 | # Methods and Functions (KEYWORD2) 9 | ####################################### 10 | 11 | addCommand KEYWORD2 12 | addRelay KEYWORD2 13 | setDefaultHandler KEYWORD2 14 | processSerial KEYWORD2 15 | processString KEYWORD2 16 | processChar KEYWORD2 17 | clearBuffer KEYWORD2 18 | remaining KEYWORD2 19 | next KEYWORD2 20 | readInt16Arg KEYWORD2 21 | readInt32Arg KEYWORD2 22 | readBoolArg KEYWORD2 23 | readFloatArg KEYWORD2 24 | readDoubleArg KEYWORD2 25 | readStringArg KEYWORD2 26 | compareStringArg KEYWORD2 27 | 28 | ####################################### 29 | # Instances (KEYWORD2) 30 | ####################################### 31 | 32 | ####################################### 33 | # Constants (LITERAL1) 34 | ####################################### 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CommandHandler 2 | 3 | A Wiring/Arduino library to tokenize, parse and relay commands received either by the serial port, as a string, or char by char. 4 | 5 | The message format is: 6 | ``` 7 | CmdString,arg1,[...],argN; 8 | ``` 9 | 10 | Although the field separator ',' and command separator ';' can be changed. 11 | 12 | The library can: 13 | - Attach callback functions to received command 14 | - Relay the remaining of a command to attached callback functions (typically another CommandHandler) 15 | - Parse a command char by char 16 | - Parse a string command 17 | - Receive commands through the serial port 18 | - Read multiple arguments 19 | - Read all primary data types 20 | - Forging of string packet with multiple arguments of different primary type 21 | 22 | 23 | ### Features and main difference with [SerialCommand](https://github.com/kroimon/Arduino-SerialCommand) and [CmdMessenger](https://github.com/thijse/Arduino-CmdMessenger) 24 | 25 | Unlike [CmdMessenger](https://github.com/thijse/Arduino-CmdMessenger), the command identifier can be any string, which make your code more readable 26 | 27 | It differs with [SerialCommand](https://github.com/kroimon/Arduino-SerialCommand) by the ability to nest commandHandlers. This has been made possible by a simple change: adding the ability to read from string. While not being at all a technical challenge to implement, it can be really useful to modularize your code. 28 | 29 | Imagine your main program receiving "M1,P,2000;" command through Serial. The main program could redirect the "P,2000;" string command to the a sub-device called "M1". The M1 device can then use its own commandHandler to handle the sub command "P,2000;". To extract the remaining of a command, you can use the function remaining(), give that remaining to another commandHandler instance (e.g. the one associated to M1) through the processString() function. The command handler of M1 can then parse it and enjoy all the feature of the commandHandler, i.e. callback and casting of arguments. 30 | 31 | Please refer, and read through, the [Demo example](examples/Demo/Demo.ino) for practical usage of this library. 32 | 33 | All the above steps can be encapsulated by registering relay callback function. When triggered by the associated command, the command handler with call the relay command, passing in argument the remaining of the command. 34 | 35 | This behavior is illustrated in the [Arduino-CommandTools](https://github.com/croningp/Arduino-CommandTools) libraries, a set of modular librairies build on top of this message parsing library. 36 | 37 | ## Getting started 38 | 39 | Download or clone this repository, rename the folder as CommandHandler and move it to your Arduino libraries folder. You need to restart the Arduino IDE for the library to be loaded and recognized by Arduino. 40 | 41 | Please refer to https://www.arduino.cc/en/Guide/Libraries#toc5 for manual installation of libraries. 42 | 43 | ## Inspiration 44 | 45 | This is derived from the SerialCommand library whose original version was written by [Steven Cogswell](http://husks.wordpress.com) (published May 23, 2011 in his blog post ["A Minimal Arduino Library for Processing Serial Commands"](http://husks.wordpress.com/2011/05/23/a-minimal-arduino-library-for-processing-serial-commands/)). It is based on the [SerialCommand heavily modified version with smaller footprint and a cleaned up code by Stefan Rado](https://github.com/kroimon/Arduino-SerialCommand). 46 | 47 | Some features are also extracted from [CmdMessenger](https://github.com/thijse/Arduino-CmdMessenger), such as the function returning next arg already casted in types, e.g. readInt16Arg 48 | 49 | ## Author 50 | 51 | [Jonathan Grizou](http://jgrizou.com/) while working in the [Cronin Group](http://www.chem.gla.ac.uk/cronin/). 52 | 53 | ## License 54 | 55 | [![GPL V3](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl.html) 56 | -------------------------------------------------------------------------------- /CommandHandler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * CommandHandler - A Wiring/Arduino library to tokenize and parse commands 3 | * received in different forms, serial, string, or char. 4 | * 5 | * Copyright (C) 2015 Cronin Group http://www.chem.gla.ac.uk/cronin/ 6 | * Copyright (C) 2012 Stefan Rado 7 | * Copyright (C) 2011 Steven Cogswell 8 | * http://husks.wordpress.com 9 | * 10 | * Version 20151029 11 | * 12 | * This library is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU Lesser General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU Lesser General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this library. If not, see . 24 | */ 25 | 26 | #ifndef CommandHandler_h 27 | #define CommandHandler_h 28 | 29 | #if defined(WIRING) && WIRING >= 100 30 | #include 31 | #elif defined(ARDUINO) && ARDUINO >= 100 32 | #include 33 | #else 34 | #include 35 | #endif 36 | #include 37 | 38 | // Size of the input buffer in bytes (maximum length of one command plus arguments) 39 | #define COMMANDHANDLER_BUFFER 64 40 | // Maximum length of a command excluding the terminating null 41 | #define COMMANDHANDLER_MAXCOMMANDLENGTH 8 42 | // Default delimitor and terminator 43 | #define COMMANDHANDLER_DEFAULT_DELIM "," 44 | #define COMMANDHANDLER_DEFAULT_TERM ';' 45 | // The null term for string 46 | #define STRING_NULL_TERM '\0' 47 | 48 | // Uncomment the next line to run the library in debug mode (verbose messages) 49 | // #define COMMANDHANDLER_DEBUG 50 | 51 | 52 | class CommandHandler { 53 | public: 54 | CommandHandler(const char *newdelim = COMMANDHANDLER_DEFAULT_DELIM, const char newterm = COMMANDHANDLER_DEFAULT_TERM); // Constructor 55 | void addCommand(const char *command, void(*function)()); // Add a command to the processing dictionary. 56 | void addRelay(const char *command, void (*function)(const char *, void*), void* pt2Object = NULL); // Add a command to the relay dictionary. Such relay are given the remaining of the command. pt2Object is the reference to the instance associated with the callback, it will be given as the second argument of the callback function, default is NULL 57 | void setDefaultHandler(void (*function)(const char *)); // A handler to call when no valid command received. 58 | void setDefaultHandler(void (*function)(const char *, void*), void* pt2Object); // A handler to call when no valid command received. 59 | 60 | void setInCmdSerial(Stream &inStream); // define to which serial to send the read commands 61 | void processSerial(); // Process what on the in stream 62 | void processSerial(Stream &inStream); // Process what on the designated stream 63 | void processString(const char *inString); // Process a String 64 | void processChar(char inChar); //Process a char 65 | void clearBuffer(); // Clears the input buffer. 66 | char *remaining(); // Returns pointer to remaining of the command buffer (for getting arguments to commands). 67 | char *next(); // Returns pointer to next token found in command buffer (for getting arguments to commands). 68 | 69 | // helpers to cast next into different types 70 | bool argOk; // this variable is set after the below function are run, it tell you if thing went well 71 | bool readBoolArg(); 72 | int readIntArg(); 73 | long readLongArg(); 74 | float readFloatArg(); 75 | double readDoubleArg(); 76 | char *readStringArg(); 77 | bool compareStringArg(const char *stringToCompare); 78 | 79 | //helpers to create a message 80 | void setCmdHeader(const char *cmdHeader, bool addDelim = true); // setting a char to be added at the start of each out message (default "") 81 | void initCmd(); // initialize the command buffer to build next message to be sent 82 | 83 | void clearCmd(); // clear the output command 84 | void addCmdDelim(); 85 | void addCmdTerm(); 86 | 87 | void addCmdBool(bool value); 88 | void addCmdInt(int value); 89 | void addCmdLong(long value); 90 | 91 | void setCmdDecimal(byte decimal); 92 | void addCmdFloat(double value); 93 | void addCmdFloat(float value, byte decimal); 94 | void addCmdDouble(double value); 95 | void addCmdDouble(double value, byte decimal); 96 | 97 | void addCmdString(const char *value); 98 | 99 | char* getOutCmd(); // get pointer to command buffer 100 | 101 | void setOutCmdSerial(Stream &outStream); // define to which serial to send the out commands 102 | void sendCmdSerial(); //send current command thought the Stream 103 | void sendCmdSerial(Stream &outStream); //send current command thought the Stream 104 | 105 | private: 106 | 107 | // Command/handler dictionary 108 | struct CommandHandlerCallback { 109 | char command[COMMANDHANDLER_MAXCOMMANDLENGTH + 1]; 110 | void (*function)(); 111 | }; // Data structure to hold Command/Handler function key-value pairs 112 | CommandHandlerCallback *commandList; // Actual definition for command/handler array 113 | byte commandCount; 114 | 115 | // Relay/handler dictionary 116 | struct RelayHandlerCallback { 117 | char command[COMMANDHANDLER_MAXCOMMANDLENGTH + 1]; 118 | void* pt2Object; 119 | void (*function)(const char *, void*); 120 | }; // Data structure to hold Relay/Handler function key-value pairs 121 | RelayHandlerCallback *relayList; // Actual definition for Relay/handler array 122 | byte relayCount; 123 | 124 | // Pointer to the default handler function 125 | void (*defaultHandler)(const char *); 126 | void* pt2defaultHandlerObject; 127 | void (*wrapper_defaultHandler)(const char *, void*); 128 | 129 | const char *delim; // null-terminated list of character to be used as delimeters for tokenizing (default " ") 130 | char term; // Character that signals end of command (default '\n') 131 | 132 | char buffer[COMMANDHANDLER_BUFFER + 1]; // Buffer of stored characters while waiting for terminator character 133 | byte bufPos; // Current position in the buffer 134 | char *last; // State variable used by strtok_r during processing 135 | 136 | char remains[COMMANDHANDLER_BUFFER + 1]; // Buffer of stored characters to pass to a relay function 137 | 138 | char command[COMMANDHANDLER_BUFFER + 1]; 139 | String commandString; // Out Command 140 | String commandHeader; // header for out command 141 | byte commandDecimal; 142 | 143 | 144 | // in and out default strem 145 | Stream *inCmdStream; 146 | Stream *outCmdStream; 147 | }; 148 | 149 | #endif //CommandHandler_h 150 | -------------------------------------------------------------------------------- /examples/Demo/Demo.ino: -------------------------------------------------------------------------------- 1 | // Demo Code for CommandHandler Library 2 | // Jonathan Grizou 3 | // October 2015 4 | 5 | #include 6 | 7 | //Create a CommandHandler Instance 8 | CommandHandler cmdHdl; 9 | 10 | // If needed you can change the delimitor string and terminator char 11 | // default are delim="," and term=';' 12 | //CommandHandler cmdHdl(" ", '\n'); 13 | 14 | 15 | void setup() { 16 | 17 | Serial.begin(115200); // setting up the serial port to 9600 baud 18 | 19 | // Setup callbacks for SerialCommand commands 20 | cmdHdl.addCommand("HELLO", sayHello); // Echos the string argument back 21 | cmdHdl.addCommand("FWD", forwardRemaining);// Fwd the remaining of the command to the cmdHdl 22 | cmdHdl.addCommand("P", processCommand); // Converts two arguments, first to double and echos them back 23 | cmdHdl.addCommand("GUESS", guessMyName); // A game for guessing my name, used to test compareStringArg 24 | cmdHdl.addCommand("PING", pongMesssage); // A function that use the packet forging tool to send a random ping time 25 | cmdHdl.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?") 26 | 27 | // You can process string directly into the arduino 28 | cmdHdl.processString("HELLO,I am ready!;"); 29 | // The above command send give a string to the command parser 30 | // This string happens to be a, HELLO command 31 | // Thus the arduino will execute the sayHello function 32 | // The sayHello function can then access the argument of the command 33 | 34 | // MORE EXPLANATION ABOUT CALLBACK POSSIBILITIES IN THE loop() 35 | 36 | // You can also forge packet to send 37 | // This is useful to talk to another device having a command handler 38 | // For example talking to another arduino board 39 | // Or to you computer using the Python-CommandHandler module [add link once online] 40 | 41 | // First you can define a header for all you message, e.g. the name of the device 42 | cmdHdl.setCmdHeader("FEEDBACK"); // here we call it FEEDBACK, a delim will automatically be added after the header 43 | // use cmdHdl.setCmdHeader("FEEDBACK", false); if you do not want a delimiter 44 | 45 | // always start by initiating your message, it just set things up 46 | cmdHdl.initCmd(); 47 | // now create the message you like 48 | cmdHdl.addCmdString("ALIVE"); // add a string 49 | cmdHdl.addCmdDelim(); // add a delim 50 | cmdHdl.addCmdBool(true); // add a boolean 51 | cmdHdl.addCmdDelim(); // add a delim 52 | cmdHdl.addCmdLong(938); //add a int32 53 | cmdHdl.addCmdDelim(); // add a delim 54 | cmdHdl.addCmdDouble(-2147.483647, 3); // add a double, printed with 3 decimal 55 | // if unspecified decimal, default is 2) 56 | // you can change the default decimal to N using cmdHdl.setCmdDecimal(N) 57 | cmdHdl.addCmdTerm(); //finally end your message with the term char 58 | 59 | // once the message is ready, send it 60 | // either by getting it and sending it however you want 61 | Serial.println(cmdHdl.getOutCmd()); // that will print the message on Serial 62 | // or by using the Serial using the directly embeded Serial send 63 | cmdHdl.sendCmdSerial(); // also send current Cmd to Serial (by default) 64 | // you can also set the default Out Serial to use by: 65 | // cmdHdl.setOutCmdSerial(Serial); // default is Serial 66 | Serial.println(); // just adding this so the ouput on the serial terminal looks nice 67 | } 68 | 69 | void loop() { 70 | //Of course we can use the same functionnality by readng from the Serial 71 | cmdHdl.processSerial(Serial); 72 | // Try send "HELLO,yourname;" to your board via serial 73 | // you can also set the default In Serial to use by: 74 | // cmdHdl.setInCmdSerial(Serial); // default is Serial 75 | // cmdHdl.processSerial(); 76 | 77 | // The above processSerial function is simply doing the following 78 | //if (Serial.available() > 0){ 79 | // cmdHdl.processChar(Serial.read()); 80 | //} 81 | // Thus, depending on your needs, you can also use the command handler 82 | // by feeding it char by char using the processChar 83 | 84 | // Why having three different process function? 85 | // In some cases you might have to parse a string, for example when nesting command handlers 86 | // Imagine your main program receiving "M1,P,2000;" command throught Serial 87 | // The main program could redirect the "P,2000;" string command to the a device called "M1" 88 | // The M1 device can then use its own commandHandler to handle the sub command "P,2000;" 89 | // To extract the remaining of a command, you can use the function remaining() 90 | // This is illustrated in the forwardRemaining() function 91 | // that is associated to the FWD command in this example 92 | // Send "FWD,P,2000;" to the board, it should forward the "P,2000;" to itself 93 | // (thanks to the processString() function) 94 | // As "P" is assocviated with the processCommand() function, it will run it 95 | 96 | // Below are a bunch of serial command you can try sending 97 | // Try predicting what it will do 98 | // P,3.5; 99 | // FWD,P,3.5; 100 | // FWD,FWD,FWD,P,3.5; 101 | // PING; 102 | // GUESS,Maurice; 103 | } 104 | 105 | void sayHello() { 106 | char *arg; 107 | arg = cmdHdl.next(); // Get the next argument from the SerialCommand object buffer 108 | if (arg != NULL) { // As long as it existed, take it 109 | Serial.print("Hello "); 110 | Serial.println(arg); 111 | } 112 | else { 113 | Serial.println("Hello, whoever you are"); 114 | } 115 | } 116 | 117 | void forwardRemaining() { 118 | char *remaining; 119 | remaining = cmdHdl.remaining(); // Get the next argument from the SerialCommand object buffer 120 | if (remaining != NULL) { // As long as it existed, take it 121 | Serial.print("Forwarding "); 122 | Serial.println(remaining); 123 | cmdHdl.processString(remaining); 124 | } else { 125 | Serial.print("Nothing to forward"); 126 | } 127 | } 128 | 129 | void processCommand() { 130 | Serial.println("We're in processCommand"); 131 | 132 | double aNumber; 133 | 134 | aNumber = cmdHdl.readDoubleArg(); 135 | if (cmdHdl.argOk) { 136 | Serial.print("First argument was: "); 137 | Serial.print(aNumber, 10); 138 | Serial.println(" (converted to double, printed with 10 decimals)"); 139 | } 140 | else { 141 | Serial.println("No arguments"); 142 | } 143 | 144 | char *aString; 145 | aString = cmdHdl.readStringArg(); 146 | if (cmdHdl.argOk) { 147 | Serial.print("Second argument was: "); 148 | Serial.println(aString); 149 | } 150 | else { 151 | Serial.println("No second argument"); 152 | } 153 | } 154 | 155 | void guessMyName() { 156 | if (cmdHdl.compareStringArg("Jonathan")) { 157 | Serial.println("Yes! My name is Jonathan"); 158 | } 159 | else { 160 | Serial.println("Nop, that's not my name, try again.."); 161 | } 162 | } 163 | 164 | void pongMesssage() { 165 | 166 | Serial.println("Received PING, pausing for a random time..."); // for the demo only! 167 | 168 | unsigned long start = millis(); 169 | delay(random(1000)); 170 | unsigned long elasped = millis() - start; 171 | 172 | cmdHdl.initCmd(); 173 | cmdHdl.addCmdString("PONG"); 174 | cmdHdl.addCmdDelim(); 175 | cmdHdl.addCmdLong(elasped); 176 | cmdHdl.addCmdTerm(); 177 | cmdHdl.sendCmdSerial(); 178 | 179 | Serial.println(); // for the demo only! so the output look nice 180 | Serial.println("Above is the feedback command indicating the pause time."); 181 | } 182 | 183 | // This gets set as the default handler, and gets called when no other command matches. 184 | void unrecognized(const char *command) { 185 | Serial.println("What?"); 186 | } 187 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /wstring_fix/WString.h: -------------------------------------------------------------------------------- 1 | /* 2 | WString.h - String library for Wiring & Arduino 3 | ...mostly rewritten by Paul Stoffregen... 4 | Copyright (c) 2009-10 Hernando Barragan. All right reserved. 5 | Copyright 2011, Paul Stoffregen, paul@pjrc.com 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef String_class_h 23 | #define String_class_h 24 | #ifdef __cplusplus 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // When compiling programs with this class, the following gcc parameters 32 | // dramatically increase performance and memory (RAM) efficiency, typically 33 | // with little or no increase in code size. 34 | // -felide-constructors 35 | // -std=c++0x 36 | 37 | class __FlashStringHelper; 38 | #define F(string_literal) (reinterpret_cast(PSTR(string_literal))) 39 | 40 | // An inherited class for holding the result of a concatenation. These 41 | // result objects are assumed to be writable by subsequent concatenations. 42 | class StringSumHelper; 43 | 44 | // The string class 45 | class String 46 | { 47 | // use a function pointer to allow for "if (s)" without the 48 | // complications of an operator bool(). for more information, see: 49 | // http://www.artima.com/cppsource/safebool.html 50 | typedef void (String::*StringIfHelperType)() const; 51 | void StringIfHelper() const {} 52 | 53 | public: 54 | // constructors 55 | // creates a copy of the initial value. 56 | // if the initial value is null or invalid, or if memory allocation 57 | // fails, the string will be marked as invalid (i.e. "if (s)" will 58 | // be false). 59 | String(const char *cstr = ""); 60 | String(const String &str); 61 | String(const __FlashStringHelper *str); 62 | #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) 63 | String(String &&rval); 64 | String(StringSumHelper &&rval); 65 | #endif 66 | explicit String(char c); 67 | explicit String(unsigned char, unsigned char base=10); 68 | explicit String(int, unsigned char base=10); 69 | explicit String(unsigned int, unsigned char base=10); 70 | explicit String(long, unsigned char base=10); 71 | explicit String(unsigned long, unsigned char base=10); 72 | explicit String(float, unsigned char decimalPlaces=2); 73 | explicit String(double, unsigned char decimalPlaces=2); 74 | ~String(void); 75 | 76 | // memory management 77 | // return true on success, false on failure (in which case, the string 78 | // is left unchanged). reserve(0), if successful, will validate an 79 | // invalid string (i.e., "if (s)" will be true afterwards) 80 | unsigned char reserve(unsigned int size); 81 | inline unsigned int length(void) const {return len;} 82 | 83 | // creates a copy of the assigned value. if the value is null or 84 | // invalid, or if the memory allocation fails, the string will be 85 | // marked as invalid ("if (s)" will be false). 86 | String & operator = (const String &rhs); 87 | String & operator = (const char *cstr); 88 | String & operator = (const __FlashStringHelper *str); 89 | #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) 90 | String & operator = (String &&rval); 91 | String & operator = (StringSumHelper &&rval); 92 | #endif 93 | 94 | // concatenate (works w/ built-in types) 95 | 96 | // returns true on success, false on failure (in which case, the string 97 | // is left unchanged). if the argument is null or invalid, the 98 | // concatenation is considered unsucessful. 99 | unsigned char concat(const String &str); 100 | unsigned char concat(const char *cstr); 101 | unsigned char concat(char c); 102 | unsigned char concat(unsigned char c); 103 | unsigned char concat(int num); 104 | unsigned char concat(unsigned int num); 105 | unsigned char concat(long num); 106 | unsigned char concat(unsigned long num); 107 | unsigned char concat(float num); 108 | unsigned char concat(double num); 109 | unsigned char concat(const __FlashStringHelper * str); 110 | 111 | // if there's not enough memory for the concatenated value, the string 112 | // will be left unchanged (but this isn't signalled in any way) 113 | String & operator += (const String &rhs) {concat(rhs); return (*this);} 114 | String & operator += (const char *cstr) {concat(cstr); return (*this);} 115 | String & operator += (char c) {concat(c); return (*this);} 116 | String & operator += (unsigned char num) {concat(num); return (*this);} 117 | String & operator += (int num) {concat(num); return (*this);} 118 | String & operator += (unsigned int num) {concat(num); return (*this);} 119 | String & operator += (long num) {concat(num); return (*this);} 120 | String & operator += (unsigned long num) {concat(num); return (*this);} 121 | String & operator += (float num) {concat(num); return (*this);} 122 | String & operator += (double num) {concat(num); return (*this);} 123 | String & operator += (const __FlashStringHelper *str){concat(str); return (*this);} 124 | 125 | friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs); 126 | friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr); 127 | friend StringSumHelper & operator + (const StringSumHelper &lhs, char c); 128 | friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num); 129 | friend StringSumHelper & operator + (const StringSumHelper &lhs, int num); 130 | friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num); 131 | friend StringSumHelper & operator + (const StringSumHelper &lhs, long num); 132 | friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num); 133 | friend StringSumHelper & operator + (const StringSumHelper &lhs, float num); 134 | friend StringSumHelper & operator + (const StringSumHelper &lhs, double num); 135 | friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs); 136 | 137 | // comparison (only works w/ Strings and "strings") 138 | operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; } 139 | int compareTo(const String &s) const; 140 | unsigned char equals(const String &s) const; 141 | unsigned char equals(const char *cstr) const; 142 | unsigned char operator == (const String &rhs) const {return equals(rhs);} 143 | unsigned char operator == (const char *cstr) const {return equals(cstr);} 144 | unsigned char operator != (const String &rhs) const {return !equals(rhs);} 145 | unsigned char operator != (const char *cstr) const {return !equals(cstr);} 146 | unsigned char operator < (const String &rhs) const; 147 | unsigned char operator > (const String &rhs) const; 148 | unsigned char operator <= (const String &rhs) const; 149 | unsigned char operator >= (const String &rhs) const; 150 | unsigned char equalsIgnoreCase(const String &s) const; 151 | unsigned char startsWith( const String &prefix) const; 152 | unsigned char startsWith(const String &prefix, unsigned int offset) const; 153 | unsigned char endsWith(const String &suffix) const; 154 | 155 | // character acccess 156 | char charAt(unsigned int index) const; 157 | void setCharAt(unsigned int index, char c); 158 | char operator [] (unsigned int index) const; 159 | char& operator [] (unsigned int index); 160 | void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const; 161 | void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const 162 | {getBytes((unsigned char *)buf, bufsize, index);} 163 | const char * c_str() const { return buffer; } 164 | 165 | // search 166 | int indexOf( char ch ) const; 167 | int indexOf( char ch, unsigned int fromIndex ) const; 168 | int indexOf( const String &str ) const; 169 | int indexOf( const String &str, unsigned int fromIndex ) const; 170 | int lastIndexOf( char ch ) const; 171 | int lastIndexOf( char ch, unsigned int fromIndex ) const; 172 | int lastIndexOf( const String &str ) const; 173 | int lastIndexOf( const String &str, unsigned int fromIndex ) const; 174 | String substring( unsigned int beginIndex ) const { return substring(beginIndex, len); }; 175 | String substring( unsigned int beginIndex, unsigned int endIndex ) const; 176 | 177 | // modification 178 | void replace(char find, char replace); 179 | void replace(const String& find, const String& replace); 180 | void remove(unsigned int index); 181 | void remove(unsigned int index, unsigned int count); 182 | void toLowerCase(void); 183 | void toUpperCase(void); 184 | void trim(void); 185 | 186 | // parsing/conversion 187 | long toInt(void) const; 188 | float toFloat(void) const; 189 | 190 | protected: 191 | char *buffer; // the actual char array 192 | unsigned int capacity; // the array length minus one (for the '\0') 193 | unsigned int len; // the String length (not counting the '\0') 194 | protected: 195 | void init(void); 196 | void invalidate(void); 197 | unsigned char changeBuffer(unsigned int maxStrLen); 198 | unsigned char concat(const char *cstr, unsigned int length); 199 | 200 | // copy and move 201 | String & copy(const char *cstr, unsigned int length); 202 | String & copy(const __FlashStringHelper *pstr, unsigned int length); 203 | #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) 204 | void move(String &rhs); 205 | #endif 206 | }; 207 | 208 | class StringSumHelper : public String 209 | { 210 | public: 211 | StringSumHelper(const String &s) : String(s) {} 212 | StringSumHelper(const char *p) : String(p) {} 213 | StringSumHelper(char c) : String(c) {} 214 | StringSumHelper(unsigned char num) : String(num) {} 215 | StringSumHelper(int num) : String(num) {} 216 | StringSumHelper(unsigned int num) : String(num) {} 217 | StringSumHelper(long num) : String(num) {} 218 | StringSumHelper(unsigned long num) : String(num) {} 219 | StringSumHelper(float num) : String(num) {} 220 | StringSumHelper(double num) : String(num) {} 221 | }; 222 | 223 | #endif // __cplusplus 224 | #endif // String_class_h 225 | -------------------------------------------------------------------------------- /CommandHandler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * CommandHandler - A Wiring/Arduino library to tokenize and parse commands 3 | * received in different forms, serial, string, or char. 4 | * 5 | * Copyright (C) 2015 Cronin Group http://www.chem.gla.ac.uk/cronin/ 6 | * Copyright (C) 2012 Stefan Rado 7 | * Copyright (C) 2011 Steven Cogswell 8 | * http://husks.wordpress.com 9 | * 10 | * Version 20151029 11 | * 12 | * This library is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU Lesser General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU Lesser General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this library. If not, see . 24 | */ 25 | 26 | #include "CommandHandler.h" 27 | 28 | /** 29 | * Constructor allowing to change default delim and term 30 | * Example: SerialCommand sCmd(" ", ';'); 31 | * Default are COMMANDHANDLER_DEFAULT_DELIM and COMMANDHANDLER_DEFAULT_TERM 32 | */ 33 | CommandHandler::CommandHandler(const char *newdelim, char newterm) 34 | : commandList(NULL), 35 | commandCount(0), 36 | relayList(NULL), 37 | relayCount(0), 38 | defaultHandler(NULL), 39 | pt2defaultHandlerObject(NULL), 40 | wrapper_defaultHandler(NULL), 41 | term(newterm), // asssign new terminator for commands 42 | last(NULL), 43 | delim(newdelim) // assign new delimitor 44 | { 45 | inCmdStream = &Serial; 46 | outCmdStream = &Serial; 47 | 48 | commandHeader = String(""); 49 | commandDecimal = 2; 50 | 51 | clearBuffer(); 52 | } 53 | 54 | /** 55 | * Adds a "command" and a handler function to the list of available commands. 56 | * This is used for matching a found token in the buffer, and gives the pointer 57 | * to the handler function to deal with it. 58 | */ 59 | void CommandHandler::addCommand(const char *command, void (*function)()) { 60 | #ifdef COMMANDHANDLER_DEBUG 61 | Serial.print("Adding command ("); 62 | Serial.print(commandCount); 63 | Serial.print("): "); 64 | Serial.println(command); 65 | #endif 66 | 67 | commandList = (CommandHandlerCallback *) realloc(commandList, (commandCount + 1) * sizeof(CommandHandlerCallback)); 68 | strncpy(commandList[commandCount].command, command, COMMANDHANDLER_MAXCOMMANDLENGTH); 69 | commandList[commandCount].function = function; 70 | commandCount++; 71 | } 72 | 73 | /** 74 | * Adds a "command" and a handler function to the list of available relay. 75 | * This is used for matching a found token in the buffer, and gives the pointer 76 | * to the handler function to deal with the remaining of the command 77 | */ 78 | void CommandHandler::addRelay(const char *command, void (*function)(const char *, void*), void* pt2Object) { 79 | #ifdef COMMANDHANDLER_DEBUG 80 | Serial.print("Adding relay ("); 81 | Serial.print(relayCount); 82 | Serial.print("): "); 83 | Serial.println(command); 84 | #endif 85 | 86 | relayList = (RelayHandlerCallback *) realloc(relayList, (relayCount + 1) * sizeof(RelayHandlerCallback)); 87 | strncpy(relayList[relayCount].command, command, COMMANDHANDLER_MAXCOMMANDLENGTH); 88 | relayList[relayCount].pt2Object = pt2Object; 89 | relayList[relayCount].function = function; 90 | relayCount++; 91 | } 92 | 93 | /** 94 | * This sets up a handler to be called in the event that the receveived command string 95 | * isn't in the list of commands. 96 | */ 97 | void CommandHandler::setDefaultHandler(void (*function)(const char *)) { 98 | defaultHandler = function; 99 | } 100 | 101 | void CommandHandler::setDefaultHandler(void (*function)(const char *, void*), void* pt2Object) { 102 | pt2defaultHandlerObject = pt2Object; 103 | wrapper_defaultHandler = function; 104 | } 105 | 106 | /** 107 | * Assign the default serial 108 | */ 109 | void CommandHandler::setInCmdSerial(Stream &inStream) { 110 | inCmdStream = &inStream; 111 | } 112 | 113 | /** 114 | * Check the default Serial 115 | */ 116 | void CommandHandler::processSerial() { 117 | processSerial(*inCmdStream); 118 | } 119 | 120 | /** 121 | * This checks the Serial stream for characters, and assembles them into a buffer. 122 | * When the terminator character (default COMMANDHANDLER_DEFAULT_TERM) is seen, it starts parsing the 123 | * buffer for a prefix command, and calls handlers setup by addCommand() member 124 | */ 125 | void CommandHandler::processSerial(Stream &inStream) { 126 | while (inStream.available() > 0) { 127 | char inChar = inStream.read(); // Read single available character, there may be more waiting 128 | #ifdef COMMANDHANDLER_DEBUG 129 | Serial.print("Serial: "); 130 | Serial.println(inChar); // Echo back to serial stream 131 | #endif 132 | processChar(inChar); 133 | } 134 | } 135 | 136 | /** 137 | * This iterate on a String char by char, and push them into a buffer. 138 | * When the terminator character (default COMMANDHANDLER_DEFAULT_TERM) is seen, it starts parsing the 139 | * buffer for a prefix command, and calls handlers setup by addCommand() member 140 | */ 141 | void CommandHandler::processString(const char *inString) { 142 | for (int i = 0; i < strlen(inString); i++){ 143 | char inChar = inString[i]; 144 | #ifdef COMMANDHANDLER_DEBUG 145 | Serial.print("String: "); 146 | Serial.println(inChar); // Echo back to serial stream 147 | #endif 148 | processChar(inChar); 149 | } 150 | } 151 | 152 | /** 153 | * This add a characters to the buffer, and analyse the buffer. 154 | * When the terminator character (default COMMANDHANDLER_DEFAULT_TERM) is seen, it starts parsing the 155 | * buffer for a prefix command, and calls handlers setup by addCommand() member 156 | */ 157 | void CommandHandler::processChar(char inChar) { 158 | if (inChar == term) { // Check for the terminator (default '\r') meaning end of command 159 | #ifdef COMMANDHANDLER_DEBUG 160 | Serial.print("Received: "); 161 | Serial.println(buffer); 162 | #endif 163 | 164 | char *command = strtok_r(buffer, delim, &last); // Search for command at start of buffer 165 | if (command != NULL) { 166 | boolean matched = false; 167 | // searching in commands 168 | for (int i = 0; i < commandCount; i++) { 169 | #ifdef COMMANDHANDLER_DEBUG 170 | Serial.print("Comparing ["); 171 | Serial.print(command); 172 | Serial.print("] to ["); 173 | Serial.print(commandList[i].command); 174 | Serial.println("]"); 175 | #endif 176 | 177 | // Compare the found command against the list of known commands for a match 178 | if (strncmp(command, commandList[i].command, COMMANDHANDLER_MAXCOMMANDLENGTH) == 0) { 179 | #ifdef COMMANDHANDLER_DEBUG 180 | Serial.print("Matched Command: "); 181 | Serial.println(command); 182 | #endif 183 | 184 | // Execute the stored handler function for the command 185 | (*commandList[i].function)(); 186 | matched = true; 187 | break; 188 | } 189 | } 190 | // searching in relays 191 | for (int i = 0; i < relayCount; i++) { 192 | #ifdef COMMANDHANDLER_DEBUG 193 | Serial.print("Comparing ["); 194 | Serial.print(command); 195 | Serial.print("] to ["); 196 | Serial.print(relayList[i].command); 197 | Serial.println("]"); 198 | #endif 199 | 200 | // Compare the found command against the relay list of known commands for a match 201 | if (strncmp(command, relayList[i].command, COMMANDHANDLER_MAXCOMMANDLENGTH) == 0) { 202 | #ifdef COMMANDHANDLER_DEBUG 203 | Serial.print("Matched Relay: "); 204 | Serial.println(command); 205 | #endif 206 | 207 | // Execute the stored handler function for the command 208 | (*relayList[i].function)(remaining(), relayList[i].pt2Object); 209 | matched = true; 210 | break; 211 | } 212 | } 213 | if (!matched){ 214 | if (defaultHandler != NULL) { 215 | (*defaultHandler)(command); 216 | } else if (pt2defaultHandlerObject != NULL) { 217 | (*wrapper_defaultHandler)(command, pt2defaultHandlerObject); 218 | } 219 | } 220 | } 221 | clearBuffer(); 222 | } 223 | else if (isprint(inChar)) { // Only printable characters into the buffer 224 | if (bufPos < COMMANDHANDLER_BUFFER) { 225 | buffer[bufPos] = inChar; // Put character into buffer 226 | buffer[bufPos+1] = STRING_NULL_TERM; // Null terminate 227 | bufPos++; 228 | } else { 229 | #ifdef COMMANDHANDLER_DEBUG 230 | Serial.println("Line buffer is full - increase COMMANDHANDLER_BUFFER"); 231 | #endif 232 | } 233 | } 234 | } 235 | 236 | /* 237 | * Clear the input buffer. 238 | */ 239 | void CommandHandler::clearBuffer() { 240 | buffer[0] = STRING_NULL_TERM; 241 | bufPos = 0; 242 | } 243 | 244 | /** 245 | * Retrieve the next token ("word" or "argument") from the command buffer. 246 | * Returns NULL if no more tokens exist. 247 | */ 248 | char *CommandHandler::next() { 249 | return strtok_r(NULL, delim, &last); 250 | } 251 | 252 | /** 253 | * Returns char* of the remaining of the command buffer (for getting arguments to commands). 254 | * Returns NULL if no more tokens exist. 255 | */ 256 | char *CommandHandler::remaining() { 257 | 258 | //reinit the remains char 259 | remains[0] = STRING_NULL_TERM; 260 | 261 | char str_term[2]; 262 | str_term[0] = term; 263 | str_term[1] = STRING_NULL_TERM; 264 | 265 | // Search for the remaining up to next term 266 | char *command = strtok_r(NULL, str_term, &last); 267 | 268 | // forge term in string format 269 | strcpy(remains, command); 270 | strcat(remains, str_term); 271 | 272 | // clear the buffer now, we emptied the current command 273 | // the remaining is might be given to another handler 274 | // or it might used by the same commandHandler instance 275 | // hence the buffer should be emptied now 276 | clearBuffer(); 277 | 278 | return remains; 279 | } 280 | 281 | 282 | 283 | /***************************************** 284 | * Helpers to read args and cast them into specific type, strongly inspired by CmdMessenger 285 | *****************************************/ 286 | 287 | /** 288 | * Read the next argument as int16 289 | */ 290 | int CommandHandler::readIntArg() { 291 | char *arg; 292 | arg = next(); 293 | if (arg != NULL) { 294 | argOk = true; 295 | return atoi(arg); 296 | } 297 | argOk = false; 298 | return 0; 299 | } 300 | 301 | /** 302 | * Read the next argument as int32 303 | */ 304 | long CommandHandler::readLongArg() { 305 | char *arg; 306 | arg = next(); 307 | if (arg != NULL) { 308 | argOk = true; 309 | return atol(arg); 310 | } 311 | argOk = false; 312 | return 0L; // 'L' to force the constant into a long data format 313 | } 314 | 315 | /** 316 | * Read the next argument as bool 317 | */ 318 | bool CommandHandler::readBoolArg() { 319 | return (readIntArg() != 0) ? true : false; 320 | } 321 | 322 | /** 323 | * Read the next argument as float 324 | */ 325 | float CommandHandler::readFloatArg() { 326 | char *arg; 327 | arg = next(); 328 | if (arg != NULL) { 329 | argOk = true; 330 | return strtod(arg, NULL); 331 | } 332 | argOk = false; 333 | return 0; 334 | } 335 | 336 | /** 337 | * Read the next argument as double 338 | */ 339 | double CommandHandler::readDoubleArg() { 340 | char *arg; 341 | arg = next(); 342 | if (arg != NULL) { 343 | argOk = true; 344 | return strtod(arg, NULL); 345 | } 346 | argOk = false; 347 | return 0; 348 | } 349 | 350 | /** 351 | * Read next argument as string. 352 | */ 353 | char* CommandHandler::readStringArg() { 354 | char *arg; 355 | arg = next(); 356 | if (arg != NULL) { 357 | argOk = true; 358 | return arg; 359 | } 360 | argOk = false; 361 | return STRING_NULL_TERM; 362 | } 363 | 364 | /** 365 | * Compare the next argument with a string 366 | */ 367 | bool CommandHandler::compareStringArg(const char *stringToCompare) { 368 | char *arg; 369 | arg = next(); 370 | if (arg != NULL) { 371 | return (strcmp(stringToCompare, arg) == 0) ? true : false; 372 | } 373 | return false; 374 | } 375 | 376 | /***************************************** 377 | * Forging and sending output commands 378 | *****************************************/ 379 | 380 | 381 | /** 382 | * Set an header for the output command 383 | */ 384 | void CommandHandler::setCmdHeader(const char *cmdHeader, bool addDelim) { 385 | 386 | commandHeader = String(cmdHeader); 387 | 388 | if (addDelim == true) { 389 | commandHeader = commandHeader + String(delim); 390 | } 391 | 392 | #ifdef COMMANDHANDLER_DEBUG 393 | Serial.print("Out Command Header is now "); 394 | Serial.println(commandHeader); 395 | #endif 396 | } 397 | 398 | void CommandHandler::initCmd() { 399 | 400 | commandString = commandHeader; 401 | 402 | #ifdef COMMANDHANDLER_DEBUG 403 | Serial.print("Out command is now "); 404 | Serial.println(commandString); 405 | #endif 406 | } 407 | 408 | void CommandHandler::addCmdDelim() { 409 | 410 | commandString = commandString + String(delim); 411 | 412 | #ifdef COMMANDHANDLER_DEBUG 413 | Serial.print("Out command is now "); 414 | Serial.println(commandString); 415 | #endif 416 | } 417 | // 418 | void CommandHandler::addCmdTerm() { 419 | 420 | commandString = commandString + String(term); 421 | 422 | #ifdef COMMANDHANDLER_DEBUG 423 | Serial.print("Out command is now "); 424 | Serial.println(commandString); 425 | #endif 426 | } 427 | 428 | void CommandHandler::addCmdBool(bool value) { 429 | 430 | commandString = commandString + String(value); 431 | 432 | #ifdef COMMANDHANDLER_DEBUG 433 | Serial.print("Out command is now "); 434 | Serial.println(commandString); 435 | #endif 436 | } 437 | 438 | void CommandHandler::addCmdInt(int value) { 439 | 440 | commandString = commandString + String(value, DEC); 441 | 442 | #ifdef COMMANDHANDLER_DEBUG 443 | Serial.print("Out command is now "); 444 | Serial.println(commandString); 445 | #endif 446 | } 447 | 448 | void CommandHandler::addCmdLong(long value) { 449 | 450 | commandString = commandString + String(value, DEC); 451 | 452 | #ifdef COMMANDHANDLER_DEBUG 453 | Serial.print("Out command is now "); 454 | Serial.println(commandString); 455 | #endif 456 | } 457 | 458 | 459 | void CommandHandler::setCmdDecimal(byte decimal) { 460 | commandDecimal = decimal; 461 | } 462 | 463 | void CommandHandler::addCmdFloat(double value) { 464 | addCmdFloat(value, commandDecimal); 465 | } 466 | 467 | void CommandHandler::addCmdFloat(float value, byte decimal) { 468 | 469 | 470 | commandString = commandString + String(value, decimal); 471 | 472 | #ifdef COMMANDHANDLER_DEBUG 473 | Serial.print("Out command is now "); 474 | Serial.println(commandString); 475 | #endif 476 | } 477 | 478 | void CommandHandler::addCmdDouble(double value) { 479 | addCmdDouble(value, commandDecimal); 480 | } 481 | 482 | void CommandHandler::addCmdDouble(double value, byte decimal) { 483 | 484 | commandString = commandString + String(value, decimal); 485 | 486 | #ifdef COMMANDHANDLER_DEBUG 487 | Serial.print("Out command is now "); 488 | Serial.println(commandString); 489 | #endif 490 | } 491 | 492 | void CommandHandler::addCmdString(const char *value) { 493 | 494 | commandString = commandString + String(value); 495 | 496 | #ifdef COMMANDHANDLER_DEBUG 497 | Serial.print("Out command is now "); 498 | Serial.println(commandString); 499 | #endif 500 | 501 | } 502 | 503 | char* CommandHandler::getOutCmd() { 504 | 505 | commandString.toCharArray(command, COMMANDHANDLER_BUFFER + 1); 506 | 507 | return command; 508 | } 509 | 510 | void CommandHandler::setOutCmdSerial(Stream &outStream) { 511 | outCmdStream = &outStream; 512 | } 513 | 514 | void CommandHandler::sendCmdSerial() { 515 | sendCmdSerial(*outCmdStream); 516 | } 517 | 518 | void CommandHandler::sendCmdSerial(Stream &outStream) { 519 | outStream.print(commandString); 520 | } 521 | -------------------------------------------------------------------------------- /wstring_fix/WString.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | WString.cpp - String library for Wiring & Arduino 3 | ...mostly rewritten by Paul Stoffregen... 4 | Copyright (c) 2009-10 Hernando Barragan. All rights reserved. 5 | Copyright 2011, Paul Stoffregen, paul@pjrc.com 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #include "WString.h" 23 | 24 | /*********************************************/ 25 | /* Constructors */ 26 | /*********************************************/ 27 | 28 | String::String(const char *cstr) 29 | { 30 | init(); 31 | if (cstr) copy(cstr, strlen(cstr)); 32 | } 33 | 34 | String::String(const String &value) 35 | { 36 | init(); 37 | *this = value; 38 | } 39 | 40 | String::String(const __FlashStringHelper *pstr) 41 | { 42 | init(); 43 | *this = pstr; 44 | } 45 | 46 | #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) 47 | String::String(String &&rval) 48 | { 49 | init(); 50 | move(rval); 51 | } 52 | String::String(StringSumHelper &&rval) 53 | { 54 | init(); 55 | move(rval); 56 | } 57 | #endif 58 | 59 | String::String(char c) 60 | { 61 | init(); 62 | char buf[2]; 63 | buf[0] = c; 64 | buf[1] = 0; 65 | *this = buf; 66 | } 67 | 68 | String::String(unsigned char value, unsigned char base) 69 | { 70 | init(); 71 | char buf[1 + 8 * sizeof(unsigned char)]; 72 | utoa(value, buf, base); 73 | *this = buf; 74 | } 75 | 76 | String::String(int value, unsigned char base) 77 | { 78 | init(); 79 | char buf[2 + 8 * sizeof(int)]; 80 | itoa(value, buf, base); 81 | *this = buf; 82 | } 83 | 84 | String::String(unsigned int value, unsigned char base) 85 | { 86 | init(); 87 | char buf[1 + 8 * sizeof(unsigned int)]; 88 | utoa(value, buf, base); 89 | *this = buf; 90 | } 91 | 92 | String::String(long value, unsigned char base) 93 | { 94 | init(); 95 | char buf[2 + 8 * sizeof(long)]; 96 | ltoa(value, buf, base); 97 | *this = buf; 98 | } 99 | 100 | String::String(unsigned long value, unsigned char base) 101 | { 102 | init(); 103 | char buf[1 + 8 * sizeof(unsigned long)]; 104 | ultoa(value, buf, base); 105 | *this = buf; 106 | } 107 | 108 | String::String(float value, unsigned char decimalPlaces) 109 | { 110 | init(); 111 | char buf[33]; 112 | *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); 113 | } 114 | 115 | String::String(double value, unsigned char decimalPlaces) 116 | { 117 | init(); 118 | char buf[33]; 119 | *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); 120 | } 121 | 122 | String::~String() 123 | { 124 | free(buffer); 125 | } 126 | 127 | /*********************************************/ 128 | /* Memory Management */ 129 | /*********************************************/ 130 | 131 | inline void String::init(void) 132 | { 133 | buffer = NULL; 134 | capacity = 0; 135 | len = 0; 136 | } 137 | 138 | void String::invalidate(void) 139 | { 140 | if (buffer) free(buffer); 141 | buffer = NULL; 142 | capacity = len = 0; 143 | } 144 | 145 | unsigned char String::reserve(unsigned int size) 146 | { 147 | if (buffer && capacity >= size) return 1; 148 | if (changeBuffer(size)) { 149 | if (len == 0) buffer[0] = 0; 150 | return 1; 151 | } 152 | return 0; 153 | } 154 | 155 | unsigned char String::changeBuffer(unsigned int maxStrLen) 156 | { 157 | char *newbuffer = (char *)realloc(buffer, maxStrLen + 1); 158 | if (newbuffer) { 159 | buffer = newbuffer; 160 | capacity = maxStrLen; 161 | return 1; 162 | } 163 | return 0; 164 | } 165 | 166 | /*********************************************/ 167 | /* Copy and Move */ 168 | /*********************************************/ 169 | 170 | String & String::copy(const char *cstr, unsigned int length) 171 | { 172 | if (!reserve(length)) { 173 | invalidate(); 174 | return *this; 175 | } 176 | len = length; 177 | strcpy(buffer, cstr); 178 | return *this; 179 | } 180 | 181 | String & String::copy(const __FlashStringHelper *pstr, unsigned int length) 182 | { 183 | if (!reserve(length)) { 184 | invalidate(); 185 | return *this; 186 | } 187 | len = length; 188 | strcpy_P(buffer, (PGM_P)pstr); 189 | return *this; 190 | } 191 | 192 | #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) 193 | void String::move(String &rhs) 194 | { 195 | if (buffer) { 196 | if (capacity >= rhs.len) { 197 | strcpy(buffer, rhs.buffer); 198 | len = rhs.len; 199 | rhs.len = 0; 200 | return; 201 | } else { 202 | free(buffer); 203 | } 204 | } 205 | buffer = rhs.buffer; 206 | capacity = rhs.capacity; 207 | len = rhs.len; 208 | rhs.buffer = NULL; 209 | rhs.capacity = 0; 210 | rhs.len = 0; 211 | } 212 | #endif 213 | 214 | String & String::operator = (const String &rhs) 215 | { 216 | if (this == &rhs) return *this; 217 | 218 | if (rhs.buffer) copy(rhs.buffer, rhs.len); 219 | else invalidate(); 220 | 221 | return *this; 222 | } 223 | 224 | #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) 225 | String & String::operator = (String &&rval) 226 | { 227 | if (this != &rval) move(rval); 228 | return *this; 229 | } 230 | 231 | String & String::operator = (StringSumHelper &&rval) 232 | { 233 | if (this != &rval) move(rval); 234 | return *this; 235 | } 236 | #endif 237 | 238 | String & String::operator = (const char *cstr) 239 | { 240 | if (cstr) copy(cstr, strlen(cstr)); 241 | else invalidate(); 242 | 243 | return *this; 244 | } 245 | 246 | String & String::operator = (const __FlashStringHelper *pstr) 247 | { 248 | if (pstr) copy(pstr, strlen_P((PGM_P)pstr)); 249 | else invalidate(); 250 | 251 | return *this; 252 | } 253 | 254 | /*********************************************/ 255 | /* concat */ 256 | /*********************************************/ 257 | 258 | unsigned char String::concat(const String &s) 259 | { 260 | return concat(s.buffer, s.len); 261 | } 262 | 263 | unsigned char String::concat(const char *cstr, unsigned int length) 264 | { 265 | unsigned int newlen = len + length; 266 | if (!cstr) return 0; 267 | if (length == 0) return 1; 268 | if (!reserve(newlen)) return 0; 269 | strcpy(buffer + len, cstr); 270 | len = newlen; 271 | return 1; 272 | } 273 | 274 | unsigned char String::concat(const char *cstr) 275 | { 276 | if (!cstr) return 0; 277 | return concat(cstr, strlen(cstr)); 278 | } 279 | 280 | unsigned char String::concat(char c) 281 | { 282 | char buf[2]; 283 | buf[0] = c; 284 | buf[1] = 0; 285 | return concat(buf, 1); 286 | } 287 | 288 | unsigned char String::concat(unsigned char num) 289 | { 290 | char buf[1 + 3 * sizeof(unsigned char)]; 291 | itoa(num, buf, 10); 292 | return concat(buf, strlen(buf)); 293 | } 294 | 295 | unsigned char String::concat(int num) 296 | { 297 | char buf[2 + 3 * sizeof(int)]; 298 | itoa(num, buf, 10); 299 | return concat(buf, strlen(buf)); 300 | } 301 | 302 | unsigned char String::concat(unsigned int num) 303 | { 304 | char buf[1 + 3 * sizeof(unsigned int)]; 305 | utoa(num, buf, 10); 306 | return concat(buf, strlen(buf)); 307 | } 308 | 309 | unsigned char String::concat(long num) 310 | { 311 | char buf[2 + 3 * sizeof(long)]; 312 | ltoa(num, buf, 10); 313 | return concat(buf, strlen(buf)); 314 | } 315 | 316 | unsigned char String::concat(unsigned long num) 317 | { 318 | char buf[1 + 3 * sizeof(unsigned long)]; 319 | ultoa(num, buf, 10); 320 | return concat(buf, strlen(buf)); 321 | } 322 | 323 | unsigned char String::concat(float num) 324 | { 325 | char buf[20]; 326 | char* string = dtostrf(num, 4, 2, buf); 327 | return concat(string, strlen(string)); 328 | } 329 | 330 | unsigned char String::concat(double num) 331 | { 332 | char buf[20]; 333 | char* string = dtostrf(num, 4, 2, buf); 334 | return concat(string, strlen(string)); 335 | } 336 | 337 | unsigned char String::concat(const __FlashStringHelper * str) 338 | { 339 | if (!str) return 0; 340 | int length = strlen_P((const char *) str); 341 | if (length == 0) return 1; 342 | unsigned int newlen = len + length; 343 | if (!reserve(newlen)) return 0; 344 | strcpy_P(buffer + len, (const char *) str); 345 | len = newlen; 346 | return 1; 347 | } 348 | 349 | /*********************************************/ 350 | /* Concatenate */ 351 | /*********************************************/ 352 | 353 | StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs) 354 | { 355 | StringSumHelper &a = const_cast(lhs); 356 | if (!a.concat(rhs.buffer, rhs.len)) a.invalidate(); 357 | return a; 358 | } 359 | 360 | StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr) 361 | { 362 | StringSumHelper &a = const_cast(lhs); 363 | if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate(); 364 | return a; 365 | } 366 | 367 | StringSumHelper & operator + (const StringSumHelper &lhs, char c) 368 | { 369 | StringSumHelper &a = const_cast(lhs); 370 | if (!a.concat(c)) a.invalidate(); 371 | return a; 372 | } 373 | 374 | StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num) 375 | { 376 | StringSumHelper &a = const_cast(lhs); 377 | if (!a.concat(num)) a.invalidate(); 378 | return a; 379 | } 380 | 381 | StringSumHelper & operator + (const StringSumHelper &lhs, int num) 382 | { 383 | StringSumHelper &a = const_cast(lhs); 384 | if (!a.concat(num)) a.invalidate(); 385 | return a; 386 | } 387 | 388 | StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num) 389 | { 390 | StringSumHelper &a = const_cast(lhs); 391 | if (!a.concat(num)) a.invalidate(); 392 | return a; 393 | } 394 | 395 | StringSumHelper & operator + (const StringSumHelper &lhs, long num) 396 | { 397 | StringSumHelper &a = const_cast(lhs); 398 | if (!a.concat(num)) a.invalidate(); 399 | return a; 400 | } 401 | 402 | StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num) 403 | { 404 | StringSumHelper &a = const_cast(lhs); 405 | if (!a.concat(num)) a.invalidate(); 406 | return a; 407 | } 408 | 409 | StringSumHelper & operator + (const StringSumHelper &lhs, float num) 410 | { 411 | StringSumHelper &a = const_cast(lhs); 412 | if (!a.concat(num)) a.invalidate(); 413 | return a; 414 | } 415 | 416 | StringSumHelper & operator + (const StringSumHelper &lhs, double num) 417 | { 418 | StringSumHelper &a = const_cast(lhs); 419 | if (!a.concat(num)) a.invalidate(); 420 | return a; 421 | } 422 | 423 | StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs) 424 | { 425 | StringSumHelper &a = const_cast(lhs); 426 | if (!a.concat(rhs)) a.invalidate(); 427 | return a; 428 | } 429 | 430 | /*********************************************/ 431 | /* Comparison */ 432 | /*********************************************/ 433 | 434 | int String::compareTo(const String &s) const 435 | { 436 | if (!buffer || !s.buffer) { 437 | if (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer; 438 | if (buffer && len > 0) return *(unsigned char *)buffer; 439 | return 0; 440 | } 441 | return strcmp(buffer, s.buffer); 442 | } 443 | 444 | unsigned char String::equals(const String &s2) const 445 | { 446 | return (len == s2.len && compareTo(s2) == 0); 447 | } 448 | 449 | unsigned char String::equals(const char *cstr) const 450 | { 451 | if (len == 0) return (cstr == NULL || *cstr == 0); 452 | if (cstr == NULL) return buffer[0] == 0; 453 | return strcmp(buffer, cstr) == 0; 454 | } 455 | 456 | unsigned char String::operator<(const String &rhs) const 457 | { 458 | return compareTo(rhs) < 0; 459 | } 460 | 461 | unsigned char String::operator>(const String &rhs) const 462 | { 463 | return compareTo(rhs) > 0; 464 | } 465 | 466 | unsigned char String::operator<=(const String &rhs) const 467 | { 468 | return compareTo(rhs) <= 0; 469 | } 470 | 471 | unsigned char String::operator>=(const String &rhs) const 472 | { 473 | return compareTo(rhs) >= 0; 474 | } 475 | 476 | unsigned char String::equalsIgnoreCase( const String &s2 ) const 477 | { 478 | if (this == &s2) return 1; 479 | if (len != s2.len) return 0; 480 | if (len == 0) return 1; 481 | const char *p1 = buffer; 482 | const char *p2 = s2.buffer; 483 | while (*p1) { 484 | if (tolower(*p1++) != tolower(*p2++)) return 0; 485 | } 486 | return 1; 487 | } 488 | 489 | unsigned char String::startsWith( const String &s2 ) const 490 | { 491 | if (len < s2.len) return 0; 492 | return startsWith(s2, 0); 493 | } 494 | 495 | unsigned char String::startsWith( const String &s2, unsigned int offset ) const 496 | { 497 | if (offset > len - s2.len || !buffer || !s2.buffer) return 0; 498 | return strncmp( &buffer[offset], s2.buffer, s2.len ) == 0; 499 | } 500 | 501 | unsigned char String::endsWith( const String &s2 ) const 502 | { 503 | if ( len < s2.len || !buffer || !s2.buffer) return 0; 504 | return strcmp(&buffer[len - s2.len], s2.buffer) == 0; 505 | } 506 | 507 | /*********************************************/ 508 | /* Character Access */ 509 | /*********************************************/ 510 | 511 | char String::charAt(unsigned int loc) const 512 | { 513 | return operator[](loc); 514 | } 515 | 516 | void String::setCharAt(unsigned int loc, char c) 517 | { 518 | if (loc < len) buffer[loc] = c; 519 | } 520 | 521 | char & String::operator[](unsigned int index) 522 | { 523 | static char dummy_writable_char; 524 | if (index >= len || !buffer) { 525 | dummy_writable_char = 0; 526 | return dummy_writable_char; 527 | } 528 | return buffer[index]; 529 | } 530 | 531 | char String::operator[]( unsigned int index ) const 532 | { 533 | if (index >= len || !buffer) return 0; 534 | return buffer[index]; 535 | } 536 | 537 | void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const 538 | { 539 | if (!bufsize || !buf) return; 540 | if (index >= len) { 541 | buf[0] = 0; 542 | return; 543 | } 544 | unsigned int n = bufsize - 1; 545 | if (n > len - index) n = len - index; 546 | strncpy((char *)buf, buffer + index, n); 547 | buf[n] = 0; 548 | } 549 | 550 | /*********************************************/ 551 | /* Search */ 552 | /*********************************************/ 553 | 554 | int String::indexOf(char c) const 555 | { 556 | return indexOf(c, 0); 557 | } 558 | 559 | int String::indexOf( char ch, unsigned int fromIndex ) const 560 | { 561 | if (fromIndex >= len) return -1; 562 | const char* temp = strchr(buffer + fromIndex, ch); 563 | if (temp == NULL) return -1; 564 | return temp - buffer; 565 | } 566 | 567 | int String::indexOf(const String &s2) const 568 | { 569 | return indexOf(s2, 0); 570 | } 571 | 572 | int String::indexOf(const String &s2, unsigned int fromIndex) const 573 | { 574 | if (fromIndex >= len) return -1; 575 | const char *found = strstr(buffer + fromIndex, s2.buffer); 576 | if (found == NULL) return -1; 577 | return found - buffer; 578 | } 579 | 580 | int String::lastIndexOf( char theChar ) const 581 | { 582 | return lastIndexOf(theChar, len - 1); 583 | } 584 | 585 | int String::lastIndexOf(char ch, unsigned int fromIndex) const 586 | { 587 | if (fromIndex >= len) return -1; 588 | char tempchar = buffer[fromIndex + 1]; 589 | buffer[fromIndex + 1] = '\0'; 590 | char* temp = strrchr( buffer, ch ); 591 | buffer[fromIndex + 1] = tempchar; 592 | if (temp == NULL) return -1; 593 | return temp - buffer; 594 | } 595 | 596 | int String::lastIndexOf(const String &s2) const 597 | { 598 | return lastIndexOf(s2, len - s2.len); 599 | } 600 | 601 | int String::lastIndexOf(const String &s2, unsigned int fromIndex) const 602 | { 603 | if (s2.len == 0 || len == 0 || s2.len > len) return -1; 604 | if (fromIndex >= len) fromIndex = len - 1; 605 | int found = -1; 606 | for (char *p = buffer; p <= buffer + fromIndex; p++) { 607 | p = strstr(p, s2.buffer); 608 | if (!p) break; 609 | if ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer; 610 | } 611 | return found; 612 | } 613 | 614 | String String::substring(unsigned int left, unsigned int right) const 615 | { 616 | if (left > right) { 617 | unsigned int temp = right; 618 | right = left; 619 | left = temp; 620 | } 621 | String out; 622 | if (left >= len) return out; 623 | if (right > len) right = len; 624 | char temp = buffer[right]; // save the replaced character 625 | buffer[right] = '\0'; 626 | out = buffer + left; // pointer arithmetic 627 | buffer[right] = temp; //restore character 628 | return out; 629 | } 630 | 631 | /*********************************************/ 632 | /* Modification */ 633 | /*********************************************/ 634 | 635 | void String::replace(char find, char replace) 636 | { 637 | if (!buffer) return; 638 | for (char *p = buffer; *p; p++) { 639 | if (*p == find) *p = replace; 640 | } 641 | } 642 | 643 | void String::replace(const String& find, const String& replace) 644 | { 645 | if (len == 0 || find.len == 0) return; 646 | int diff = replace.len - find.len; 647 | char *readFrom = buffer; 648 | char *foundAt; 649 | if (diff == 0) { 650 | while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { 651 | memcpy(foundAt, replace.buffer, replace.len); 652 | readFrom = foundAt + replace.len; 653 | } 654 | } else if (diff < 0) { 655 | char *writeTo = buffer; 656 | while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { 657 | unsigned int n = foundAt - readFrom; 658 | memcpy(writeTo, readFrom, n); 659 | writeTo += n; 660 | memcpy(writeTo, replace.buffer, replace.len); 661 | writeTo += replace.len; 662 | readFrom = foundAt + find.len; 663 | len += diff; 664 | } 665 | strcpy(writeTo, readFrom); 666 | } else { 667 | unsigned int size = len; // compute size needed for result 668 | while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { 669 | readFrom = foundAt + find.len; 670 | size += diff; 671 | } 672 | if (size == len) return; 673 | if (size > capacity && !changeBuffer(size)) return; // XXX: tell user! 674 | int index = len - 1; 675 | while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) { 676 | readFrom = buffer + index + find.len; 677 | memmove(readFrom + diff, readFrom, len - (readFrom - buffer)); 678 | len += diff; 679 | buffer[len] = 0; 680 | memcpy(buffer + index, replace.buffer, replace.len); 681 | index--; 682 | } 683 | } 684 | } 685 | 686 | void String::remove(unsigned int index){ 687 | // Pass the biggest integer as the count. The remove method 688 | // below will take care of truncating it at the end of the 689 | // string. 690 | remove(index, (unsigned int)-1); 691 | } 692 | 693 | void String::remove(unsigned int index, unsigned int count){ 694 | if (index >= len) { return; } 695 | if (count <= 0) { return; } 696 | if (count > len - index) { count = len - index; } 697 | char *writeTo = buffer + index; 698 | len = len - count; 699 | strncpy(writeTo, buffer + index + count,len - index); 700 | buffer[len] = 0; 701 | } 702 | 703 | void String::toLowerCase(void) 704 | { 705 | if (!buffer) return; 706 | for (char *p = buffer; *p; p++) { 707 | *p = tolower(*p); 708 | } 709 | } 710 | 711 | void String::toUpperCase(void) 712 | { 713 | if (!buffer) return; 714 | for (char *p = buffer; *p; p++) { 715 | *p = toupper(*p); 716 | } 717 | } 718 | 719 | void String::trim(void) 720 | { 721 | if (!buffer || len == 0) return; 722 | char *begin = buffer; 723 | while (isspace(*begin)) begin++; 724 | char *end = buffer + len - 1; 725 | while (isspace(*end) && end >= begin) end--; 726 | len = end + 1 - begin; 727 | if (begin > buffer) memcpy(buffer, begin, len); 728 | buffer[len] = 0; 729 | } 730 | 731 | /*********************************************/ 732 | /* Parsing / Conversion */ 733 | /*********************************************/ 734 | 735 | long String::toInt(void) const 736 | { 737 | if (buffer) return atol(buffer); 738 | return 0; 739 | } 740 | 741 | float String::toFloat(void) const 742 | { 743 | if (buffer) return float(atof(buffer)); 744 | return 0; 745 | } 746 | --------------------------------------------------------------------------------