diff --git a/src/run-tests.pl b/src/run-tests.pl index 10bca9c..76b7317 100755 --- a/src/run-tests.pl +++ b/src/run-tests.pl @@ -364,16 +364,24 @@ sub showstate($) } chomp(my @ls = ); close FILE; - $_ = shift(@ls); - if (!defined $_) { - print STDERR " Missing sync state header.\n"; + my %hdr; + OUTER: while (1) { + while (@ls) { + $_ = shift(@ls); + last OUTER if (!length($_)); + if (!/^([^ ]+) (\d+)$/) { + print STDERR "Malformed sync state header entry: $_\n"; + close FILE; + return; + } + $hdr{$1} = $2; + } + print STDERR "Unterminated sync state header.\n"; + close FILE; return; } - if (!/^1:(\d+) 1:(\d+):(\d+)$/) { - print STDERR " Malformed sync state header '$_'.\n"; - return; - } - print " [ $1, $2, $3,\n "; + print " [ ".($hdr{'MaxPulledUid'} // "missing").", ". + ($hdr{'MaxExpiredSlaveUid'} // "0").", ".($hdr{'MaxPushedUid'} // "missing").",\n "; my $frst = 1; for (@ls) { if ($frst) { @@ -456,7 +464,8 @@ sub mkchan($$@) &mkbox("slave", @{ $s }); open(FILE, ">", "slave/.mbsyncstate") or die "Cannot create sync state.\n"; - print FILE "1:".shift(@t)." 1:".shift(@t).":".shift(@t)."\n"; + print FILE "MasterUidValidity 1\nMaxPulledUid ".shift(@t)."\n". + "SlaveUidValidity 1\nMaxExpiredSlaveUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n"; while (@t) { print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n"; } @@ -499,36 +508,56 @@ sub ckbox($$$@) # $filename, @syncstate sub ckstate($@) { - my ($fn, @T) = @_; + my ($fn, $mmaxuid, $smaxxuid, $smaxuid, @T) = @_; + my %hdr; + $hdr{'MasterUidValidity'} = "1"; + $hdr{'SlaveUidValidity'} = "1"; + $hdr{'MaxPulledUid'} = $mmaxuid; + $hdr{'MaxPushedUid'} = $smaxuid; + $hdr{'MaxExpiredSlaveUid'} = $smaxxuid if ($smaxxuid ne 0); open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n"; - my $l = ; chomp(my @ls = ); close FILE; - if (!defined $l) { - print STDERR "Sync state header missing.\n"; + OUTER: while (1) { + while (@ls) { + my $l = shift(@ls); + last OUTER if (!length($l)); + if ($l !~ /^([^ ]+) (\d+)$/) { + print STDERR "Malformed sync state header entry: $l\n"; + return 1; + } + my $want = delete $hdr{$1}; + if (!defined($want)) { + print STDERR "Unexpected sync state header entry: $1\n"; + return 1; + } + if ($2 != $want) { + print STDERR "Sync state header entry $1 mismatch: got $2, wanted $want\n"; + return 1; + } + } + print STDERR "Unterminated sync state header.\n"; return 1; } - chomp($l); - my $xl = "1:".shift(@T)." 1:".shift(@T).":".shift(@T); - if ($l ne $xl) { - print STDERR "Sync state header mismatch: '$l' instead of '$xl'.\n"; + my @ky = keys %hdr; + if (@ky) { + print STDERR "Keys missing from sync state header: @ky\n"; return 1; - } else { - for $l (@ls) { - if (!@T) { - print STDERR "Excess sync state entry: '$l'.\n"; - return 1; - } - $xl = shift(@T)." ".shift(@T)." ".shift(@T); - if ($l ne $xl) { - print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n"; - return 1; - } - } - if (@T) { - print STDERR "Missing sync state entry: '".shift(@T)." ".shift(@T)." ".shift(@T)."'.\n"; + } + for my $l (@ls) { + if (!@T) { + print STDERR "Excess sync state entry: '$l'.\n"; return 1; } + my $xl = shift(@T)." ".shift(@T)." ".shift(@T); + if ($l ne $xl) { + print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n"; + return 1; + } + } + if (@T) { + print STDERR "Missing sync state entry: '".shift(@T)." ".shift(@T)." ".shift(@T)."'.\n"; + return 1; } return 0; } diff --git a/src/sync.c b/src/sync.c index 05cc4b8..c3b66d6 100644 --- a/src/sync.c +++ b/src/sync.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -723,22 +724,51 @@ box_selected( int sts, void *aux ) } if ((jfp = fopen( svars->dname, "r" ))) { debug( "reading sync state %s ...\n", svars->dname ); - if (!fgets( buf, sizeof(buf), jfp ) || !(t = strlen( buf )) || buf[t - 1] != '\n') { - error( "Error: incomplete sync state header in %s\n", svars->dname ); - jbail: - fclose( jfp ); - bail: - svars->ret = SYNC_FAIL; - sync_bail( svars ); - return; + line = 0; + while (fgets( buf, sizeof(buf), jfp )) { + line++; + if (!(t = strlen( buf )) || buf[t - 1] != '\n') { + error( "Error: incomplete sync state header entry at %s:%d\n", svars->dname, line ); + jbail: + fclose( jfp ); + bail: + svars->ret = SYNC_FAIL; + sync_bail( svars ); + return; + } + if (t == 1) + goto gothdr; + if (line == 1 && isdigit( buf[0] )) { + if (sscanf( buf, "%63s %63s", buf1, buf2 ) != 2 || + sscanf( buf1, "%d:%d", &svars->uidval[M], &svars->maxuid[M] ) < 2 || + sscanf( buf2, "%d:%d:%d", &svars->uidval[S], &svars->smaxxuid, &svars->maxuid[S] ) < 3) { + error( "Error: invalid sync state header in %s\n", svars->dname ); + goto jbail; + } + goto gothdr; + } + if (sscanf( buf, "%63s %d", buf1, &t1 ) != 2) { + error( "Error: malformed sync state header entry at %s:%d\n", svars->dname, line ); + goto jbail; + } + if (!strcmp( buf1, "MasterUidValidity" )) + svars->uidval[M] = t1; + else if (!strcmp( buf1, "SlaveUidValidity" )) + svars->uidval[S] = t1; + else if (!strcmp( buf1, "MaxPulledUid" )) + svars->maxuid[M] = t1; + else if (!strcmp( buf1, "MaxPushedUid" )) + svars->maxuid[S] = t1; + else if (!strcmp( buf1, "MaxExpiredSlaveUid" )) + svars->smaxxuid = t1; + else { + error( "Error: unrecognized sync state header entry at %s:%d\n", svars->dname, line ); + goto jbail; + } } - if (sscanf( buf, "%63s %63s", buf1, buf2 ) != 2 || - sscanf( buf1, "%d:%d", &svars->uidval[M], &svars->maxuid[M] ) < 2 || - sscanf( buf2, "%d:%d:%d", &svars->uidval[S], &svars->smaxxuid, &svars->maxuid[S] ) < 3) { - error( "Error: invalid sync state header in %s\n", svars->dname ); - goto jbail; - } - line = 1; + error( "Error: unterminated sync state header in %s\n", svars->dname ); + goto jbail; + gothdr: while (fgets( buf, sizeof(buf), jfp )) { line++; if (!(t = strlen( buf )) || buf[t - 1] != '\n') { @@ -1747,9 +1777,12 @@ box_closed_p2( sync_vars_t *svars, int t ) } } - Fprintf( svars->nfp, "%d:%d %d:%d:%d\n", - svars->uidval[M], svars->maxuid[M], - svars->uidval[S], svars->smaxxuid, svars->maxuid[S] ); + Fprintf( svars->nfp, + "MasterUidValidity %d\nSlaveUidValidity %d\nMaxPulledUid %d\nMaxPushedUid %d\n", + svars->uidval[M], svars->uidval[S], svars->maxuid[M], svars->maxuid[S] ); + if (svars->smaxxuid) + Fprintf( svars->nfp, "MaxExpiredSlaveUid %d\n", svars->smaxxuid ); + Fprintf( svars->nfp, "\n" ); for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue;