2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
6 /*****************************************************************************
14 Search [FROM] {(name | pattern} [SEARCH] (string | pattern) [ALL]
15 [NONUM] [QUIET] [QUICK] [FILE] [PATTERN] [LINES=Number]
23 Search looks through the files contained in the FROM directory for
24 a specified string (SEARCH); in case the ALL switch is specified,
25 the subdirectories of the FROM directory are also searched. The name
26 of all files containing the SEARCH string is displayed together with
27 the numbers of the lines where the string occurred.
28 If CTRL-C is pressed, the search will be abandoned. CTRL-D will
29 abandon searching the current file.
33 NONUM -- no line numbers are printed
34 QUIET -- don't display the name of the file being searched
35 QUICK -- more compact output
36 FILE -- look for a file with a specific name rather than a string
38 PATTERN -- use pattern matching when searching
39 CASE -- use case sensitive pattern matching when searching
40 LINES -- extra lines after a line match which should be shown
44 If the object is found, the condition flag is set to 0. Otherwise it's
59 Author: Neil Cafferkey
60 Placed in the public domain by Neil Cafferkey.
61 Changes by: Johan 'S.Duvan' Alfredsson
63 ******************************************************************************/
65 #include <proto/exec.h>
66 #include <proto/dos.h>
67 #include <proto/locale.h>
68 #include <exec/memory.h>
70 #include <libraries/locale.h>
74 // ***** Layout and version parameters ***********
76 #define LOCALE_VERSION 38
77 #define PATH_BUF_SIZE 512
78 #define SPACES_SIZE (160 + 1)
84 // ***** Command line arguments *******************
101 int LocaleBase_version
= LOCALE_VERSION
;
103 // ***** Prototypes for internal functions *******
105 VOID
PrintFullName(TEXT
*buffer
, UWORD cut_off
, struct AnchorPath
*anchor
);
106 UWORD
GetDirName(struct AnchorPath
*anchor
, TEXT
*buffer
);
107 BOOL
FindString(struct AnchorPath
*anchor
, IPTR
*args
, TEXT
*pattern
,
108 struct Locale
*locale
, UBYTE
*pi
);
109 BOOL
MatchStringNoCase(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
110 struct Locale
*locale
);
111 BOOL
MatchString(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
112 struct Locale
*locale
);
115 // ***** String information (version, messages) ***
117 const TEXT
template[] =
118 "FROM/M,SEARCH/A,ALL/S,NONUM/S,QUIET/S,QUICK/S,FILE/S,PATTERN/S,CASE/S,LINES/N";
119 const TEXT version_string
[] = "$VER: Search 42.4 (6.4.2008)";
120 const TEXT locale_name
[] = "locale.library";
122 const TEXT control_codes
[] = { 0x9b, 'K', 13 };
123 const TEXT wild_card
[] = { '#', '?'};
124 const TEXT new_line
[] = "\n";
125 const TEXT abandon_msg
[] = "** File abandoned\n";
127 const STRPTR default_from
[] = {"", 0};
133 IPTR args
[ARG_COUNT
] = {0};
134 struct RDArgs
*read_args
;
135 struct AnchorPath
*anchor
;
137 LONG return_code
= RETURN_WARN
;
138 TEXT
*text
, *spaces
, *pattern
= NULL
, *path_buffer
,
139 *user_pattern
= NULL
, *p
, ch
, **from
;
140 BOOL found
, success
= TRUE
, new_dir
, print_names
;
141 UWORD indent
= 0, pat_buf_length
= 0, cut_off
= 0, pat_length
= 0;
143 struct Locale
*locale
;
145 /* Allocate buffers */
147 spaces
= AllocMem(SPACES_SIZE
, MEMF_CLEAR
);
148 anchor
= AllocMem(sizeof(struct AnchorPath
), MEMF_CLEAR
);
149 path_buffer
= AllocMem(PATH_BUF_SIZE
,MEMF_ANY
);
151 if(anchor
&& spaces
&& path_buffer
)
153 locale
= OpenLocale(NULL
);
155 for(text
= spaces
+ SPACES_SIZE
- 1; text
> spaces
; *(--text
) = ' ');
157 /* Parse arguments */
159 read_args
= ReadArgs((STRPTR
)template, args
, NULL
);
161 if(locale
&& read_args
)
163 if ( ! args
[ARG_FROM
] )
165 /* /M ignores the default value, so we must set it after
167 args
[ARG_FROM
] = (IPTR
)default_from
;
170 /* Prepare the pattern to be matched */
172 pat_length
= strlen((TEXT
*)args
[ARG_SEARCH
]);
173 pat_buf_length
= pat_length
* 2 + 3;
174 user_pattern
= AllocMem(pat_length
+ 5, MEMF_CLEAR
);
175 pattern
= AllocMem(pat_buf_length
, MEMF_ANY
);
177 if(user_pattern
&& pattern
)
179 if(args
[ARG_PATTERN
] || args
[ARG_FILE
])
185 text
= user_pattern
+ 2;
186 CopyMem(wild_card
, user_pattern
, 2);
187 CopyMem(wild_card
, text
+ pat_length
, 2);
190 CopyMem((TEXT
*)args
[ARG_SEARCH
], text
, pat_length
);
193 if (ParsePattern(user_pattern
, pattern
, pat_buf_length
) < 0)
200 if(ParsePatternNoCase(user_pattern
, pattern
,
207 /* Copy the search string and convert it to uppercase */
211 for(p
= (TEXT
*)args
[ARG_SEARCH
]; (ch
= *p
) != '\0'; p
++)
212 *(text
++) = ConvToUpper(locale
, ch
);
216 /* Construct prefix table for Knuth-Morris-Pratt
222 for(q
= 1; q
< pat_length
; q
++)
224 while(k
&& (pattern
[k
] != pattern
[q
]))
225 k
= user_pattern
[k
- 1];
227 if(pattern
[k
] == pattern
[q
])
237 /* Get the next starting point */
239 for(from
= (TEXT
**)args
[ARG_FROM
]; from
&& *from
&& success
;
243 /* Initialise file search */
245 anchor
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
246 anchor
->ap_FoundBreak
= 0;
247 anchor
->ap_Flags
= 0;
248 error
= MatchFirst(*from
, anchor
);
250 /* Work out if more than one file is being searched */
252 print_names
= ((TEXT
**)args
[ARG_FROM
])[1]
253 || (anchor
->ap_Flags
& APF_ITSWILD
);
255 /* Enter sub-dir if the pattern was an explicitly named dir */
257 if(!(anchor
->ap_Flags
& APF_ITSWILD
)
258 && (anchor
->ap_Info
.fib_DirEntryType
> 0))
259 anchor
->ap_Flags
|= APF_DODIR
;
261 /* Set flag to get name of starting directory */
265 /* Traverse the directory */
267 while(!error
&& success
)
271 if(anchor
->ap_Info
.fib_DirEntryType
> 0)
273 /* Enter sub-dir if the ALL switch was supplied and
274 we're not on the way out of it */
276 if(!(anchor
->ap_Flags
& APF_DIDDIR
))
278 if(!(args
[ARG_FILE
] || args
[ARG_QUIET
] ||
281 WriteChars(spaces
, MARGIN
+ INDENT
* indent
+
283 Printf("%s (dir)\n", &anchor
->ap_Info
.fib_FileName
[0]);
286 if(args
[ARG_ALL
] || (anchor
->ap_Flags
& APF_DODIR
))
288 anchor
->ap_Flags
|= APF_DODIR
;
299 anchor
->ap_Flags
&= ~APF_DIDDIR
;
303 /* Deal with a file */
305 if(anchor
->ap_Flags
& APF_DirChanged
)
310 if(!(cut_off
= GetDirName(anchor
, path_buffer
)))
318 found
= MatchPatternNoCase(pattern
,
319 (TEXT
*)&(anchor
->ap_Info
.fib_FileName
));
325 PrintFullName(path_buffer
, cut_off
, anchor
);
326 WriteChars((STRPTR
)control_codes
, 3);
328 else if(!args
[ARG_QUIET
] && print_names
)
330 WriteChars(spaces
, MARGIN
+ INDENT
*indent
);
331 Printf("%s..\n", &anchor
->ap_Info
.fib_FileName
[0]);
334 found
= FindString(anchor
, args
, pattern
, locale
,
340 if((args
[ARG_FILE
] || args
[ARG_QUIET
]) &&
343 PrintFullName(path_buffer
, cut_off
, anchor
);
347 return_code
= RETURN_OK
;
351 error
= MatchNext(anchor
);
353 if(error
&& (error
!= ERROR_NO_MORE_ENTRIES
))
361 /* Clear line for next shell prompt */
363 if(args
[ARG_QUICK
] && !args
[ARG_FILE
])
364 WriteChars((STRPTR
)control_codes
, 2);
378 SetIoErr(ERROR_NO_FREE_STORE
);
384 FreeMem(anchor
, sizeof(struct AnchorPath
));
386 FreeMem(spaces
, SPACES_SIZE
);
388 FreeMem(path_buffer
, PATH_BUF_SIZE
);
390 FreeMem(user_pattern
, pat_length
+ 5);
392 FreeMem(pattern
, pat_buf_length
);
394 /* Check and reset signals */
396 if(SetSignal(0, -1) & SIGBREAKF_CTRL_C
)
397 SetIoErr(ERROR_BREAK
);
401 if((error
= IoErr()) != 0)
403 PrintFault(error
, NULL
);
412 VOID
PrintFullName(TEXT
*buffer
, UWORD cut_off
, struct AnchorPath
*anchor
)
414 buffer
[cut_off
] = '\0';
416 if(AddPart(buffer
, (TEXT
*)&(anchor
->ap_Info
.fib_FileName
), PATH_BUF_SIZE
))
425 UWORD
GetDirName(struct AnchorPath
*anchor
, TEXT
*buffer
)
427 if(NameFromLock(anchor
->ap_Current
->an_Lock
, buffer
, PATH_BUF_SIZE
))
428 return strlen(buffer
);
434 BOOL
FindString(struct AnchorPath
*anchor
, IPTR
*args
, TEXT
*pattern
,
435 struct Locale
*locale
, UBYTE
*pi
)
437 BOOL found
= FALSE
, end_early
= FALSE
, line_matches
, at_end
;
439 TEXT
*p
, *q
, *r
, *line
, *buffer
= NULL
, ch
;
440 ULONG max_line_length
= 0, line_length
, offset
= 0, file_size
, buf_size
,
441 line_start
= 0, line_count
= 1, sigs
, lines_to_show
= 0;
442 LONG read_length
= 1;
444 /* Move into the file's directory */
446 old_lock
= CurrentDir(anchor
->ap_Current
->an_Lock
);
448 /* Open the file for reading */
450 if((file
= Open(anchor
->ap_Info
.fib_FileName
, MODE_OLDFILE
)) != BNULL
)
452 /* Get a buffer for the file */
454 file_size
= anchor
->ap_Info
.fib_Size
;
455 buf_size
= file_size
+ 1;
457 while(!buffer
&& buf_size
)
459 if(!(buffer
= AllocMem(buf_size
, MEMF_ANY
)))
463 /* Check size of buffer */
465 if((buf_size
<= file_size
) && buffer
)
467 /* Get length of longest line */
469 while(read_length
> 0)
471 read_length
= Read(file
, buffer
, buf_size
- 1);
472 q
= buffer
+ read_length
;
477 for(p
= buffer
; p
< q
; p
++)
479 if((*p
=='\n')||!read_length
)
481 line_length
= offset
+ (p
- buffer
) - line_start
;
483 if(line_length
> max_line_length
)
484 max_line_length
= line_length
;
486 line_start
= offset
+ (p
- buffer
) + 1;
490 offset
+= read_length
;
493 /* Ensure buffer is big enough for longest line */
495 if(buf_size
<= max_line_length
)
497 FreeMem(buffer
, buf_size
);
498 buf_size
= max_line_length
+ 1;
499 buffer
= AllocMem(buf_size
, MEMF_ANY
);
503 /* Test every line against the pattern */
505 if(buffer
&& pattern
)
508 read_length
= Seek(file
, 0, OFFSET_BEGINNING
) + 1;
510 while(((read_length
= Read(file
, buffer
, buf_size
- 1)) > 0) &&
513 q
= buffer
+ read_length
;
514 at_end
= Seek(file
, 0, OFFSET_CURRENT
) == file_size
;
521 for(p
= buffer
; (p
< q
) && !end_early
; p
++)
525 if((ch
== '\n') || (ch
== '\0'))
531 if (args
[ARG_PATTERN
])
532 line_matches
= MatchPattern(pattern
, line
);
534 line_matches
= MatchString(pattern
, line
, p
, pi
, locale
);
538 if(args
[ARG_PATTERN
])
539 line_matches
= MatchPatternNoCase(pattern
, line
);
541 line_matches
= MatchStringNoCase(pattern
, line
, p
, pi
, locale
);
545 if(!found
&& args
[ARG_QUICK
])
557 Printf("%6lu ", line_count
);
559 /* Replace invisible characters with dots */
561 for(r
= line
; r
< p
; r
++)
563 if(!IsPrint(locale
, *r
))
567 Printf("%s\n", line
);
571 *((ULONG
*) args
[ARG_LINES
]);
577 if (lines_to_show
!= 0)
579 Printf("%6lu: ", line_count
);
581 /* Replace invisible characters with dots */
583 for (r
= line
; r
< p
; r
++)
585 if (!IsPrint(locale
, *r
))
598 sigs
= SetSignal(0, SIGBREAKF_CTRL_D
);
600 if(sigs
& (SIGBREAKF_CTRL_C
| SIGBREAKF_CTRL_D
))
604 if(sigs
& SIGBREAKF_CTRL_D
)
612 /* Start reading again at start of most recent line */
615 Seek(file
, line
- q
, OFFSET_CURRENT
);
620 FreeMem(buffer
, buf_size
);
625 CurrentDir(old_lock
);
631 BOOL
MatchStringNoCase(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
632 struct Locale
*locale
)
638 while(text
< text_end
)
640 ch
= ConvToUpper(locale
, *(text
++));
642 while((s
!= string
) && (*s
!= ch
))
643 s
= string
+ pi
[s
- string
- 1];
655 BOOL
MatchString(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
656 struct Locale
*locale
)
662 while (text
< text_end
)
666 while ((s
!= string
) && (*s
!= ch
))
667 s
= string
+ pi
[s
- string
- 1];