1 /**************************************************************************
2 *
3 * Copyright (C) 2002-2010, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *
6 ***************************************************************************
7 */
8
9 //
10 // ugrep - an ICU sample program illustrating the use of ICU Regular Expressions.
11 //
12 // The use of the ICU Regex API all occurs within the main()
13 // function. The rest of the code deals with with opening files,
14 // encoding conversions, printing results, etc.
15 //
16 // This is not a full-featured grep program. The command line options
17 // have been kept to a minimum to avoid complicating the sample code.
18 //
19
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "unicode/utypes.h"
27 #include "unicode/ustring.h"
28 #include "unicode/regex.h"
29 #include "unicode/ucnv.h"
30 #include "unicode/uclean.h"
31
32
33 //
34 // The following variables contain paramters that may be set from the command line.
35 //
36 const char *pattern = NULL; // The regular expression
37 int firstFileNum; // argv index of the first file name
38 UBool displayFileName = FALSE;
39 UBool displayLineNum = FALSE;
40
41
42 //
43 // Info regarding the file currently being processed
44 //
45 const char *fileName;
46 int fileLen; // Length, in UTF-16 Code Units.
47
48 UChar *ucharBuf = 0; // Buffer, holds converted file. (Simple minded program, always reads
49 // the whole file at once.
50
51 char *charBuf = 0; // Buffer, for original, unconverted file data.
52
53
54 //
55 // Info regarding the line currently being processed
56 //
57 int lineStart; // Index of first char of the current line in the file buffer
58 int lineEnd; // Index of char following the new line sequence for the current line
59 int lineNum;
60
61 //
62 // Converter, used on output to convert Unicode data back to char *
63 // so that it will display in non-Unicode terminal windows.
64 //
65 UConverter *outConverter = 0;
66
67 //
68 // Function forward declarations
69 //
70 void processOptions(int argc, const char **argv);
71 void nextLine(int start);
72 void printMatch();
73 void printUsage();
74 void readFile(const char *name);
75
76
77
78 //------------------------------------------------------------------------------------------
79 //
80 // main for ugrep
81 //
82 // Structurally, all use of the ICU Regular Expression API is in main(),
83 // and all of the supporting stuff necessary to make a running program, but
84 // not directly related to regular expressions, is factored out into these other
85 // functions.
86 //
87 //------------------------------------------------------------------------------------------
main(int argc,const char ** argv)88 int main(int argc, const char** argv) {
89 UBool matchFound = FALSE;
90
91 //
92 // Process the commmand line options.
93 //
94 processOptions(argc, argv);
95
96 //
97 // Create a RegexPattern object from the user supplied pattern string.
98 //
99 UErrorCode status = U_ZERO_ERROR; // All ICU operations report success or failure
100 // in a status variable.
101
102 UParseError parseErr; // In the event of a syntax error in the regex pattern,
103 // this struct will contain the position of the
104 // error.
105
106 RegexPattern *rePat = RegexPattern::compile(pattern, parseErr, status);
107 // Note that C++ is doing an automatic conversion
108 // of the (char *) pattern to a temporary
109 // UnicodeString object.
110 if (U_FAILURE(status)) {
111 fprintf(stderr, "ugrep: error in pattern: \"%s\" at position %d\n",
112 u_errorName(status), parseErr.offset);
113 exit(-1);
114 }
115
116 //
117 // Create a RegexMatcher from the newly created pattern.
118 //
119 UnicodeString empty;
120 RegexMatcher *matcher = rePat->matcher(empty, status);
121 if (U_FAILURE(status)) {
122 fprintf(stderr, "ugrep: error in creating RegexMatcher: \"%s\"\n",
123 u_errorName(status));
124 exit(-1);
125 }
126
127 //
128 // Loop, processing each of the input files.
129 //
130 for (int fileNum=firstFileNum; fileNum < argc; fileNum++) {
131 readFile(argv[fileNum]);
132
133 //
134 // Loop through the lines of a file, trying to match the regex pattern on each.
135 //
136 for (nextLine(0); lineStart<fileLen; nextLine(lineEnd)) {
137 UnicodeString s(FALSE, ucharBuf+lineStart, lineEnd-lineStart);
138 matcher->reset(s);
139 if (matcher->find()) {
140 matchFound = TRUE;
141 printMatch();
142 }
143 }
144 }
145
146 //
147 // Clean up
148 //
149 delete matcher;
150 delete rePat;
151 free(ucharBuf);
152 free(charBuf);
153 ucnv_close(outConverter);
154
155 u_cleanup(); // shut down ICU, release any cached data it owns.
156
157 return matchFound? 0: 1;
158 }
159
160
161
162 //------------------------------------------------------------------------------------------
163 //
164 // doOptions Run through the command line options, and set
165 // the global variables accordingly.
166 //
167 // exit without returning if an error occured and
168 // ugrep should not proceed further.
169 //
170 //------------------------------------------------------------------------------------------
processOptions(int argc,const char ** argv)171 void processOptions(int argc, const char **argv) {
172 int optInd;
173 UBool doUsage = FALSE;
174 UBool doVersion = FALSE;
175 const char *arg;
176
177
178 for(optInd = 1; optInd < argc; ++optInd) {
179 arg = argv[optInd];
180
181 /* version info */
182 if(strcmp(arg, "-V") == 0 || strcmp(arg, "--version") == 0) {
183 doVersion = TRUE;
184 }
185 /* usage info */
186 else if(strcmp(arg, "--help") == 0) {
187 doUsage = TRUE;
188 }
189 else if(strcmp(arg, "-n") == 0 || strcmp(arg, "--line-number") == 0) {
190 displayLineNum = TRUE;
191 }
192 /* POSIX.1 says all arguments after -- are not options */
193 else if(strcmp(arg, "--") == 0) {
194 /* skip the -- */
195 ++optInd;
196 break;
197 }
198 /* unrecognized option */
199 else if(strncmp(arg, "-", strlen("-")) == 0) {
200 printf("ugrep: invalid option -- %s\n", arg+1);
201 doUsage = TRUE;
202 }
203 /* done with options */
204 else {
205 break;
206 }
207 }
208
209 if (doUsage) {
210 printUsage();
211 exit(0);
212 }
213
214 if (doVersion) {
215 printf("ugrep version 0.01\n");
216 if (optInd == argc) {
217 exit(0);
218 }
219 }
220
221 int remainingArgs = argc-optInd; // pattern file ...
222 if (remainingArgs < 2) {
223 fprintf(stderr, "ugrep: files or pattern are missing.\n");
224 printUsage();
225 exit(1);
226 }
227
228 if (remainingArgs > 2) {
229 // More than one file to be processed. Display file names with match output.
230 displayFileName = TRUE;
231 }
232
233 pattern = argv[optInd];
234 firstFileNum = optInd+1;
235 }
236
237 //------------------------------------------------------------------------------------------
238 //
239 // printUsage
240 //
241 //------------------------------------------------------------------------------------------
printUsage()242 void printUsage() {
243 printf("ugrep [options] pattern file...\n"
244 " -V or --version display version information\n"
245 " --help display this help and exit\n"
246 " -- stop further option processing\n"
247 "-n, --line-number Prefix each line of output with the line number within its input file.\n"
248 );
249 exit(0);
250 }
251
252 //------------------------------------------------------------------------------------------
253 //
254 // readFile Read a file into memory, and convert it to Unicode.
255 //
256 // Since this is just a demo program, take the simple minded approach
257 // of always reading the whole file at once. No intelligent buffering
258 // is done.
259 //
260 //------------------------------------------------------------------------------------------
readFile(const char * name)261 void readFile(const char *name) {
262
263 //
264 // Initialize global file variables
265 //
266 fileName = name;
267 fileLen = 0; // zero length prevents processing in case of errors.
268
269
270 //
271 // Open the file and determine its size.
272 //
273 FILE *file = fopen(name, "rb");
274 if (file == 0 ) {
275 fprintf(stderr, "ugrep: Could not open file \"%s\"\n", fileName);
276 return;
277 }
278 fseek(file, 0, SEEK_END);
279 int rawFileLen = ftell(file);
280 fseek(file, 0, SEEK_SET);
281
282
283 //
284 // Read in the file
285 //
286 charBuf = (char *)realloc(charBuf, rawFileLen+1); // Need error checking...
287 int t = fread(charBuf, 1, rawFileLen, file);
288 if (t != rawFileLen) {
289 fprintf(stderr, "Error reading file \"%s\"\n", fileName);
290 fclose(file);
291 return;
292 }
293 charBuf[rawFileLen]=0;
294 fclose(file);
295
296 //
297 // Look for a Unicode Signature (BOM) in the data
298 //
299 int32_t signatureLength;
300 const char * charDataStart = charBuf;
301 UErrorCode status = U_ZERO_ERROR;
302 const char* encoding = ucnv_detectUnicodeSignature(
303 charDataStart, rawFileLen, &signatureLength, &status);
304 if (U_FAILURE(status)) {
305 fprintf(stderr, "ugrep: ICU Error \"%s\" from ucnv_detectUnicodeSignature()\n",
306 u_errorName(status));
307 return;
308 }
309 if(encoding!=NULL ){
310 charDataStart += signatureLength;
311 rawFileLen -= signatureLength;
312 }
313
314 //
315 // Open a converter to take the file to UTF-16
316 //
317 UConverter* conv;
318 conv = ucnv_open(encoding, &status);
319 if (U_FAILURE(status)) {
320 fprintf(stderr, "ugrep: ICU Error \"%s\" from ucnv_open()\n", u_errorName(status));
321 return;
322 }
323
324 //
325 // Convert the file data to UChar.
326 // Preflight first to determine required buffer size.
327 //
328 uint32_t destCap = ucnv_toUChars(conv,
329 NULL, // dest,
330 0, // destCapacity,
331 charDataStart,
332 rawFileLen,
333 &status);
334 if (status != U_BUFFER_OVERFLOW_ERROR) {
335 fprintf(stderr, "ugrep: ucnv_toUChars: ICU Error \"%s\"\n", u_errorName(status));
336 return;
337 };
338
339 status = U_ZERO_ERROR;
340 ucharBuf = (UChar *)realloc(ucharBuf, (destCap+1) * sizeof(UChar));
341 ucnv_toUChars(conv,
342 ucharBuf, // dest,
343 destCap+1,
344 charDataStart,
345 rawFileLen,
346 &status);
347 if (U_FAILURE(status)) {
348 fprintf(stderr, "ugrep: ucnv_toUChars: ICU Error \"%s\"\n", u_errorName(status));
349 return;
350 };
351 ucnv_close(conv);
352
353 //
354 // Successful conversion. Set the global size variables so that
355 // the rest of the processing will proceed for this file.
356 //
357 fileLen = destCap;
358 }
359
360
361
362
363
364 //------------------------------------------------------------------------------------------
365 //
366 // nextLine Advance the line index variables, starting at the
367 // specified position in the input file buffer, by
368 // scanning forwrd until the next end-of-line.
369 //
370 // Need to take into account all of the possible Unicode
371 // line ending sequences.
372 //
373 //------------------------------------------------------------------------------------------
nextLine(int startPos)374 void nextLine(int startPos) {
375 if (startPos == 0) {
376 lineNum = 0;
377 } else {
378 lineNum++;
379 }
380 lineStart = lineEnd = startPos;
381
382 for (;;) {
383 if (lineEnd >= fileLen) {
384 return;
385 }
386 UChar c = ucharBuf[lineEnd];
387 lineEnd++;
388 if (c == 0x0a || // Line Feed
389 c == 0x0c || // Form Feed
390 c == 0x0d || // Carriage Return
391 c == 0x85 || // Next Line
392 c == 0x2028 || // Line Separator
393 c == 0x2029) // Paragraph separator
394 {
395 break;
396 }
397 }
398
399 // Check for CR/LF sequence, and advance over the LF if we're in the middle of one.
400 if (lineEnd < fileLen &&
401 ucharBuf[lineEnd-1] == 0x0d &&
402 ucharBuf[lineEnd] == 0x0a)
403 {
404 lineEnd++;
405 }
406 }
407
408
409 //------------------------------------------------------------------------------------------
410 //
411 // printMatch Called when a matching line has been located.
412 // Print out the line from the file with the match, after
413 // converting it back to the default code page.
414 //
415 //------------------------------------------------------------------------------------------
printMatch()416 void printMatch() {
417 char buf[2000];
418 UErrorCode status = U_ZERO_ERROR;
419
420 // If we haven't already created a converter for output, do it now.
421 if (outConverter == 0) {
422 outConverter = ucnv_open(NULL, &status);
423 if (U_FAILURE(status)) {
424 fprintf(stderr, "ugrep: Error opening default converter: \"%s\"\n",
425 u_errorName(status));
426 exit(-1);
427 }
428 };
429
430 // Convert the line to be printed back to the default 8 bit code page.
431 // If the line is too long for our buffer, just truncate it.
432 ucnv_fromUChars(outConverter,
433 buf, // destination buffer for conversion
434 sizeof(buf), // capacity of destination buffer
435 &ucharBuf[lineStart], // Input to conversion
436 lineEnd-lineStart, // number of UChars to convert
437 &status);
438 buf[sizeof(buf)-1] = 0; // Add null for use in case of too long lines.
439 // The converter null-terminates its output unless
440 // the buffer completely fills.
441
442 if (displayFileName) {
443 printf("%s:", fileName);
444 }
445 if (displayLineNum) {
446 printf("%d:", lineNum);
447 }
448 printf("%s", buf);
449 }
450
451