2021-02-07 23:37:12 +01:00
#!/usr/bin/env perl
2012-01-16 16:34:32 +01:00
2021-07-11 00:00:22 +02:00
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
2017-03-05 22:33:31 +01:00
2012-01-16 16:34:32 +01:00
use warnings ;
use strict ;
my $ debug = 0 ;
# for paragraphs
my $ USER_SPECIFIED = 1 ;
my $ RESULTS_SPECIFIED = 2 ;
my $ search = join ' ' , @ ARGV ;
2019-05-28 18:19:42 +02:00
if ( not length $ search ) {
2020-02-15 23:38:32 +01:00
print
2022-04-04 19:42:42 +02:00
"Usage: c11std [-list] [-n#] [-section <section>] [search text] [-text <regex>] -- `section` must be in the form of `X.Y[pZ]` where `X` and `Y` are section/chapter and, optionally, `pZ` is paragraph. If both `section` and `search text` are specified, then the search space will be within the specified section. Use `-n <n>` to skip to the nth match. To list only the section numbers containing 'search text', add -list. To display specific text, use `-text <regex>`.\n" ;
2020-02-15 23:38:32 +01:00
exit 0 ;
2012-01-16 16:34:32 +01:00
}
2022-04-04 19:42:42 +02:00
my ( $ section , $ paragraph , $ section_specified , $ paragraph_specified , $ match , $ list_only , $ list_titles , $ match_text ) ;
2012-01-16 16:34:32 +01:00
2020-02-15 23:38:32 +01:00
$ section_specified = 0 ;
2012-01-16 16:34:32 +01:00
$ paragraph_specified = 0 ;
2019-05-28 18:19:42 +02:00
if ( $ search =~ s/-section\s*([A-Z0-9\.p]+)//i or $ search =~ s/\b([A-Z0-9]+\.[0-9\.p]+)//i ) {
2020-02-15 23:38:32 +01:00
$ section = $ 1 ;
2012-01-16 16:34:32 +01:00
2020-02-15 23:38:32 +01:00
if ( $ section =~ s/p(\d+)//i ) {
$ paragraph = $ 1 ;
$ paragraph_specified = $ USER_SPECIFIED ;
} else {
$ paragraph = 1 ;
}
2012-01-16 16:34:32 +01:00
2020-02-15 23:38:32 +01:00
$ section = "$section." if $ section =~ m/^[A-Z0-9]+$/i ;
2012-01-16 16:34:32 +01:00
2020-02-15 23:38:32 +01:00
$ section_specified = 1 ;
2012-01-16 16:34:32 +01:00
}
2022-04-04 19:42:42 +02:00
if ( $ search =~ s/-n\s*(\d+)// ) {
$ match = $ 1 ;
} else {
$ match = 1 ;
}
2012-01-16 16:34:32 +01:00
2019-05-28 18:19:42 +02:00
if ( $ search =~ s/-list//i ) {
2020-02-15 23:38:32 +01:00
$ list_only = 1 ;
$ list_titles = 1 ; # Added here instead of removing -titles option
2012-01-16 16:34:32 +01:00
}
2019-05-28 18:19:42 +02:00
if ( $ search =~ s/-titles//i ) {
2020-02-15 23:38:32 +01:00
$ list_only = 1 ;
$ list_titles = 1 ;
2012-01-16 16:34:32 +01:00
}
2022-04-04 19:42:42 +02:00
if ( $ search =~ s/-text ([^ ]+)// ) {
$ match_text = $ 1 ;
}
2012-01-16 16:34:32 +01:00
$ search =~ s/^\s+// ;
$ search =~ s/\s+$// ;
2019-05-28 18:19:42 +02:00
if ( not defined $ section ) {
2020-02-15 23:38:32 +01:00
$ section = "1." ;
$ paragraph = 1 ;
2012-01-16 16:34:32 +01:00
}
2019-05-28 18:19:42 +02:00
if ( $ list_only and not length $ search ) {
2020-02-15 23:38:32 +01:00
print "You must specify some search text to use with -list.\n" ;
exit 0 ;
2012-01-16 16:34:32 +01:00
}
2012-01-23 23:24:51 +01:00
open FH , "<n1570.out" or die "Could not open n1570: $!" ;
2012-01-16 16:34:32 +01:00
my @ contents = <FH> ;
close FH ;
my $ text = join '' , @ contents ;
$ text =~ s/\r//g ;
my $ result ;
2020-02-15 23:38:32 +01:00
my $ found_section = "" ;
2012-01-16 16:34:32 +01:00
my $ found_section_title = "" ;
my $ section_title ;
my $ found_paragraph ;
2020-02-15 23:38:32 +01:00
my $ found = 0 ;
2012-01-16 16:34:32 +01:00
my $ matches = 0 ;
my $ this_section ;
my $ comma = "" ;
2020-02-15 23:38:32 +01:00
if ( $ list_only ) { $ result = "Sections containing '$search':\n " ; }
2012-01-16 16:34:32 +01:00
2014-12-30 08:24:54 +01:00
my $ qsearch = quotemeta $ search ;
$ qsearch =~ s/\\ / /g ;
$ qsearch =~ s/\s+/\\s+/g ;
2012-01-16 16:34:32 +01:00
2019-05-28 18:19:42 +02:00
while ( $ text =~ m/^\s{0,4}([0-9A-Z]+\.[0-9\.]*)/msg ) {
2020-02-15 23:38:32 +01:00
$ this_section = $ 1 ;
print "----------------------------------\n" if $ debug >= 2 ;
print "Processing section [$this_section]\n" if $ debug ;
if ( $ section_specified and $ this_section !~ m/^$section/i ) {
print "No section match, skipping.\n" if $ debug >= 4 ;
next ;
}
my $ section_text ;
if ( $ text =~ m/(.*?)^(?=\s{0,4}(?!FOOTNOTE)[0-9A-Z]+\.)/msg ) { $ section_text = $ 1 ; }
else {
print "No section text, end of file marker found.\n" if $ debug >= 4 ;
last ;
2012-01-16 16:34:32 +01:00
}
2020-02-15 23:38:32 +01:00
if ( $ section =~ /FOOTNOTE/i ) {
$ section_text =~ s/^\s{4}//ms ;
$ section_text =~ s/^\s{4}FOOTNOTE.*//msi ;
$ section_text =~ s/^\d.*//ms ;
} elsif ( $ section_text =~ m/(.*?)$/msg ) {
$ section_title = $ 1 if length $ 1 ;
$ section_title =~ s/^\s+// ;
$ section_title =~ s/\s+$// ;
}
print "$this_section [$section_title]\n" if $ debug >= 2 ;
while ( $ section_text =~ m/^(\d+)\s(.*?)^(?=\d)/msgic or $ section_text =~ m/^(\d+)\s(.*)/msgi ) {
my $ p = $ 1 ;
my $ t = $ 2 ;
print "paragraph $p: [$t]\n" if $ debug >= 3 ;
if ( $ paragraph_specified == $ USER_SPECIFIED and not length $ search and $ p == $ paragraph ) {
$ result = $ t if not $ found ;
$ found_paragraph = $ p ;
$ found_section = $ this_section ;
$ found_section_title = $ section_title ;
$ found = 1 ;
last ;
}
if ( length $ search ) {
eval {
if ( $ t =~ m/\b$qsearch\b/mis or $ section_title =~ m/\b$qsearch\b/mis ) {
$ matches + + ;
if ( $ matches >= $ match ) {
if ( $ list_only ) {
$ result . = sprintf ( "%s%-15s" , $ comma , $ this_section . "p" . $ p ) ;
$ result . = " $section_title" if $ list_titles ;
$ comma = ",\n " ;
} else {
if ( not $ found ) {
$ result = $ t ;
$ found_section = $ this_section ;
$ found_section_title = $ section_title ;
$ found_paragraph = $ p ;
$ paragraph_specified = $ RESULTS_SPECIFIED ;
}
$ found = 1 ;
}
}
}
} ;
if ( $@ ) {
print "Error in search regex; you may need to escape characters such as *, ?, ., etc.\n" ;
exit 0 ;
2012-01-16 16:34:32 +01:00
}
}
2020-02-15 23:38:32 +01:00
}
last if $ found && $ paragraph_specified == $ USER_SPECIFIED ;
2012-01-16 16:34:32 +01:00
2020-02-15 23:38:32 +01:00
if ( $ paragraph_specified == $ USER_SPECIFIED ) {
if ( length $ search ) { print "No such text '$search' in paragraph $paragraph of section $section of n1570.\n" ; }
else { print "No such paragraph $paragraph in section $section of n1570.\n" ; }
2012-01-16 16:34:32 +01:00
exit 0 ;
}
2020-02-15 23:38:32 +01:00
if ( defined $ section_specified and not length $ search ) {
$ found = 1 ;
$ found_section = $ this_section ;
$ found_section_title = $ section_title ;
$ found_paragraph = $ paragraph ;
$ result = $ section_text ;
last ;
2014-02-22 03:42:25 +01:00
}
2012-01-16 16:34:32 +01:00
}
2019-05-28 18:19:42 +02:00
if ( not $ found and $ comma eq "" ) {
2020-02-15 23:38:32 +01:00
$ search =~ s/\\s\+/ /g ;
if ( $ section_specified ) {
print "No such text '$search' found within section '$section' in C11 Draft Standard (n1570).\n" if length $ search ;
print "No such section '$section' in C11 Draft Standard (n1570).\n" if not length $ search ;
exit 0 ;
}
2012-01-16 16:34:32 +01:00
2020-02-15 23:38:32 +01:00
print "No such section '$section' in C11 Draft Standard (n1570).\n" if not length $ search ;
print "No such text '$search' found in C11 Draft Standard (n1570).\n" if length $ search ;
exit 0 ;
2012-01-16 16:34:32 +01:00
}
$ result =~ s/$found_section_title// if length $ found_section_title ;
$ result =~ s/^\s+// ;
$ result =~ s/\s+$// ;
2020-02-15 23:38:32 +01:00
2012-01-16 16:34:32 +01:00
= cut
$ result =~ s/\s+/ /g ;
$ result =~ s/[\n\r]/ /g ;
= cut
2020-02-15 23:38:32 +01:00
if ( $ matches > 1 and not $ list_only ) { print "Displaying $match of $matches matches: " ; }
2012-01-16 16:34:32 +01:00
2019-05-28 18:19:42 +02:00
if ( $ comma eq "" ) {
2020-02-15 23:38:32 +01:00
2012-01-16 16:34:32 +01:00
= cut
print $ found_section ;
print "p" . $ found_paragraph if $ paragraph_specified ;
= cut
2020-02-15 23:38:32 +01:00
2022-04-04 19:42:42 +02:00
print "http://www.iso-9899.info/n1570.html\#$found_section" ;
print "p" . $ found_paragraph if $ paragraph_specified ;
print "\n\n" ;
print "[" , $ found_section_title , "]\n\n" if length $ found_section_title ;
2012-01-16 16:34:32 +01:00
}
2012-11-02 23:08:20 +01:00
$ result =~ s/\s*Constraints\s*$// ;
$ result =~ s/\s*Semantics\s*$// ;
$ result =~ s/\s*Description\s*$// ;
$ result =~ s/\s*Returns\s*$// ;
$ result =~ s/\s*Runtime-constraints\s*$// ;
$ result =~ s/\s*Recommended practice\s*$// ;
2022-04-04 19:42:42 +02:00
if ( length $ match_text ) {
my $ match_result = $ result ;
$ match_result =~ s/\s+/ /g ;
my $ match = eval {
2022-07-04 18:52:50 +02:00
my @ matches = ( $ match_result =~ m/($match_text)/ms p ) ;
if ( @ matches > 1 ) {
shift @ matches ;
@ matches = grep { length $ _ } @ matches ;
}
return [ $ { ^ PREMATCH } , join ( ' ... ' , @ matches ) , $ { ^ POSTMATCH } ] ;
2022-04-04 19:42:42 +02:00
} ;
if ( $@ ) {
print "Error in -text option: $@\n" ;
exit 1 ;
}
$ result = '' ;
if ( length $ match - > [ 0 ] ) {
$ result = '... ' ;
}
if ( length $ match - > [ 1 ] ) {
$ result . = $ match - > [ 1 ] ;
} else {
$ result = "No text found for `$match_text`." ;
}
if ( length $ match - > [ 2 ] ) {
$ result . = ' ...' ;
}
}
2012-01-16 16:34:32 +01:00
print "$result\n" ;