Skip to contentSkip to conversation

bin/stdc: Improve output formatting

PATCH In review Oskari Pirhonen 2 participants 4 messages Apr 14, 2023 – Apr 17, 2023 Man-pages: manual pages for Linux -- Sections 2, 3, 4, 5, and 7

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 bin/stdc: Improve output formatting

Remove leading whitespace and collapse multi-line declarations into a single line using (g)awk.

---

Here's a snippet for comparison. I've manually removed some of the formatting repeats from C99 and C11 output for the sake of making this message shorter.

Original:

    $ ./stdc c89 '[[:alpha:]]*scanf'
             int fscanf(FILE *stream, const char *format, ...);
             int scanf(const char *format, ...);
             int sscanf(const char *s, const char *format, ...);
    $ ./stdc c99 '[[:alpha:]]*scanf'
          int fscanf(FILE * restrict stream,
               const char * restrict format, ...);
          int scanf(const char * restrict format, ...);
            int fwscanf(FILE * restrict stream,
                 const wchar_t * restrict format, ...);
            int wscanf(const wchar_t * restrict format, ...);
    $ ./stdc c11 '[[:alpha:]]*scanf'
          int fscanf(FILE * restrict stream,
               const char * restrict format, ...);
          int scanf(const char * restrict format, ...);
            int vsscanf(const char * restrict s,
                 const char * restrict format, va_list arg);

New:

    $ ./stdc c89 '[[:alpha:]]*scanf'
    int fscanf(FILE *stream, const char *format, ...);
    int scanf(const char *format, ...);
    int sscanf(const char *s, const char *format, ...);
    $ ./stdc c99 '[[:alpha:]]*scanf'
    int fscanf(FILE * restrict stream, const char * restrict format, ...);
    int scanf(const char * restrict format, ...);
    int fwscanf(FILE * restrict stream, const wchar_t * restrict format, ...);
    int wscanf(const wchar_t * restrict format, ...);
    $ ./stdc c11 '[[:alpha:]]*scanf'
    int fscanf(FILE * restrict stream, const char * restrict format, ...);
    int scanf(const char * restrict format, ...);
    int vsscanf(const char * restrict s, const char * restrict format, va_list arg);
 bin/stdc | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
bin/stdc +8 -1
index b685df8..8c07956 100755
@@ -14,7 +14,14 @@ err()
 
 grep_proto()
 {
-	pcre2grep -M "(?s)\b$1 *\([[:alnum:]*,._\s\(\)-]*\);$";
+	pcre2grep -M "(?s)\b$1 *\([[:alnum:]*,._\s\(\)-]*\);$" \
+	| awk -e 'BEGIN { RS=";\n"; ORS=RS; }
+		{
+			gsub(/\n/, " ");
+			sub(/^ +/, "");
+			gsub(/ +/, " ");
+			print;
+		}';
 }
 
 libc_summ_c89()
Signature · 1 line
2.39.2
#1 lore raw

Hi Oskari,

Quoted 2 lines · Oskari Pirhonen

Remove leading whitespace and collapse multi-line declarations into a single line using (g)awk.

I can't reak awk(1) :(

But I like the idea. I implemented the same using sed(1) after your suggestion. Does the below patch look good to you?

Cheers,
Alex

P.S.: I forgot about writing a man page. I'll start now.

commit af1ab8cf11165dba56dc54bae7310aa7824fd89b (HEAD -> main)

Author: Alejandro Colomar <[email protected]>
Date:   Fri Apr 14 13:21:27 2023 +0200

bin/stdc: Improve output formatting

Remove leading whitespace, and collapse multi-line declarations into a single line.

