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
, cut_off
, pat_length
;
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
;
241 // for(from = *(((TEXT ***)args) + ARG_FROM); from && *from && success;
245 /* Initialise file search */
247 anchor
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
248 anchor
->ap_FoundBreak
= 0;
249 anchor
->ap_Flags
= 0;
250 error
= MatchFirst(*from
, anchor
);
252 /* Work out if more than one file is being searched */
254 print_names
= ((TEXT
**)args
[ARG_FROM
])[1]
255 || (anchor
->ap_Flags
& APF_ITSWILD
);
256 // print_names = (*(*(((TEXT ***)args) + ARG_FROM) + 1))
257 // || (anchor->ap_Flags & APF_ITSWILD);
259 /* Enter sub-dir if the pattern was an explicitly named dir */
261 if(!(anchor
->ap_Flags
& APF_ITSWILD
)
262 && (anchor
->ap_Info
.fib_DirEntryType
> 0))
263 anchor
->ap_Flags
|= APF_DODIR
;
265 /* Set flag to get name of starting directory */
269 /* Traverse the directory */
271 while(!error
&& success
)
275 if(anchor
->ap_Info
.fib_DirEntryType
> 0)
277 /* Enter sub-dir if the ALL switch was supplied and
278 we're not on the way out of it */
280 if(!(anchor
->ap_Flags
& APF_DIDDIR
))
282 if(!(args
[ARG_FILE
] || args
[ARG_QUIET
] ||
285 WriteChars(spaces
, MARGIN
+ INDENT
* indent
+
287 text
= (TEXT
*)&(anchor
->ap_Info
.fib_FileName
);
288 VPrintf("%s (dir)\n", (IPTR
*)&text
);
291 if(args
[ARG_ALL
] || (anchor
->ap_Flags
& APF_DODIR
))
293 anchor
->ap_Flags
|= APF_DODIR
;
304 anchor
->ap_Flags
&= ~APF_DIDDIR
;
308 /* Deal with a file */
310 if(anchor
->ap_Flags
& APF_DirChanged
)
315 if(!(cut_off
= GetDirName(anchor
, path_buffer
)))
323 found
= MatchPatternNoCase(pattern
,
324 (TEXT
*)&(anchor
->ap_Info
.fib_FileName
));
330 PrintFullName(path_buffer
, cut_off
, anchor
);
331 WriteChars((STRPTR
)control_codes
, 3);
333 else if(!args
[ARG_QUIET
] && print_names
)
335 WriteChars(spaces
, MARGIN
+ INDENT
*indent
);
336 text
= (TEXT
*)&(anchor
->ap_Info
.fib_FileName
);
337 VPrintf("%s..\n", (IPTR
*)&text
);
340 found
= FindString(anchor
, args
, pattern
, locale
,
346 if((args
[ARG_FILE
] || args
[ARG_QUIET
]) &&
349 PrintFullName(path_buffer
, cut_off
, anchor
);
353 return_code
= RETURN_OK
;
357 error
= MatchNext(anchor
);
359 if(error
&& (error
!= ERROR_NO_MORE_ENTRIES
))
367 /* Clear line for next shell prompt */
369 if(args
[ARG_QUICK
] && !args
[ARG_FILE
])
370 WriteChars((STRPTR
)control_codes
, 2);
384 SetIoErr(ERROR_NO_FREE_STORE
);
390 FreeMem(anchor
, sizeof(struct AnchorPath
));
392 FreeMem(spaces
, SPACES_SIZE
);
394 FreeMem(path_buffer
, PATH_BUF_SIZE
);
396 FreeMem(user_pattern
, pat_length
+ 5);
398 FreeMem(pattern
, pat_buf_length
);
400 /* Check and reset signals */
402 if(SetSignal(0, -1) & SIGBREAKF_CTRL_C
)
403 SetIoErr(ERROR_BREAK
);
407 if((error
= IoErr()) != 0)
409 PrintFault(error
, NULL
);
418 VOID
PrintFullName(TEXT
*buffer
, UWORD cut_off
, struct AnchorPath
*anchor
)
420 buffer
[cut_off
] = '\0';
422 if(AddPart(buffer
, (TEXT
*)&(anchor
->ap_Info
.fib_FileName
), PATH_BUF_SIZE
))
431 UWORD
GetDirName(struct AnchorPath
*anchor
, TEXT
*buffer
)
433 if(NameFromLock(anchor
->ap_Current
->an_Lock
, buffer
, PATH_BUF_SIZE
))
434 return strlen(buffer
);
440 BOOL
FindString(struct AnchorPath
*anchor
, IPTR
*args
, TEXT
*pattern
,
441 struct Locale
*locale
, UBYTE
*pi
)
443 BOOL found
= FALSE
, end_early
= FALSE
, line_matches
, at_end
;
445 TEXT
*p
, *q
, *r
, *line
, *buffer
= NULL
, ch
;
446 ULONG max_line_length
= 0, line_length
, offset
= 0, file_size
, buf_size
,
447 line_start
= 0, line_count
= 1, sigs
, lines_to_show
= 0;
448 LONG read_length
= 1;
450 /* Move into the file's directory */
452 old_lock
= CurrentDir(anchor
->ap_Current
->an_Lock
);
454 /* Open the file for reading */
456 if((file
= Open((TEXT
*)&(anchor
->ap_Info
.fib_FileName
), MODE_OLDFILE
)) != NULL
)
458 /* Get a buffer for the file */
460 file_size
= anchor
->ap_Info
.fib_Size
;
461 buf_size
= file_size
+ 1;
463 while(!buffer
&& buf_size
)
465 if(!(buffer
= AllocMem(buf_size
, MEMF_ANY
)))
469 /* Check size of buffer */
471 if((buf_size
<= file_size
) && buffer
)
473 /* Get length of longest line */
475 while(read_length
> 0)
477 read_length
= Read(file
, buffer
, buf_size
- 1);
478 q
= buffer
+ read_length
;
483 for(p
= buffer
; p
< q
; p
++)
485 if((*p
=='\n')||!read_length
)
487 line_length
= offset
+ (p
- buffer
) - line_start
;
489 if(line_length
> max_line_length
)
490 max_line_length
= line_length
;
492 line_start
= offset
+ (p
- buffer
) + 1;
496 offset
+= read_length
;
499 /* Ensure buffer is big enough for longest line */
501 if(buf_size
<= max_line_length
)
503 FreeMem(buffer
, buf_size
);
504 buf_size
= max_line_length
+ 1;
505 buffer
= AllocMem(buf_size
, MEMF_ANY
);
509 /* Test every line against the pattern */
511 if(buffer
&& pattern
)
514 read_length
= Seek(file
, 0, OFFSET_BEGINNING
) + 1;
516 while(((read_length
= Read(file
, buffer
, buf_size
- 1)) > 0) &&
519 q
= buffer
+ read_length
;
520 at_end
= Seek(file
, 0, OFFSET_CURRENT
) == file_size
;
527 for(p
= buffer
; (p
< q
) && !end_early
; p
++)
531 if((ch
== '\n') || (ch
== '\0'))
537 if (args
[ARG_PATTERN
])
538 line_matches
= MatchPattern(pattern
, line
);
540 line_matches
= MatchString(pattern
, line
, p
, pi
, locale
);
544 if(args
[ARG_PATTERN
])
545 line_matches
= MatchPatternNoCase(pattern
, line
);
547 line_matches
= MatchStringNoCase(pattern
, line
, p
, pi
, locale
);
551 if(!found
&& args
[ARG_QUICK
])
563 VPrintf("%6lu ", (IPTR
*)&line_count
);
565 /* Replace invisible characters with dots */
567 for(r
= line
; r
< p
; r
++)
569 if(!IsPrint(locale
, *r
))
573 VPrintf("%s\n", (IPTR
*)&line
);
577 *((ULONG
*) args
[ARG_LINES
]);
583 if (lines_to_show
!= 0)
585 Printf("%6lu: ", line_count
);
587 /* Replace invisible characters with dots */
589 for (r
= line
; r
< p
; r
++)
591 if (!IsPrint(locale
, *r
))
604 sigs
= SetSignal(0, SIGBREAKF_CTRL_D
);
606 if(sigs
& (SIGBREAKF_CTRL_C
| SIGBREAKF_CTRL_D
))
610 if(sigs
& SIGBREAKF_CTRL_D
)
618 /* Start reading again at start of most recent line */
621 Seek(file
, line
- q
, OFFSET_CURRENT
);
626 FreeMem(buffer
, buf_size
);
631 CurrentDir(old_lock
);
637 BOOL
MatchStringNoCase(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
638 struct Locale
*locale
)
644 while(text
< text_end
)
646 ch
= ConvToUpper(locale
, *(text
++));
648 while((s
!= string
) && (*s
!= ch
))
649 s
= string
+ pi
[s
- string
- 1];
661 BOOL
MatchString(TEXT
*string
, TEXT
*text
, TEXT
*text_end
, UBYTE
*pi
,
662 struct Locale
*locale
)
668 while (text
< text_end
)
672 while ((s
!= string
) && (*s
!= ch
))
673 s
= string
+ pi
[s
- string
- 1];