On Tue, Aug 22, 2023 at 12:42:25PM +0200, Michiel van den Heuvel wrote:
>I don't suppose you have some formatter config file?
>
bah! Real Hackers (TM) write their own formatters as shell-perl hybrids with recursive regular expressions. ON PUNCH CARDS.

I'll trade you my magnetic needles so you can do it directly on disk
next time. ;)

:-)=)

Maybe just add it to the repository?

no, it feels "too off-topic".
i'm attaching it for your masochistic pleasure. there you can see that it's actually not even aimed at isync specifically, because c++/qt?! i originally wrote this some 22 years ago, when astyle was Teh Shit. i wouldn't expect it to actually produce invariably correct results, and haven't used it on isync code for ... a very long time.

>+      fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
>+      va_start( va, fmt );
>+      vfprintf( stderr, fmt, va );
>
totally on a tangent ...
i have a bit of an aversion towards tearing apart format strings. it isn't a big deal here, because we're just printing to a stream with no concurrency, but if the target was syslog or a dynamically allocated string, things would look differently. this could be avoided by printing the "payload" to a temporary, but this would obviously have a runtime cost, esp. when using nfasprintf() to be super-safe. but as we already have xvprintf_core(), this overhead could be avoided by supporting recursive format strings: xprintf("%s:%d: %@\n", file, line, fmt, va). of course this would be total overkill here ... though once you have it, all kinds of use cases pop up. just thinking aloud ...

Not fixed yet due to 1: the next point, and 2: do you want this?

depending on what you mean by "this".

if you mean the "short" tangent of using a temporary: nope, not worth it.

if you mean the "long" tangent of using xprintf: yes, but not in this patch series. for background, see the wip/better-stderr branch, which turns the personal aversion into an actual requirement. but notably, the config parser doesn't strictly _need_ that, because it runs before concurrency starts.

It would
introduce some duplication because conf_error and conf_sys_error print
to stderr and a buffer respectively. But if you see a way to avoid this,
I'd like to hear it.

once the infra is there, strerror(errno) (and thus perror()) can be handled via the %m format (stolen from syslog), so things can be unified pretty much. the branch shows the direction.