bin/stdc +3 -1
index b685df8..54db0d7 100755
@@ -14,7 +14,9 @@ err()
 
 grep_proto()
 {
-       pcre2grep -M "(?s)\b$1 *\([[:alnum:]*,._\s\(\)-]*\);$";
+       pcre2grep -M "(?s)\b$1 *\([[:alnum:]*,._\s\(\)-]*\);$" \
+       | sed 's/^ *//' \
+       | sed -z 's/\([^;]\)\n/\1 /g';
 }
 
 libc_summ_c89()
Quoted 71 lines

---

Here's a snippet for comparison. I've manually removed some of the formatting repeats from C99 and C11 output for the sake of making this message shorter.

Original:

    $ ./stdc c89 '[[:alpha:]]*scanf'
             int fscanf(FILE *stream, const char *format, ...);
             int scanf(const char *format, ...);
             int sscanf(const char *s, const char *format, ...);
    $ ./stdc c99 '[[:alpha:]]*scanf'
          int fscanf(FILE * restrict stream,
               const char * restrict format, ...);
          int scanf(const char * restrict format, ...);
            int fwscanf(FILE * restrict stream,
                 const wchar_t * restrict format, ...);
            int wscanf(const wchar_t * restrict format, ...);
    $ ./stdc c11 '[[:alpha:]]*scanf'
          int fscanf(FILE * restrict stream,
               const char * restrict format, ...);
          int scanf(const char * restrict format, ...);
            int vsscanf(const char * restrict s,
                 const char * restrict format, va_list arg);

New:

    $ ./stdc c89 '[[:alpha:]]*scanf'
    int fscanf(FILE *stream, const char *format, ...);
    int scanf(const char *format, ...);
    int sscanf(const char *s, const char *format, ...);
    $ ./stdc c99 '[[:alpha:]]*scanf'
    int fscanf(FILE * restrict stream, const char * restrict format, ...);
    int scanf(const char * restrict format, ...);
    int fwscanf(FILE * restrict stream, const wchar_t * restrict format, ...);
    int wscanf(const wchar_t * restrict format, ...);
    $ ./stdc c11 '[[:alpha:]]*scanf'
    int fscanf(FILE * restrict stream, const char * restrict format, ...);
    int scanf(const char * restrict format, ...);
    int vsscanf(const char * restrict s, const char * restrict format, va_list arg);
 bin/stdc | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
bin/stdc +8 -1
index b685df8..8c07956 100755
@@ -14,7 +14,14 @@ err()
 
 grep_proto()
 {
-	pcre2grep -M "(?s)\b$1 *\([[:alnum:]*,._\s\(\)-]*\);$";
+	pcre2grep -M "(?s)\b$1 *\([[:alnum:]*,._\s\(\)-]*\);$" \
+	| awk -e 'BEGIN { RS=";\n"; ORS=RS; }
+		{
+			gsub(/\n/, " ");
+			sub(/^ +/, "");
+			gsub(/ +/, " ");
+			print;
+		}';
 }
 
 libc_summ_c89()
Signature · 2 lines
<http://www.alejandro-colomar.es/>
GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5
#2 lore raw

Hi Alex,

Quoted 8 lines · Alejandro Colomar

Hi Oskari,

Quoted 2 lines · Oskari Pirhonen

Remove leading whitespace and collapse multi-line declarations into a single line using (g)awk.

I can't reak awk(1) :(

Awww man, but I even left the optional semicolons in...

Quoted 3 lines

But I like the idea. I implemented the same using sed(1) after your suggestion. Does the below patch look good to you?

I actually had an earlier version with sed(1), but it used looping/branching to handle the multi-line bits, so I figured it was a bit ugly and didn't send it. I didn't think to try -z.

It seems to do the same thing, so LGTM.

Quoted 5 lines

Cheers,
Alex

P.S.: I forgot about writing a man page. I'll start now.

I was about to say "and license file and appropriate blurb" but then I saw your commit. I've got some suggestions for the man page, so I'll send some patches sometime soon.

- Oskari

Since you said you can't read awk, then just to satisfy your curiosity, here's what was going on:

    BEGIN {
        RS = ";\n"
        ORS = RS
    }

