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 text
= (TEXT
*)&(anchor
->ap_Info
.fib_FileName
);
284 VPrintf("%s (dir)\n", (IPTR
*)&text
);
287 if(args
[ARG_ALL
] || (anchor
->ap_Flags
& APF_DODIR
))
289 anchor
->ap_Flags
|= APF_DODIR
;
300 anchor
->ap_Flags
&= ~APF_DIDDIR
;
304 /* Deal with a file */
306 if(anchor
->ap_Flags
& APF_DirChanged
)
311 if(!(cut_off
= GetDirName(anchor
, path_buffer
)))
319 found
= MatchPatternNoCase(pattern
,
320 (TEXT
*)&(anchor
->ap_Info
.fib_FileName
));
326 PrintFullName(path_buffer
, cut_off
, anchor
);
327 WriteChars((STRPTR
)control_codes
, 3);
329 else if(!args
[ARG_QUIET
] && print_names
)
331 WriteChars(spaces
, MARGIN
+ INDENT
*indent
);
332 text
= (TEXT
*)&(anchor
->ap_Info
.fib_FileName
);
333 VPrintf("%s..\n", (IPTR
*)&text
);
336 found
= FindString(anchor
, args
, pattern
, locale
,
342 if((args
[ARG_FILE
] || args
[ARG_QUIET
]) &&
345 PrintFullName(path_buffer
, cut_off
, anchor
);
349 return_code
= RETURN_OK
;
353 error
= MatchNext(anchor
);
355 if(error
&& (error
!= ERROR_NO_MORE_ENTRIES
))
363 /* Clear line for next shell prompt */
365 if(args
[ARG_QUICK
] && !args
[ARG_FILE
])
366 WriteChars((STRPTR
)control_codes
, 2);
380 SetIoErr(ERROR_NO_FREE_STORE
);
386 FreeMem(anchor
, sizeof(struct AnchorPath
));
388 FreeMem(spaces
, SPACES_SIZE
);
390 FreeMem(path_buffer
, PATH_BUF_SIZE
);
392 FreeMem(user_pattern
, pat_length
+ 5);
394 FreeMem(pattern
, pat_buf_length
);
396 /* Check and reset signals */
398 if(SetSignal(0, -1) & SIGBREAKF_CTRL_C
)
399 SetIoErr(ERROR_BREAK
);
403 if((error
= IoErr()) != 0)
405 PrintFault(error
, NULL
);
414 VOID
PrintFullName(TEXT
*buffer
, UWORD cut_off
, struct AnchorPath
*anchor
)
416 buffer
[cut_off
] = '\0';
418 if(AddPart(buffer
, (TEXT
*)&(anchor
->ap_Info
.fib_FileName
), PATH_BUF_SIZE
))
427 UWORD
GetDirName(struct AnchorPath
*anchor
, TEXT
*buffer
)
429 if(NameFromLock(anchor
->ap_Current
->an_Lock
, buffer
, PATH_BUF_SIZE
))
430 return strlen(buffer
);
436 BOOL
FindString(struct AnchorPath
*anchor
, IPTR
*args
, TEXT
*pattern
,
437 struct Locale
*locale
, UBYTE
*pi
)
439 BOOL found
= FALSE
, end_early
= FALSE
, line_matches
, at_end
;
441 TEXT
*p
, *q
, *r
, *line
, *buffer
= NULL
, ch
;
442 ULONG max_line_length
= 0, line_length
, offset
= 0, file_size
, buf_size
,
443 line_start
= 0, line_count
= 1, sigs
, lines_to_show
= 0;
444 LONG read_length
= 1;
446 /* Move into the file's directory */
448 old_lock
= CurrentDir(anchor
->ap_Current
->an_Lock
);
450 /* Open the file for reading */
452 if((file
= Open(anchor
->ap_Info
.fib_FileName
, MODE_OLDFILE
)) != BNULL
)
454 /* Get a buffer for the file */
456 file_size
= anchor
->ap_Info
.fib_Size
;
457 buf_size
= file_size
+ 1;
459 while(!buffer
&& buf_size
)
461 if(!(buffer
= AllocMem(buf_size
, MEMF_ANY
)))
465 /* Check size of buffer */
467 if((buf_size
<= file_size
) && buffer
)
469 /* Get length of longest line */
471 while(read_length
> 0)
473 read_length
= Read(file
, buffer
, buf_size
- 1);
474 q
= buffer
+ read_length
;
479 for(p
= buffer
; p
< q
; p
++)
481 if((*p
=='\n')||!read_length
)
483 line_length
= offset
+ (p
- buffer
) - line_start
;
485 if(line_length
> max_line_length
)
486 max_line_length
= line_length
;
488 line_start
= offset
+ (p
- buffer
) + 1;
492 offset
+= read_length
;
495 /* Ensure buffer is big enough for longest line */
497 if(buf_size
<= max_line_length
)
499 FreeMem(buffer
, buf_size
);
500 buf_size
= max_line_length
+ 1;
501 buffer
= AllocMem(buf_size
, MEMF_ANY
);
505 /* Test every line against the pattern */
507 if(buffer
&& pattern
)
510 read_length
= Seek(file
, 0, OFFSET_BEGINNING
) + 1;
512 while(((read_length
= Read(file
, buffer
, buf_size
- 1)) > 0) &&
515 q
= buffer
+ read_length
;
516 at_end
= Seek(file
, 0, OFFSET_CURRENT
) == file_size
;
523 for(p
= buffer
; (p
< q
) && !end_early
; p
++)
527 if((ch
== '\n') || (ch
== '\0'))
533 if (args
[ARG_PATTERN
])
534 line_matches
= MatchPattern(pattern
, line
);
536 line_matches
= MatchString(pattern
, line
, p
, pi
, locale
);
540 if(args
[ARG_PATTERN
])
541 line_matches
= MatchPatternNoCase(pattern
, line
);
543 line_matches
= MatchStringNoCase(pattern
, line
, p
, pi
, locale
);
547 if(!found
&& args
[ARG_QUICK
])
559 VPrintf("%6lu ", (IPTR
*)&line_count
);
561 /* Replace invisible characters with dots */
563 for(r
= line
; r
< p
; r
++)
565 if(!IsPrint(locale
, *r
))
569 VPrintf("%s\n", (IPTR
*)&line
);
573 *((ULONG
*) args
[ARG_LINES
]);
579 if (lines_to_show
!= 0)
581 Printf("%6lu: ", line_count
);
583 /* Replace invisible characters with dots */
585 for (r
= line
; r
< p
; r
++)
587 if (!IsPrint(locale
, *r
))
600 sigs
= SetSignal(0, SIGBREAKF_CTRL_D
);
602 if(sigs
& (SIGBREAKF_CTRL_C
| SIGBREAKF_CTRL_D
))
606 if(sigs
& SIGBREAKF_CTRL_D
)
614 /* Start reading again at start of most recent line */
617 Seek(file
, line
- q
, OFFSET_CURRENT
);
622 FreeMem(buffer
, buf_size
);
627 CurrentDir(old_lock
);
633 BOOL
MatchStringNoCase(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
634 struct Locale
*locale
)
640 while(text
< text_end
)
642 ch
= ConvToUpper(locale
, *(text
++));
644 while((s
!= string
) && (*s
!= ch
))
645 s
= string
+ pi
[s
- string
- 1];
657 BOOL
MatchString(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
658 struct Locale
*locale
)
664 while (text
< text_end
)
668 while ((s
!= string
) && (*s
!= ch
))
669 s
= string
+ pi
[s
- string
- 1];