>+void
>+conf_sys_error( conffile_t *cfile, const char *fmt, ... )
>+{
>+      va_list va;
>+
>+      fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
>+      va_start( va, fmt );
>+      vsys_error( fmt, va );
>
that call is theoretically unsafe; see the implementation.

Sorry, my lack of experience with C is showing here. I looked at
vsys_error, but are not sure what you mean. Is it the `abort`? Do I fix
it with inlining `vsys_error` and a dynamically allocated buffer?

no, i mean the saving of errno - pedantically, you need to wrap the first fprintf(). otoh, if the first print fails, the second one will most likely fail as well, and therefore it doesn't really matter if errno gets corrupted or not. but as i pushed e295f483 after much deliberation, i guess we should keep up the good practice.

Do you think that the `pclose` branch `ret < 0` error shouldn't be a
`conf_sys_error` but the normal one (`sys_error`)?

yeah, probably. and definitely it should match the popen one.

Subject: [PATCH 1/2] Refactor printing configuration errors

--- a/src/config.c
+++ b/src/config.c
@@ -61,6 +61,31 @@ expand_strdup( const char *s, const conffile_t *cfile )

+void
+conf_error( conffile_t *cfile, const char *fmt, ... )
+{
+       va_list va;
+
+       flushn();

bzzt

Subject: [PATCH 2/2] Add IncludeCmd directive to config parser

--- a/src/config.c
+++ b/src/config.c
void
conf_error( conffile_t *cfile, const char *fmt, ... )
{
        va_list va;

-       flushn();

ah, you committed it in the wrong patch. ^^

+static void
+include_cmd_pclose( conffile_t *cfile )
+{
...
+       free( cfile->include_command );

should null that pointer for safety/pedantry as well.

+static int
+read_cline( conffile_t *cfile )
+{
+       if (cfile->include_fp) {
+               cfile->include_line++;
+               if ((cfile->rest = fgets( cfile->buf, cfile->bufl, 
cfile->include_fp )))

assigning cfile->rest right at the start would be less obscure. the pointer value is already known anyway.

+                       return 1;
+               include_cmd_pclose( cfile );
+       }
+       cfile->line++;
+       return (cfile->rest = fgets( cfile->buf, cfile->bufl, cfile->fp )) != 
NULL;

...

@@ -325,9 +377,13 @@ getcline( conffile_t *cfile )

+       if (cfile->cmd && !strcasecmp( cfile->cmd, "IncludeCmd" )) {
+               if (cfile->include_fp)
+                       conf_error( cfile, "nested IncludeCmd\n" );
+               else
+                       include_cmd_popen( cfile, cfile->val );
+       }

wouldn't just moving the body into the conditional below work?
of course that would do the excess-argument check only after doing the popen, but that's consistent with how it works for other options. otoh, the side effects of instantly executing a potentially malformed command may be somewhat more grave than when processing other options. but handlign this properly would actually require a separate check anyway, as then we need to actually skip the command.

--- a/src/config.h
+++ b/src/config.h
@@ -12,10 +12,13 @@

typedef struct {
        const char *file;
+       char *include_command;

damn, i was clearly not clear enough. :}
what i meant to say was that the "frontend" naming should be changed, while leaving the internal naming (of variables and functions) at "eval", because it's shorter (and arguably clearer without the wider context of the options in the other file).

regards
#! /bin/bash
# XXX this does not handle c++ namespacing.
# XXX this becomes *incredibly* slow for some files -
# have to replace the regexp voodoo with a proper parser, i guess.
# ki18n( "str" ) => ki18n( "str") ?!

me=$(me)
typefile=typedefs.out

it=
ot=
while [ $# -gt 0 ]; do
  case $1 in
  -it) it=$2; shift 2;;
  -ot) ot=$2; shift 2;;
  -h) echo "Usage -it <m> -ot <n> file..."; exit 0;;
  -*) echo "Unrecognized option $1"; exit 1;;
  *) break;;
  esac
done
if [ -z "$it" -o -z "$ot" ]; then
  echo "Must specify both -it and -ot."
  exit 1
fi

if ! test -f $typefile; then
  echo "Collecting types ..."
  find . \( -name '*.c' -o  -name '*.cpp' -o -name '*.h' \) -exec cpp 
-DHAVE_CONFIG_H -I. -I.. -I../.. {} \; 2>/dev/null | \
    perl ${me}_collect.pl | sort -u | tr '\n' '|' | sed 's,|$,,' > $typefile
fi

maybe_expand() {
  if [ $it -gt 0 ]; then
    expand -i -t $it | "$@"
  else
    "$@"
  fi
}

maybe_unexpand() {
  if [ $ot -gt 0 ]; then
    unexpand -t $ot --first-only | "$@"
  else
    "$@"
  fi
}

pty=$(<$typefile)
for i in "$@"; do
  echo "Indenting $i ..."
  maybe_expand maybe_unexpand perl ${me}_spacing.pl "$pty" < $i | perl 
${me}_lineup.pl > $i.tmp && mv $i.tmp $i
done

exit 0
#! /usr/bin/perl

use strict;
use warnings;
use re "eval";

my ($btypes, $bt, $ctypes, $np, $wrd);
$bt = $btypes = "void
(?:(?:un)?signed\\s+)?(?:short\\s+|long\\s+(?:long\\s+)?\\s+)?int
(?:(?:un)?signed\\s+)?(?:short|long(?:\\s+long)?)
(?:un)?signed
(?:(?:un)?signed\\s+)?char
float
(?:long\\s+(?:long\\s+)?)?double";
$ctypes = "(?:struct|union)\\s+\\w+";
$bt =~ s/\n/|/g;
$np = qr{\{(?:(?>[^{}]+)|(??{$np}))*\}}x;
$wrd = qr{(?>\s*)(?>\**)(?>\s*)((?>\b\w+\b))(?{print $^N."\n"})(?>\s*)}x;
read STDIN, $_, 1e9;
s/^\s*typedef\s+(?:$bt\s|(?:struct|union)(?>\s+)(?>\b\w+\b\s+)?(??{$np})|$ctypes\s)$wrd(?:,$wrd)*;//xmg;
s/^\s*class\s+(\w+)(?{print $^N."\n"})//xmg;
print $btypes."\n".$ctypes."\n";
#! /usr/bin/perl

use strict;
use warnings;

my $il = "";
my $cmt = 0;
my @pcs = ();
my $cpp = 0;
my @ifst = ();
my $els = 0;
my @elst = ();

while (<>) {
  if ($cpp) {
    $cpp = /\\$/;
  } elsif (/^#/) {
    if (/^#\ *if/) {
      push @ifst, ($il, @pcs);
      push @elst, $els;
      $els = 0;
    } elsif (/^# *elif/) {
      ($il, @pcs) = $ifst[$#ifst];
    } elsif (/^# *else/) {
      ($il, @pcs) = pop @ifst;
      $els = 1;
    } elsif (/^# *endif/) {
      if (!$els) {
        ($il, @pcs) = pop @ifst;
      }
      $els = pop @elst;
    }
    $cpp = /\\$/;
  } else {
    $cmt = 1 if /^([ \t]*)\/\*/;
    if (!$cmt) {
      if (@pcs) {
        s/^[ \t]*/$il.(" " x ($pcs[$#pcs] - length($il)))/e;
      } else {
        /^([ \t]*)/;
        $il = $1;
      }
      /^(?>[ \t]*)(?:\((?>\ *)(?{push @pcs, pos;})|\)(?{pop @pcs;})|\/\*.*?\*\/|'(?:\\.|[^'])*'|"(?:\\.|[^"])*"|.)*$/x;
    }
    $cmt = 0 if /\*\/$/;
  }
  print $_;
}
#! /usr/bin/perl

use strict;
use warnings;
use re "eval";

my $pty = $ARGV[0];
my $skw = "if|switch|for|while|return";
my $nskw = "sizeof|offsetof|defined|as|ki18n|i18n|i18nc|i18np|i18npc|I18N_NOOP|I18N_NOOP2|QString::fromLatin1|QLatin1String|SIGNAL|SLOT";

# remove trailing whitespace, append exactly one space after semicolon
sub stws($)
{
  local $_ = shift;
  s/ *$//;
  s/(?<!;);$/; /;
  return $_;
}

my ($np, $fp, $pc);
# complex expression: n times (word or basic expression or parenthesized complex expression)
$pc = qr{
    (?:
        (?>[^()]+)
      |
        (??{$fp})
      |
        (??{$np})
    )*
}xs;
# parenthesized complex expression
$np = qr{\($pc\)}s;
# basic expression: function( n times complex expression )
$fp = qr{
    (
        \b(?:
            new(?>\s+)
          |
            (?!$pty|$skw|$nskw|else)
        )
        (?:\w|\.|::)+
      |
        \(
        \h*\*\h*
        (?:
            (?>[^()]*)
          |
            (??{$np})
        )*
        \)
    )
    (?>\s*)
    \(
    (?>\h*)
    ($pc)
    \)
}xs;

sub ffn($)
{
  local $_ = shift;
  # fix whitespace around function calls
  # XXX maybe correct parens in $1, too?
  s/$fp/my ($f, $p) = ($1, $2); $f."( ".stws(&ffn($p))." )"/eg;
  # fix whitespace around function-like (pseudo-)operators
  s/\b($nskw)\s*\(\s*(?!{~)($pc)\)/my ($o, $p) = ($1, $2); $o."(".stws($p).")"/eg;
  # fix whitespace around c-style type casts
  s/(?<!sizeof)\(($pty)\ *(\**)\ *\)\ *(?!{~)/"(".$1.($2?(" ".$2):"").")"/eg;
  return $_;
}

read STDIN, $_, 1e6;
my @subs = ();
my $csub = 0;
# replace strings and comments with {~###~} tokens
s/(\/\*.*?\*\/|\/\/.*?$|"(?:\\.|[^"])*"|^#(?:\\.|[^\n])+(?:\\\n(?:\\.|[^\n])*)*(?=\n))/push(@subs, $1), "{~".($csub++)."~}"/egsm;
# majority of the conversion
$_ = ffn($_);
# fix whitespace around control statements
s/\b($skw)\s*\(\s*(?!{~)($pc)\)/$1." (".stws($2).")"/eg;
# remove spaces between otherwise empty paren pairs
s/\( +\)/()/g;
# convert non-leading tabs to spaces
s/(?<!^)(?<!\t)\t+/ /mgx;
# replace the {~###~} tokens back with strings
for (my $i = 0; $i < @subs; $i++) {
  s/{~$i~}/$subs[$i]/e;
}
s/[ \t]+$//mg;
print $_;
#! /bin/bash
type -p `tr '\0' '\n' < /proc/$PPID/cmdline | sed '1d;/^-/d;q'`
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to