This block is run at the start before any records are processed.

The default Record Separator is "\n", but here we set it to ";\n". In (g)awk, a value of RS that is >1 char is actually a regex, but we only need to match a literal string. The Output Record Separator is by default also "\n".

    {
        gsub(/\n/, " ")
        sub(/^ +/, "")
        gsub(/ +/, " ")
        print
    }

This block is run on all records, since it doesn't have any patterns for contitional execution attached to it.

gsub() does an in-place global regex replace on a string, similar to the s/regex/replace/g you're familiar with. It takes an optional third arg, but if it's left out then it has an implicit $0 which means the entire current record. sub() is like gsub(), but only does the first match, similar to s/regex/replace/.

print, without any args, prints the current record followed by ORS. This was set earlier because the RS is consumed from the input as it is being processed record by record, but we want to keep the output looking intact.

Hopefully not so bad after all. Awk is pretty nice IMO (and gawk in particular), and I would recommend checking it out if you find yourself borded one day :)

#3 lore raw

Hi Oskari,

Quoted 5 lines · Oskari Pirhonen

Hi Alex,

Quoted 2 lines · Alejandro Colomar

Hi Oskari,

Quoted 8 lines
Quoted 6 lines · Oskari Pirhonen
Quoted 2 lines

Remove leading whitespace and collapse multi-line declarations into a single line using (g)awk.

I can't reak awk(1) :(

Awww man, but I even left the optional semicolons in...

:-)

Quoted 10 lines
Quoted 4 lines

But I like the idea. I implemented the same using sed(1) after your suggestion. Does the below patch look good to you?

I actually had an earlier version with sed(1), but it used looping/branching to handle the multi-line bits, so I figured it was a bit ugly and didn't send it. I didn't think to try -z.

It seems to do the same thing, so LGTM.

Good.

Quoted 10 lines
Quoted 6 lines

Cheers,
Alex

P.S.: I forgot about writing a man page. I'll start now.

I was about to say "and license file and appropriate blurb" but then I saw your commit. I've got some suggestions for the man page, so I'll send some patches sometime soon.

Sure, please!

Quoted 5 lines

- Oskari

Since you said you can't read awk, then just to satisfy your curiosity,

Thanks!

Quoted 30 lines

here's what was going on:

    BEGIN {
        RS = ";\n"
        ORS = RS
    }

This block is run at the start before any records are processed.

The default Record Separator is "\n", but here we set it to ";\n". In (g)awk, a value of RS that is >1 char is actually a regex, but we only need to match a literal string. The Output Record Separator is by default also "\n".

    {
        gsub(/\n/, " ")
        sub(/^ +/, "")
        gsub(/ +/, " ")
        print
    }

This block is run on all records, since it doesn't have any patterns for contitional execution attached to it.

gsub() does an in-place global regex replace on a string, similar to the s/regex/replace/g you're familiar with. It takes an optional third arg, but if it's left out then it has an implicit $0 which means the entire current record. sub() is like gsub(), but only does the first match, similar to s/regex/replace/.

Quoted 6 lines

print, without any args, prints the current record followed by ORS. This was set earlier because the RS is consumed from the input as it is being processed record by record, but we want to keep the output looking intact.

Hopefully not so bad after all.

Meh, I'll need some time to get used to it, I guess :)

Quoted 3 lines

Awk is pretty nice IMO (and gawk in
particular), and I would recommend checking it out if you find yourself borded one day :)

So far, I've almost always found one way or another with more specific commands. The only case I find awk(1) interesting is for processing columns; it's quite good at that (e.g., for printing column sum totals). The other alternative I can think of is pee(1), and process each column separately, which might be more readable (for me). But I might give it a try some day. :)

For now, awk(1) is only for awk '{print $3}' to me.

Cheers,
Alex

Signature · 2 lines
<http://www.alejandro-colomar.es/>
GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5