| /************************************************************************* | |
| * | |
| * © 2016 and later: Unicode, Inc. and others. | |
| * License & terms of use: http://www.unicode.org/copyright.html | |
| * | |
| ************************************************************************** | |
| ************************************************************************** | |
| * | |
| * Copyright (C) 2002-2010, International Business Machines | |
| * Corporation and others. All Rights Reserved. | |
| * | |
| *************************************************************************** | |
| */ | |
| // | |
| // ugrep - an ICU sample program illustrating the use of ICU Regular Expressions. | |
| // | |
| // The use of the ICU Regex API all occurs within the main() | |
| // function. The rest of the code deals with opening files, | |
| // encoding conversions, printing results, etc. | |
| // | |
| // This is not a full-featured grep program. The command line options | |
| // have been kept to a minimum to avoid complicating the sample code. | |
| // | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "unicode/utypes.h" | |
| #include "unicode/ustring.h" | |
| #include "unicode/regex.h" | |
| #include "unicode/ucnv.h" | |
| #include "unicode/uclean.h" | |
| using namespace icu; | |
| // | |
| // The following variables contain parameters that may be set from the command line. | |
| // | |
| const char *pattern = NULL; // The regular expression | |
| int firstFileNum; // argv index of the first file name | |
| UBool displayFileName = false; | |
| UBool displayLineNum = false; | |
| // | |
| // Info regarding the file currently being processed | |
| // | |
| const char *fileName; | |
| int fileLen; // Length, in UTF-16 Code Units. | |
| UChar *ucharBuf = 0; // Buffer, holds converted file. (Simple minded program, always reads | |
| // the whole file at once. | |
| char *charBuf = 0; // Buffer, for original, unconverted file data. | |
| // | |
| // Info regarding the line currently being processed | |
| // | |
| int lineStart; // Index of first char of the current line in the file buffer | |
| int lineEnd; // Index of char following the new line sequence for the current line | |
| int lineNum; | |
| // | |
| // Converter, used on output to convert Unicode data back to char * | |
| // so that it will display in non-Unicode terminal windows. | |
| // | |
| UConverter *outConverter = 0; | |
| // | |
| // Function forward declarations | |
| // | |
| void processOptions(int argc, const char **argv); | |
| void nextLine(int start); | |
| void printMatch(); | |
| void printUsage(); | |
| void readFile(const char *name); | |
| //------------------------------------------------------------------------------------------ | |
| // | |
| // main for ugrep | |
| // | |
| // Structurally, all use of the ICU Regular Expression API is in main(), | |
| // and all of the supporting stuff necessary to make a running program, but | |
| // not directly related to regular expressions, is factored out into these other | |
| // functions. | |
| // | |
| //------------------------------------------------------------------------------------------ | |
| int main(int argc, const char** argv) { | |
| UBool matchFound = false; | |
| // | |
| // Process the command line options. | |
| // | |
| processOptions(argc, argv); | |
| // | |
| // Create a RegexPattern object from the user supplied pattern string. | |
| // | |
| UErrorCode status = U_ZERO_ERROR; // All ICU operations report success or failure | |
| // in a status variable. | |
| UParseError parseErr; // In the event of a syntax error in the regex pattern, | |
| // this struct will contain the position of the | |
| // error. | |
| RegexPattern *rePat = RegexPattern::compile(pattern, parseErr, status); | |
| // Note that C++ is doing an automatic conversion | |
| // of the (char *) pattern to a temporary | |
| // UnicodeString object. | |
| if (U_FAILURE(status)) { | |
| fprintf(stderr, "ugrep: error in pattern: \"%s\" at position %d\n", | |
| u_errorName(status), parseErr.offset); | |
| exit(-1); | |
| } | |
| // | |
| // Create a RegexMatcher from the newly created pattern. | |
| // | |
| UnicodeString empty; | |
| RegexMatcher *matcher = rePat->matcher(empty, status); | |
| if (U_FAILURE(status)) { | |
| fprintf(stderr, "ugrep: error in creating RegexMatcher: \"%s\"\n", | |
| u_errorName(status)); | |
| exit(-1); | |
| } | |
| // | |
| // Loop, processing each of the input files. | |
| // | |
| for (int fileNum=firstFileNum; fileNum < argc; fileNum++) { | |
| readFile(argv[fileNum]); | |
| // | |
| // Loop through the lines of a file, trying to match the regex pattern on each. | |
| // | |
| for (nextLine(0); lineStart<fileLen; nextLine(lineEnd)) { | |
| UnicodeString s(false, ucharBuf+lineStart, lineEnd-lineStart); | |
| matcher->reset(s); | |
| if (matcher->find()) { | |
| matchFound = true; | |
| printMatch(); | |
| } | |
| } | |
| } | |
| // | |
| // Clean up | |
| // | |
| delete matcher; | |
| delete rePat; | |
| free(ucharBuf); | |
| free(charBuf); | |
| ucnv_close(outConverter); | |
| u_cleanup(); // shut down ICU, release any cached data it owns. | |
| return matchFound? 0: 1; | |
| } | |
| //------------------------------------------------------------------------------------------ | |
| // | |
| // doOptions Run through the command line options, and set | |
| // the global variables accordingly. | |
| // | |
| // exit without returning if an error occurred and | |
| // ugrep should not proceed further. | |
| // | |
| //------------------------------------------------------------------------------------------ | |
| void processOptions(int argc, const char **argv) { | |
| int optInd; | |
| UBool doUsage = false; | |
| UBool doVersion = false; | |
| const char *arg; | |
| for(optInd = 1; optInd < argc; ++optInd) { | |
| arg = argv[optInd]; | |
| /* version info */ | |
| if(strcmp(arg, "-V") == 0 || strcmp(arg, "--version") == 0) { | |
| doVersion = true; | |
| } | |
| /* usage info */ | |
| else if(strcmp(arg, "--help") == 0) { | |
| doUsage = true; | |
| } | |
| else if(strcmp(arg, "-n") == 0 || strcmp(arg, "--line-number") == 0) { | |
| displayLineNum = true; | |
| } | |
| /* POSIX.1 says all arguments after -- are not options */ | |
| else if(strcmp(arg, "--") == 0) { | |
| /* skip the -- */ | |
| ++optInd; | |
| break; | |
| } | |
| /* unrecognized option */ | |
| else if(strncmp(arg, "-", strlen("-")) == 0) { | |
| printf("ugrep: invalid option -- %s\n", arg+1); | |
| doUsage = true; | |
| } | |
| /* done with options */ | |
| else { | |
| break; | |
| } | |
| } | |
| if (doUsage) { | |
| printUsage(); | |
| exit(0); | |
| } | |
| if (doVersion) { | |
| printf("ugrep version 0.01\n"); | |
| if (optInd == argc) { | |
| exit(0); | |
| } | |
| } | |
| int remainingArgs = argc-optInd; // pattern file ... | |
| if (remainingArgs < 2) { | |
| fprintf(stderr, "ugrep: files or pattern are missing.\n"); | |
| printUsage(); | |
| exit(1); | |
| } | |
| if (remainingArgs > 2) { | |
| // More than one file to be processed. Display file names with match output. | |
| displayFileName = true; | |
| } | |
| pattern = argv[optInd]; | |
| firstFileNum = optInd+1; | |
| } | |
| //------------------------------------------------------------------------------------------ | |
| // | |
| // printUsage | |
| // | |
| //------------------------------------------------------------------------------------------ | |
| void printUsage() { | |
| printf("ugrep [options] pattern file...\n" | |
| " -V or --version display version information\n" | |
| " --help display this help and exit\n" | |
| " -- stop further option processing\n" | |
| "-n, --line-number Prefix each line of output with the line number within its input file.\n" | |
| ); | |
| exit(0); | |
| } | |
| //------------------------------------------------------------------------------------------ | |
| // | |
| // readFile Read a file into memory, and convert it to Unicode. | |
| // | |
| // Since this is just a demo program, take the simple minded approach | |
| // of always reading the whole file at once. No intelligent buffering | |
| // is done. | |
| // | |
| //------------------------------------------------------------------------------------------ | |
| void readFile(const char *name) { | |
| // | |
| // Initialize global file variables | |
| // | |
| fileName = name; | |
| fileLen = 0; // zero length prevents processing in case of errors. | |
| // | |
| // Open the file and determine its size. | |
| // | |
| FILE *file = fopen(name, "rb"); | |
| if (file == 0 ) { | |
| fprintf(stderr, "ugrep: Could not open file \"%s\"\n", fileName); | |
| return; | |
| } | |
| fseek(file, 0, SEEK_END); | |
| int rawFileLen = ftell(file); | |
| fseek(file, 0, SEEK_SET); | |
| // | |
| // Read in the file | |
| // | |
| charBuf = (char *)realloc(charBuf, rawFileLen+1); // Need error checking... | |
| int t = static_cast<int>(fread(charBuf, 1, rawFileLen, file)); | |
| if (t != rawFileLen) { | |
| fprintf(stderr, "Error reading file \"%s\"\n", fileName); | |
| fclose(file); | |
| return; | |
| } | |
| charBuf[rawFileLen]=0; | |
| fclose(file); | |
| // | |
| // Look for a Unicode Signature (BOM) in the data | |
| // | |
| int32_t signatureLength; | |
| const char * charDataStart = charBuf; | |
| UErrorCode status = U_ZERO_ERROR; | |
| const char* encoding = ucnv_detectUnicodeSignature( | |
| charDataStart, rawFileLen, &signatureLength, &status); | |
| if (U_FAILURE(status)) { | |
| fprintf(stderr, "ugrep: ICU Error \"%s\" from ucnv_detectUnicodeSignature()\n", | |
| u_errorName(status)); | |
| return; | |
| } | |
| if(encoding!=NULL ){ | |
| charDataStart += signatureLength; | |
| rawFileLen -= signatureLength; | |
| } | |
| // | |
| // Open a converter to take the file to UTF-16 | |
| // | |
| UConverter* conv; | |
| conv = ucnv_open(encoding, &status); | |
| if (U_FAILURE(status)) { | |
| fprintf(stderr, "ugrep: ICU Error \"%s\" from ucnv_open()\n", u_errorName(status)); | |
| return; | |
| } | |
| // | |
| // Convert the file data to UChar. | |
| // Preflight first to determine required buffer size. | |
| // | |
| uint32_t destCap = ucnv_toUChars(conv, | |
| NULL, // dest, | |
| 0, // destCapacity, | |
| charDataStart, | |
| rawFileLen, | |
| &status); | |
| if (status != U_BUFFER_OVERFLOW_ERROR) { | |
| fprintf(stderr, "ugrep: ucnv_toUChars: ICU Error \"%s\"\n", u_errorName(status)); | |
| return; | |
| }; | |
| status = U_ZERO_ERROR; | |
| ucharBuf = (UChar *)realloc(ucharBuf, (destCap+1) * sizeof(UChar)); | |
| ucnv_toUChars(conv, | |
| ucharBuf, // dest, | |
| destCap+1, | |
| charDataStart, | |
| rawFileLen, | |
| &status); | |
| if (U_FAILURE(status)) { | |
| fprintf(stderr, "ugrep: ucnv_toUChars: ICU Error \"%s\"\n", u_errorName(status)); | |
| return; | |
| }; | |
| ucnv_close(conv); | |
| // | |
| // Successful conversion. Set the global size variables so that | |
| // the rest of the processing will proceed for this file. | |
| // | |
| fileLen = destCap; | |
| } | |
| //------------------------------------------------------------------------------------------ | |
| // | |
| // nextLine Advance the line index variables, starting at the | |
| // specified position in the input file buffer, by | |
| // scanning forward until the next end-of-line. | |
| // | |
| // Need to take into account all of the possible Unicode | |
| // line ending sequences. | |
| // | |
| //------------------------------------------------------------------------------------------ | |
| void nextLine(int startPos) { | |
| if (startPos == 0) { | |
| lineNum = 0; | |
| } else { | |
| lineNum++; | |
| } | |
| lineStart = lineEnd = startPos; | |
| for (;;) { | |
| if (lineEnd >= fileLen) { | |
| return; | |
| } | |
| UChar c = ucharBuf[lineEnd]; | |
| lineEnd++; | |
| if (c == 0x0a || // Line Feed | |
| c == 0x0c || // Form Feed | |
| c == 0x0d || // Carriage Return | |
| c == 0x85 || // Next Line | |
| c == 0x2028 || // Line Separator | |
| c == 0x2029) // Paragraph separator | |
| { | |
| break; | |
| } | |
| } | |
| // Check for CR/LF sequence, and advance over the LF if we're in the middle of one. | |
| if (lineEnd < fileLen && | |
| ucharBuf[lineEnd-1] == 0x0d && | |
| ucharBuf[lineEnd] == 0x0a) | |
| { | |
| lineEnd++; | |
| } | |
| } | |
| //------------------------------------------------------------------------------------------ | |
| // | |
| // printMatch Called when a matching line has been located. | |
| // Print out the line from the file with the match, after | |
| // converting it back to the default code page. | |
| // | |
| //------------------------------------------------------------------------------------------ | |
| void printMatch() { | |
| char buf[2000]; | |
| UErrorCode status = U_ZERO_ERROR; | |
| // If we haven't already created a converter for output, do it now. | |
| if (outConverter == 0) { | |
| outConverter = ucnv_open(NULL, &status); | |
| if (U_FAILURE(status)) { | |
| fprintf(stderr, "ugrep: Error opening default converter: \"%s\"\n", | |
| u_errorName(status)); | |
| exit(-1); | |
| } | |
| }; | |
| // Convert the line to be printed back to the default 8 bit code page. | |
| // If the line is too long for our buffer, just truncate it. | |
| ucnv_fromUChars(outConverter, | |
| buf, // destination buffer for conversion | |
| sizeof(buf), // capacity of destination buffer | |
| &ucharBuf[lineStart], // Input to conversion | |
| lineEnd-lineStart, // number of UChars to convert | |
| &status); | |
| buf[sizeof(buf)-1] = 0; // Add null for use in case of too long lines. | |
| // The converter null-terminates its output unless | |
| // the buffer completely fills. | |
| if (displayFileName) { | |
| printf("%s:", fileName); | |
| } | |
| if (displayLineNum) { | |
| printf("%d:", lineNum); | |
| } | |
| printf("%s", buf); | |
| } | |